diff --git a/README.rst b/README.rst index 50e848db..dfb03abc 100644 --- a/README.rst +++ b/README.rst @@ -32,7 +32,7 @@ Features - **Fast**: Crypto parts are boosted up by TgCrypto_, a high-performance library written in pure C. - **Documented**: Pyrogram API methods, types and public interfaces are well documented. - **Type-hinted**: Exposed Pyrogram types and method parameters are all type-hinted. -- **Updated**, to the latest Telegram API version, currently Layer 91 on top of `MTProto 2.0`_. +- **Updated**, to the latest Telegram API version, currently Layer 97 on top of `MTProto 2.0`_. - **Pluggable**: The Smart Plugin system allows to write components with minimal boilerplate code. - **Comprehensive**: Execute any advanced action an official client is able to do, and even more. @@ -107,7 +107,7 @@ Copyright & License
- Schema Layer @@ -122,7 +122,7 @@ Copyright & License .. |description| replace:: **Telegram MTProto API Framework for Python** -.. |schema| image:: https://img.shields.io/badge/schema-layer%2091-eda738.svg?longCache=true&colorA=262b30 +.. |schema| image:: https://img.shields.io/badge/schema-layer%2097-eda738.svg?longCache=true&colorA=262b30 :target: compiler/api/source/main_api.tl :alt: Schema Layer diff --git a/compiler/api/compiler.py b/compiler/api/compiler.py index 9e671e80..6be16ba5 100644 --- a/compiler/api/compiler.py +++ b/compiler/api/compiler.py @@ -171,8 +171,8 @@ def start(): shutil.rmtree("{}/functions".format(DESTINATION), ignore_errors=True) with open("{}/source/auth_key.tl".format(HOME), encoding="utf-8") as auth, \ - open("{}/source/sys_msgs.tl".format(HOME), encoding="utf-8") as system, \ - open("{}/source/main_api.tl".format(HOME), encoding="utf-8") as api: + open("{}/source/sys_msgs.tl".format(HOME), encoding="utf-8") as system, \ + open("{}/source/main_api.tl".format(HOME), encoding="utf-8") as api: schema = (auth.read() + system.read() + api.read()).splitlines() with open("{}/template/mtproto.txt".format(HOME), encoding="utf-8") as f: @@ -287,9 +287,11 @@ def start(): sorted_args = sort_args(c.args) - arguments = ", " + ", ".join( - [get_argument_type(i) for i in sorted_args if i != ("flags", "#")] - ) if c.args else "" + arguments = ( + ", " + + ("*, " if c.args else "") + + (", ".join([get_argument_type(i) for i in sorted_args if i != ("flags", "#")]) if c.args else "") + ) fields = "\n ".join( ["self.{0} = {0} # {1}".format(i[0], i[1]) for i in c.args if i != ("flags", "#")] @@ -333,7 +335,7 @@ def start(): docstring_args = "Attributes:\n ID: ``{}``\n\n ".format(c.id) + docstring_args if c.section == "functions": - docstring_args += "\n\n Raises:\n :obj:`Error `" + docstring_args += "\n\n Raises:\n :obj:`RPCError `" docstring_args += "\n\n Returns:\n " + get_docstring_arg_type(c.return_type) else: references = get_references(".".join(filter(None, [c.namespace, c.name]))) @@ -456,7 +458,11 @@ def start(): fields=fields, read_types=read_types, write_types=write_types, - return_arguments=", ".join([i[0] for i in sorted_args if i != ("flags", "#")]) + return_arguments=", ".join( + ["{0}={0}".format(i[0]) for i in sorted_args if i != ("flags", "#")] + ), + slots=", ".join(['"{}"'.format(i[0]) for i in sorted_args if i != ("flags", "#")]), + qualname="{}{}".format("{}.".format(c.namespace) if c.namespace else "", c.name) ) ) diff --git a/compiler/api/source/main_api.tl b/compiler/api/source/main_api.tl index 61bd1a41..581427b5 100644 --- a/compiler/api/source/main_api.tl +++ b/compiler/api/source/main_api.tl @@ -83,7 +83,7 @@ fileLocationUnavailable#7c596b46 volume_id:long local_id:int secret:long = FileL 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; +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 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; userProfilePhotoEmpty#4f11bae1 = UserProfilePhoto; userProfilePhoto#d559d8c8 photo_id:long photo_small:FileLocation photo_big:FileLocation = UserProfilePhoto; @@ -96,12 +96,12 @@ userStatusLastWeek#7bf09fc = UserStatus; userStatusLastMonth#77ebc742 = UserStatus; chatEmpty#9ba2d800 id:int = Chat; -chat#d91cdd54 flags:# creator:flags.0?true kicked:flags.1?true left:flags.2?true admins_enabled:flags.3?true admin:flags.4?true deactivated:flags.5?true id:int title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel = 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#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; +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 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; 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#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; +chatFull#22a235da 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 = 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; @@ -163,6 +163,7 @@ photo#9c477dd8 flags:# has_stickers:flags.0?true id:long access_hash:long file_r photoSizeEmpty#e17e23c type:string = PhotoSize; photoSize#77bfb61b type:string location:FileLocation w:int h:int size:int = PhotoSize; photoCachedSize#e9a734fa type:string location:FileLocation w:int h:int bytes:bytes = PhotoSize; +photoStrippedSize#e0b0bc2e type:string bytes:bytes = PhotoSize; geoPointEmpty#1117dd5f = GeoPoint; geoPoint#296f104 long:double lat:double access_hash:long = GeoPoint; @@ -186,8 +187,7 @@ peerNotifySettings#af509d20 flags:# show_previews:flags.0?Bool silent:flags.1?Bo peerSettings#818426cd flags:# report_spam:flags.0?true = PeerSettings; -wallPaper#ccb03657 id:int title:string sizes:Vector color:int = WallPaper; -wallPaperSolid#63117f24 id:int title:string bg_color:int color:int = WallPaper; +wallPaper#a437c3ed id:long flags:# creator:flags.0?true default:flags.1?true pattern:flags.3?true dark:flags.4?true access_hash:long slug:string document:Document settings:flags.2?WallPaperSettings = WallPaper; inputReportReasonSpam#58dbcab8 = ReportReason; inputReportReasonViolence#1e22c78d = ReportReason; @@ -221,7 +221,7 @@ messages.dialogsSlice#71e094f3 count:int dialogs:Vector messages:Vector< 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.messagesSlice#a6c47aaa flags:# inexact:flags.1?true 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; @@ -281,7 +281,6 @@ updateNewChannelMessage#62ba04d9 message:Message pts:int pts_count:int = Update; updateReadChannelInbox#4214f37f channel_id:int max_id:int = Update; updateDeleteChannelMessages#c37521c9 channel_id:int messages:Vector pts:int pts_count:int = Update; updateChannelMessageViews#98a12b4b channel_id:int id:int views:int = Update; -updateChatAdmins#6e947941 chat_id:int enabled:Bool version:int = Update; updateChatParticipantAdmin#b6901959 chat_id:int user_id:int is_admin:Bool version:int = Update; updateNewStickerSet#688a30aa stickerset:messages.StickerSet = Update; updateStickerSetsOrder#bb2d201 flags:# masks:flags.0?true order:Vector = Update; @@ -316,8 +315,9 @@ 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; +updateChatPinnedMessage#e10db349 chat_id:int id:int version:int = Update; updateMessagePoll#aca1657b flags:# poll_id:long poll:flags.0?Poll results:PollResults = Update; +updateChatDefaultBannedRights#54c01850 peer:Peer default_banned_rights:ChatBannedRights version:int = Update; updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State; @@ -382,7 +382,7 @@ inputDocumentEmpty#72f0eaae = InputDocument; inputDocument#1abfb575 id:long access_hash:long file_reference:bytes = InputDocument; documentEmpty#36f8c871 id:long = 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; +document#9ba29cc1 flags:# id:long access_hash:long file_reference:bytes date:int mime_type:string size:int thumbs:flags.0?Vector dc_id:int attributes:Vector = Document; help.support#17c6b5f6 phone_number:string user:User = help.Support; @@ -411,11 +411,15 @@ inputPrivacyKeyStatusTimestamp#4f96cb18 = InputPrivacyKey; inputPrivacyKeyChatInvite#bdfb0426 = InputPrivacyKey; inputPrivacyKeyPhoneCall#fabadc5f = InputPrivacyKey; inputPrivacyKeyPhoneP2P#db9e70d2 = InputPrivacyKey; +inputPrivacyKeyForwards#a4dd4c08 = InputPrivacyKey; +inputPrivacyKeyProfilePhoto#5719bacc = InputPrivacyKey; privacyKeyStatusTimestamp#bc2eab30 = PrivacyKey; privacyKeyChatInvite#500e6dfa = PrivacyKey; privacyKeyPhoneCall#3d662b7b = PrivacyKey; privacyKeyPhoneP2P#39491cc8 = PrivacyKey; +privacyKeyForwards#69ec56a3 = PrivacyKey; +privacyKeyProfilePhoto#96151fed = PrivacyKey; inputPrivacyValueAllowContacts#d09e07b = InputPrivacyRule; inputPrivacyValueAllowAll#184b35ce = InputPrivacyRule; @@ -487,7 +491,7 @@ inputStickerSetEmpty#ffb62b95 = InputStickerSet; inputStickerSetID#9de7a269 id:long access_hash:long = InputStickerSet; inputStickerSetShortName#861cc8a0 short_name:string = InputStickerSet; -stickerSet#5585a139 flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string count:int hash:int = StickerSet; +stickerSet#6a90bcb7 flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string thumb:flags.4?PhotoSize count:int hash:int = StickerSet; messages.stickerSet#b60a24a6 set:StickerSet packs:Vector documents:Vector = messages.StickerSet; @@ -544,8 +548,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#a82fa898 flags:# can_edit:flags.0?true user_id:int inviter_id:int promoted_by:int date:int admin_rights:ChannelAdminRights = ChannelParticipant; -channelParticipantBanned#222c1886 flags:# left:flags.0?true user_id:int kicked_by:int date:int banned_rights:ChannelBannedRights = 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; +channelParticipantBanned#1c0facaf flags:# left:flags.0?true user_id:int kicked_by:int date:int banned_rights:ChatBannedRights = ChannelParticipant; channelParticipantsRecent#de3f3c79 = ChannelParticipantsFilter; channelParticipantsAdmins#b4608969 = ChannelParticipantsFilter; @@ -553,6 +557,7 @@ channelParticipantsKicked#a3b54985 q:string = ChannelParticipantsFilter; channelParticipantsBots#b0d1865b = ChannelParticipantsFilter; channelParticipantsBanned#1427a5e1 q:string = ChannelParticipantsFilter; channelParticipantsSearch#656ac4b q:string = ChannelParticipantsFilter; +channelParticipantsContacts#bb6ae88d q:string = ChannelParticipantsFilter; channels.channelParticipants#f56ee2a8 count:int participants:Vector users:Vector = channels.ChannelParticipants; channels.channelParticipantsNotModified#f0173fe9 = channels.ChannelParticipants; @@ -594,7 +599,7 @@ messages.botResults#947ca848 flags:# gallery:flags.0?true query_id:long next_off exportedMessageLink#5dab1af4 link:string html:string = ExportedMessageLink; -messageFwdHeader#559ebe6d flags:# from_id:flags.0?int date:int channel_id:flags.1?int channel_post:flags.2?int post_author:flags.3?string saved_from_peer:flags.4?Peer saved_from_msg_id:flags.4?int = MessageFwdHeader; +messageFwdHeader#ec338270 flags:# from_id:flags.0?int from_name:flags.5?string date:int channel_id:flags.1?int channel_post:flags.2?int post_author:flags.3?string saved_from_peer:flags.4?Peer saved_from_msg_id:flags.4?int = MessageFwdHeader; auth.codeTypeSms#72a3158c = auth.CodeType; auth.codeTypeCall#741cd3e3 = auth.CodeType; @@ -789,10 +794,6 @@ langPackDifference#f385c1f6 lang_code:string from_version:int version:int string 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; - -channelBannedRights#58cf4249 flags:# view_messages:flags.0?true send_messages:flags.1?true send_media:flags.2?true send_stickers:flags.3?true send_gifs:flags.4?true send_games:flags.5?true send_inline:flags.6?true embed_links:flags.7?true until_date:int = ChannelBannedRights; - channelAdminLogEventActionChangeTitle#e6dfb825 prev_value:string new_value:string = ChannelAdminLogEventAction; channelAdminLogEventActionChangeAbout#55188a2e prev_value:string new_value:string = ChannelAdminLogEventAction; channelAdminLogEventActionChangeUsername#6a4afc38 prev_value:string new_value:string = ChannelAdminLogEventAction; @@ -809,6 +810,8 @@ channelAdminLogEventActionParticipantToggleBan#e6d83d7e prev_participant:Channel channelAdminLogEventActionParticipantToggleAdmin#d5676710 prev_participant:ChannelParticipant new_participant:ChannelParticipant = ChannelAdminLogEventAction; channelAdminLogEventActionChangeStickerSet#b1c3caa7 prev_stickerset:InputStickerSet new_stickerset:InputStickerSet = ChannelAdminLogEventAction; channelAdminLogEventActionTogglePreHistoryHidden#5f5c95f1 new_value:Bool = ChannelAdminLogEventAction; +channelAdminLogEventActionDefaultBannedRights#2df5fc0a prev_banned_rights:ChatBannedRights new_banned_rights:ChatBannedRights = ChannelAdminLogEventAction; +channelAdminLogEventActionStopPoll#8f079643 message:Message = ChannelAdminLogEventAction; channelAdminLogEvent#3b5a3e40 id:long date:int user_id:int action:ChannelAdminLogEventAction = ChannelAdminLogEvent; @@ -972,6 +975,31 @@ chatOnlines#f041e250 onlines:int = ChatOnlines; statsURL#47a971e0 url:string = StatsURL; +chatAdminRights#5fb224d5 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 pin_messages:flags.7?true add_admins:flags.9?true = ChatAdminRights; + +chatBannedRights#9f120418 flags:# view_messages:flags.0?true send_messages:flags.1?true send_media:flags.2?true send_stickers:flags.3?true send_gifs:flags.4?true send_games:flags.5?true send_inline:flags.6?true embed_links:flags.7?true send_polls:flags.8?true change_info:flags.10?true invite_users:flags.15?true pin_messages:flags.17?true until_date:int = ChatBannedRights; + +inputWallPaper#e630b979 id:long access_hash:long = InputWallPaper; +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; + +wallPaperSettings#a12f40b8 flags:# blur:flags.1?true motion:flags.2?true background_color:flags.0?int intensity:flags.3?int = WallPaperSettings; + +autoDownloadSettings#d246fd47 flags:# disabled:flags.0?true video_preload_large:flags.1?true audio_preload_next:flags.2?true phonecalls_less_data:flags.3?true photo_size_max:int video_size_max:int file_size_max:int = AutoDownloadSettings; + +account.autoDownloadSettings#63cacf26 low:AutoDownloadSettings medium:AutoDownloadSettings high:AutoDownloadSettings = account.AutoDownloadSettings; + +emojiKeyword#d5b3b9f9 keyword:string emoticons:Vector = EmojiKeyword; +emojiKeywordDeleted#236df622 keyword:string emoticons:Vector = EmojiKeyword; + +emojiKeywordsDifference#5cc761bd lang_code:string from_version:int version:int keywords:Vector = EmojiKeywordsDifference; + +emojiURL#a575739d url:string = EmojiURL; + ---functions--- invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X; @@ -982,7 +1010,7 @@ invokeWithoutUpdates#bf9459b7 {X:Type} query:!X = X; invokeWithMessagesRange#365275f2 {X:Type} range:MessageRange query:!X = X; invokeWithTakeout#aca9fd2e {X:Type} takeout_id:long query:!X = X; -auth.sendCode#86aef0ec flags:# allow_flashcall:flags.0?true phone_number:string current_number:flags.0?Bool api_id:int api_hash:string = auth.SentCode; +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.signIn#bcd51581 phone_number:string phone_code_hash:string phone_code:string = auth.Authorization; auth.logOut#5717da40 = Bool; @@ -1005,7 +1033,7 @@ account.getNotifySettings#12b3ad31 peer:InputNotifyPeer = PeerNotifySettings; account.resetNotifySettings#db7e1747 = Bool; account.updateProfile#78515775 flags:# first_name:flags.0?string last_name:flags.1?string about:flags.2?string = User; account.updateStatus#6628562c offline:Bool = Bool; -account.getWallPapers#c04cfac2 = Vector; +account.getWallPapers#aabb1763 hash:int = account.WallPapers; account.reportPeer#ae189d5f peer:InputPeer reason:ReportReason = Bool; account.checkUsername#2714d86c username:string = Bool; account.updateUsername#3e0bdd7c username:string = User; @@ -1014,7 +1042,7 @@ account.setPrivacy#c9f81ce8 key:InputPrivacyKey rules:Vector = account.deleteAccount#418d4e0b reason:string = Bool; account.getAccountTTL#8fc711d = AccountDaysTTL; account.setAccountTTL#2442485e ttl:AccountDaysTTL = Bool; -account.sendChangePhoneCode#8e57deb flags:# allow_flashcall:flags.0?true phone_number:string current_number:flags.0?Bool = auth.SentCode; +account.sendChangePhoneCode#82574ae5 phone_number:string settings:CodeSettings = auth.SentCode; account.changePhone#70c32edb phone_number:string phone_code_hash:string phone_code:string = User; account.updateDeviceLocked#38df3532 period:int = Bool; account.getAuthorizations#e320c158 = account.Authorizations; @@ -1022,7 +1050,7 @@ account.resetAuthorization#df77f3bc hash:long = Bool; account.getPassword#548a30f5 = account.Password; 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.sendConfirmPhoneCode#1b3faa88 hash:string settings:CodeSettings = auth.SentCode; account.confirmPhone#5f2178c3 phone_code_hash:string phone_code:string = Bool; account.getTmpPassword#449e0b51 password:InputCheckPasswordSRP period:int = account.TmpPassword; account.getWebAuthorizations#182e6d6f = account.WebAuthorizations; @@ -1034,7 +1062,7 @@ account.saveSecureValue#899fe31d value:InputSecureValue secure_secret_id:long = account.deleteSecureValue#b880bc4b types:Vector = Bool; account.getAuthorizationForm#b86ba8e1 bot_id:int scope:string public_key:string = account.AuthorizationForm; account.acceptAuthorization#e7027c94 bot_id:int scope:string public_key:string value_hashes:Vector credentials:SecureCredentialsEncrypted = Bool; -account.sendVerifyPhoneCode#823380b4 flags:# allow_flashcall:flags.0?true phone_number:string current_number:flags.0?Bool = auth.SentCode; +account.sendVerifyPhoneCode#a5a356f9 phone_number:string settings:CodeSettings = auth.SentCode; account.verifyPhone#4dd3a7f6 phone_number:string phone_code_hash:string phone_code:string = Bool; account.sendVerifyEmailCode#7011509f email:string = account.SentEmailCode; account.verifyEmail#ecba39db email:string code:string = Bool; @@ -1046,6 +1074,13 @@ 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; +account.getWallPaper#fc8ddbea wallpaper:InputWallPaper = WallPaper; +account.uploadWallPaper#dd853661 file:InputFile mime_type:string settings:WallPaperSettings = WallPaper; +account.saveWallPaper#6c5a5b37 wallpaper:InputWallPaper unsave:Bool settings:WallPaperSettings = Bool; +account.installWallPaper#feed5769 wallpaper:InputWallPaper settings:WallPaperSettings = Bool; +account.resetWallPapers#bb3b9804 = Bool; +account.getAutoDownloadSettings#56da0b3f = account.AutoDownloadSettings; +account.saveAutoDownloadSettings#76f36233 flags:# low:flags.0?true high:flags.1?true settings:AutoDownloadSettings = Bool; users.getUsers#d91a548 id:Vector = Vector; users.getFullUser#ca30a5b1 id:InputUser = UserFull; @@ -1074,7 +1109,7 @@ messages.getDialogs#b098aee6 flags:# exclude_pinned:flags.0?true offset_date:int messages.getHistory#dcbb8260 peer:InputPeer offset_id:int offset_date:int add_offset:int limit:int max_id:int min_id:int hash:int = messages.Messages; messages.search#8614ef68 flags:# peer:InputPeer q:string from_id:flags.0?InputUser filter:MessagesFilter min_date:int max_date:int offset_id:int add_offset:int limit:int max_id:int min_id:int hash:int = messages.Messages; messages.readHistory#e306d3a peer:InputPeer max_id:int = messages.AffectedMessages; -messages.deleteHistory#1c015b09 flags:# just_clear:flags.0?true peer:InputPeer max_id:int = messages.AffectedHistory; +messages.deleteHistory#1c015b09 flags:# just_clear:flags.0?true revoke:flags.1?true peer:InputPeer max_id:int = messages.AffectedHistory; 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; @@ -1107,7 +1142,7 @@ messages.readMessageContents#36a73f77 id:Vector = messages.AffectedMessages messages.getStickers#43d4f2c emoticon:string hash:int = messages.Stickers; messages.getAllStickers#1c9618b1 hash:int = messages.AllStickers; messages.getWebPagePreview#8b68b0cc flags:# message:string entities:flags.3?Vector = MessageMedia; -messages.exportChatInvite#7d885289 chat_id:int = ExportedChatInvite; +messages.exportChatInvite#df7534c peer:InputPeer = ExportedChatInvite; messages.checkChatInvite#3eadb1bb hash:string = ChatInvite; messages.importChatInvite#6c50051c hash:string = Updates; messages.getStickerSet#2619a90e stickerset:InputStickerSet = messages.StickerSet; @@ -1115,7 +1150,6 @@ messages.installStickerSet#c78fe460 stickerset:InputStickerSet archived:Bool = m messages.uninstallStickerSet#f96e55de stickerset:InputStickerSet = Bool; messages.startBot#e6df7378 bot:InputUser peer:InputPeer random_id:long start_param:string = Updates; messages.getMessagesViews#c4c8a55d peer:InputPeer id:Vector increment:Bool = Vector; -messages.toggleChatAdmins#ec8bd9e1 chat_id:int enabled:Bool = Updates; messages.editChatAdmin#a9e69f2e chat_id:int user_id:InputUser is_admin:Bool = Bool; messages.migrateChat#15a3b8e3 chat_id:int = Updates; messages.searchGlobal#9e3cacb0 q:string offset_date:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages; @@ -1173,7 +1207,12 @@ messages.updatePinnedMessage#d2aaf7ec flags:# silent:flags.0?true peer:InputPeer 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; +messages.getStatsURL#812c2ae6 flags:# dark:flags.0?true peer:InputPeer params:string = StatsURL; +messages.editChatAbout#def60797 peer:InputPeer about:string = Bool; +messages.editChatDefaultBannedRights#a5866b41 peer:InputPeer banned_rights:ChatBannedRights = Updates; +messages.getEmojiKeywords#35a0e062 lang_code:string = EmojiKeywordsDifference; +messages.getEmojiKeywordsDifference#1508b6af lang_code:string from_version:int = EmojiKeywordsDifference; +messages.getEmojiURL#d5b10c26 lang_code:string = EmojiURL; updates.getState#edd4882a = updates.State; updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference; @@ -1223,8 +1262,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#f4893d7f flags:# broadcast:flags.0?true megagroup:flags.1?true title:string about:string = Updates; -channels.editAbout#13e27f1e channel:InputChannel about:string = Bool; -channels.editAdmin#20b88214 channel:InputChannel user_id:InputUser admin_rights:ChannelAdminRights = Updates; +channels.editAdmin#70f893ba channel:InputChannel user_id:InputUser admin_rights:ChatAdminRights = 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; @@ -1232,13 +1270,11 @@ channels.updateUsername#3514b3de channel:InputChannel username:string = Bool; channels.joinChannel#24b524c5 channel:InputChannel = Updates; channels.leaveChannel#f836aa95 channel:InputChannel = Updates; channels.inviteToChannel#199f3a6c channel:InputChannel users:Vector = Updates; -channels.exportInvite#c7560885 channel:InputChannel = ExportedChatInvite; 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.getAdminedPublicChannels#8d8d82d7 = messages.Chats; -channels.editBanned#bfd915cd channel:InputChannel user_id:InputUser banned_rights:ChannelBannedRights = Updates; +channels.editBanned#72796912 channel:InputChannel user_id:InputUser banned_rights:ChatBannedRights = 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; channels.setStickers#ea8ca4f9 channel:InputChannel stickerset:InputStickerSet = Bool; channels.readMessageContents#eab5dc38 channel:InputChannel id:Vector = Bool; @@ -1267,13 +1303,13 @@ phone.acceptCall#3bd2b4a0 peer:InputPhoneCall g_b:bytes protocol:PhoneCallProtoc phone.confirmCall#2efe1722 peer:InputPhoneCall g_a:bytes key_fingerprint:long protocol:PhoneCallProtocol = phone.PhoneCall; phone.receivedCall#17d54f61 peer:InputPhoneCall = Bool; phone.discardCall#78d413a6 peer:InputPhoneCall duration:int reason:PhoneCallDiscardReason connection_id:long = Updates; -phone.setCallRating#1c536a34 peer:InputPhoneCall rating:int comment:string = Updates; +phone.setCallRating#59ead627 flags:# user_initiative:flags.0?true peer:InputPhoneCall rating:int comment:string = Updates; phone.saveCallDebug#277add7e peer:InputPhoneCall debug:DataJSON = Bool; 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.getDifference#cd984aa5 lang_pack:string 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 91 +// LAYER 97 diff --git a/compiler/api/template/mtproto.txt b/compiler/api/template/mtproto.txt index 9a65b52d..c63525d6 100644 --- a/compiler/api/template/mtproto.txt +++ b/compiler/api/template/mtproto.txt @@ -9,7 +9,10 @@ class {class_name}(Object): """{docstring_args} """ + __slots__ = [{slots}] + ID = {object_id} + QUALNAME = "{qualname}" def __init__(self{arguments}): {fields} diff --git a/compiler/error/compiler.py b/compiler/error/compiler.py index b86222e7..996c4981 100644 --- a/compiler/error/compiler.py +++ b/compiler/error/compiler.py @@ -22,7 +22,7 @@ import re import shutil HOME = "compiler/error" -DEST = "pyrogram/api/errors/exceptions" +DEST = "pyrogram/errors/exceptions" NOTICE_PATH = "NOTICE" @@ -73,7 +73,7 @@ def start(): f_init.write("from .{}_{} import *\n".format(name.lower(), code)) with open("{}/source/{}".format(HOME, i), encoding="utf-8") as f_csv, \ - open("{}/{}_{}.py".format(DEST, name.lower(), code), "w", encoding="utf-8") as f_class: + open("{}/{}_{}.py".format(DEST, name.lower(), code), "w", encoding="utf-8") as f_class: reader = csv.reader(f_csv, delimiter="\t") super_class = caml(name) @@ -134,7 +134,7 @@ def start(): if "__main__" == __name__: HOME = "." - DEST = "../../pyrogram/api/errors/exceptions" + DEST = "../../pyrogram/errors/exceptions" NOTICE_PATH = "../../NOTICE" start() diff --git a/compiler/error/source/400_BAD_REQUEST.tsv b/compiler/error/source/400_BAD_REQUEST.tsv index b7a629be..8325040d 100644 --- a/compiler/error/source/400_BAD_REQUEST.tsv +++ b/compiler/error/source/400_BAD_REQUEST.tsv @@ -88,5 +88,12 @@ MEDIA_INVALID The media is invalid BOT_SCORE_NOT_MODIFIED The bot score was not modified USER_BOT_REQUIRED The method can be used by bots only IMAGE_PROCESS_FAILED The server failed to process your image +USERNAME_NOT_MODIFIED The username was not modified CALL_ALREADY_ACCEPTED The call is already accepted CALL_ALREADY_DECLINED The call is already declined +PHOTO_EXT_INVALID The photo extension is invalid +EXTERNAL_URL_INVALID The external media URL is invalid +CHAT_NOT_MODIFIED The chat settings were not modified +RESULTS_TOO_MUCH The result contains too many items +RESULT_ID_DUPLICATE The result contains items with duplicated identifiers +ACCESS_TOKEN_INVALID The bot access token is invalid \ No newline at end of file diff --git a/compiler/error/template/class.txt b/compiler/error/template/class.txt index e520d80c..718472b1 100644 --- a/compiler/error/template/class.txt +++ b/compiler/error/template/class.txt @@ -1,12 +1,12 @@ {notice} -from ..error import Error +from ..rpc_error import RPCError -class {super_class}(Error): +class {super_class}(RPCError): {docstring} CODE = {code} - """``int``: Error Code""" + """``int``: RPC Error Code""" NAME = __doc__ diff --git a/compiler/error/template/sub_class.txt b/compiler/error/template/sub_class.txt index e13e4cf2..c8551838 100644 --- a/compiler/error/template/sub_class.txt +++ b/compiler/error/template/sub_class.txt @@ -1,7 +1,7 @@ class {sub_class}({super_class}): {docstring} ID = {id} - """``str``: Error ID""" + """``str``: RPC Error ID""" MESSAGE = __doc__ diff --git a/docs/source/conf.py b/docs/source/conf.py index cecf047f..8acfde42 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -25,6 +25,10 @@ sys.path.insert(0, os.path.abspath('../..')) # Import after sys.path.insert() to avoid issues from pyrogram import __version__ +from pygments.styles.friendly import FriendlyStyle + +FriendlyStyle.background_color = "#f3f2f1" + # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. @@ -60,7 +64,7 @@ master_doc = 'index' # General information about the project. project = 'Pyrogram' -copyright = '2017-2018, Dan Tès' +copyright = '2017-2019, Dan Tès' author = 'Dan Tès' # The version info for the project you're documenting, acts as replacement for @@ -85,7 +89,7 @@ language = None exclude_patterns = [] # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'tango' +pygments_style = 'friendly' # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False diff --git a/docs/source/errors/BadRequest.rst b/docs/source/errors/BadRequest.rst index c51a7d54..2d56434c 100644 --- a/docs/source/errors/BadRequest.rst +++ b/docs/source/errors/BadRequest.rst @@ -1,8 +1,7 @@ 400 - Bad Request ================= -.. module:: pyrogram.api.errors.BadRequest +.. module:: pyrogram.errors.BadRequest -.. automodule:: pyrogram.api.errors.exceptions.bad_request_400 +.. automodule:: pyrogram.errors.exceptions.bad_request_400 :members: - :show-inheritance: diff --git a/docs/source/errors/Flood.rst b/docs/source/errors/Flood.rst index 72f819ea..55098cbb 100644 --- a/docs/source/errors/Flood.rst +++ b/docs/source/errors/Flood.rst @@ -1,8 +1,7 @@ 420 - Flood =========== -.. module:: pyrogram.api.errors.Flood +.. module:: pyrogram.errors.Flood -.. automodule:: pyrogram.api.errors.exceptions.flood_420 +.. automodule:: pyrogram.errors.exceptions.flood_420 :members: - :show-inheritance: diff --git a/docs/source/errors/Forbidden.rst b/docs/source/errors/Forbidden.rst index aaaceaff..cd794979 100644 --- a/docs/source/errors/Forbidden.rst +++ b/docs/source/errors/Forbidden.rst @@ -1,8 +1,7 @@ 403 - Forbidden =============== -.. module:: pyrogram.api.errors.Forbidden +.. module:: pyrogram.errors.Forbidden -.. automodule:: pyrogram.api.errors.exceptions.forbidden_403 +.. automodule:: pyrogram.errors.exceptions.forbidden_403 :members: - :show-inheritance: diff --git a/docs/source/errors/InternalServerError.rst b/docs/source/errors/InternalServerError.rst index 5e506fc9..7f78d519 100644 --- a/docs/source/errors/InternalServerError.rst +++ b/docs/source/errors/InternalServerError.rst @@ -1,8 +1,7 @@ 500 - Internal Server Error =========================== -.. module:: pyrogram.api.errors.InternalServerError +.. module:: pyrogram.errors.InternalServerError -.. automodule:: pyrogram.api.errors.exceptions.internal_server_error_500 +.. automodule:: pyrogram.errors.exceptions.internal_server_error_500 :members: - :show-inheritance: diff --git a/docs/source/errors/NotAcceptable.rst b/docs/source/errors/NotAcceptable.rst index e9301396..5a8365fc 100644 --- a/docs/source/errors/NotAcceptable.rst +++ b/docs/source/errors/NotAcceptable.rst @@ -1,8 +1,7 @@ 406 - Not Acceptable ==================== -.. module:: pyrogram.api.errors.NotAcceptable +.. module:: pyrogram.errors.NotAcceptable -.. automodule:: pyrogram.api.errors.exceptions.not_acceptable_406 +.. automodule:: pyrogram.errors.exceptions.not_acceptable_406 :members: - :show-inheritance: diff --git a/docs/source/errors/SeeOther.rst b/docs/source/errors/SeeOther.rst index a916e779..f90902d0 100644 --- a/docs/source/errors/SeeOther.rst +++ b/docs/source/errors/SeeOther.rst @@ -1,8 +1,7 @@ 303 - See Other =============== -.. module:: pyrogram.api.errors.SeeOther +.. module:: pyrogram.errors.SeeOther -.. automodule:: pyrogram.api.errors.exceptions.see_other_303 +.. automodule:: pyrogram.errors.exceptions.see_other_303 :members: - :show-inheritance: diff --git a/docs/source/errors/Unauthorized.rst b/docs/source/errors/Unauthorized.rst index 6de3ff67..d47ed3fb 100644 --- a/docs/source/errors/Unauthorized.rst +++ b/docs/source/errors/Unauthorized.rst @@ -1,8 +1,7 @@ 401 - Unauthorized ================== -.. module:: pyrogram.api.errors.Unauthorized +.. module:: pyrogram.errors.Unauthorized -.. automodule:: pyrogram.api.errors.exceptions.unauthorized_401 +.. automodule:: pyrogram.errors.exceptions.unauthorized_401 :members: - :show-inheritance: diff --git a/docs/source/errors/UnknownError.rst b/docs/source/errors/UnknownError.rst index 767f19c3..21495957 100644 --- a/docs/source/errors/UnknownError.rst +++ b/docs/source/errors/UnknownError.rst @@ -1,8 +1,7 @@ 520 - Unknown Error =================== -.. module:: pyrogram.api.errors.UnknownError +.. module:: pyrogram.errors.UnknownError -.. autoexception:: pyrogram.api.errors.error.UnknownError +.. autoexception:: pyrogram.errors.rpc_error.UnknownError :members: - :show-inheritance: diff --git a/docs/source/index.rst b/docs/source/index.rst index 4d913d23..bd62547e 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -26,7 +26,7 @@ Welcome to Pyrogram
- Schema Layer @@ -67,7 +67,7 @@ Features - **Fast**: Crypto parts are boosted up by TgCrypto_, a high-performance library written in pure C. - **Documented**: Pyrogram API methods, types and public interfaces are well documented. - **Type-hinted**: Exposed Pyrogram types and method parameters are all type-hinted. -- **Updated**, to the latest Telegram API version, currently Layer 91 on top of `MTProto 2.0`_. +- **Updated**, to the latest Telegram API version, currently Layer 97 on top of `MTProto 2.0`_. - **Pluggable**: The Smart Plugin system allows to write components with minimal boilerplate code. - **Comprehensive**: Execute any advanced action an official client is able to do, and even more. @@ -115,7 +115,7 @@ To get started, press the Next button. functions/index types/index -.. _`Telegram`: https://telegram.org/ -.. _TgCrypto: https://docs.pyrogram.ml/resources/TgCrypto/ +.. _`Telegram`: https://telegram.org +.. _TgCrypto: https://docs.pyrogram.ml/resources/TgCrypto .. _`MTProto API`: https://core.telegram.org/api#telegram-api -.. _`MTProto 2.0`: https://core.telegram.org/mtproto \ No newline at end of file +.. _`MTProto 2.0`: https://core.telegram.org/mtproto diff --git a/docs/source/pyrogram/Client.rst b/docs/source/pyrogram/Client.rst index 548f1c5b..dcf8bb79 100644 --- a/docs/source/pyrogram/Client.rst +++ b/docs/source/pyrogram/Client.rst @@ -31,6 +31,7 @@ Decorators on_message on_callback_query + on_inline_query on_deleted_messages on_user_status on_disconnect @@ -56,6 +57,7 @@ Messages send_location send_venue send_contact + send_cached_media send_chat_action edit_message_text edit_message_caption @@ -67,6 +69,7 @@ Messages iter_history send_poll vote_poll + close_poll retract_vote download_media @@ -97,6 +100,8 @@ Chats iter_chat_members get_dialogs iter_dialogs + restrict_chat + update_chat_username Users ----- @@ -109,6 +114,7 @@ Users get_user_profile_photos set_user_profile_photo delete_user_profile_photos + update_username Contacts -------- @@ -139,10 +145,12 @@ Bots get_inline_bot_results send_inline_bot_result answer_callback_query + answer_inline_query request_callback_answer send_game set_game_score get_game_high_scores + answer_inline_query .. autoclass:: pyrogram.Client diff --git a/docs/source/pyrogram/Handlers.rst b/docs/source/pyrogram/Handlers.rst index 3b748e9d..1bb16ece 100644 --- a/docs/source/pyrogram/Handlers.rst +++ b/docs/source/pyrogram/Handlers.rst @@ -9,6 +9,7 @@ Handlers MessageHandler DeletedMessagesHandler CallbackQueryHandler + InlineQueryHandler UserStatusHandler DisconnectHandler RawUpdateHandler @@ -22,6 +23,9 @@ Handlers .. autoclass:: CallbackQueryHandler :members: +.. autoclass:: InlineQueryHandler + :members: + .. autoclass:: UserStatusHandler :members: diff --git a/docs/source/pyrogram/Error.rst b/docs/source/pyrogram/RPCError.rst similarity index 77% rename from docs/source/pyrogram/Error.rst rename to docs/source/pyrogram/RPCError.rst index 2ec1159d..a47c9b9c 100644 --- a/docs/source/pyrogram/Error.rst +++ b/docs/source/pyrogram/RPCError.rst @@ -1,9 +1,8 @@ -Error -===== +RPCError +======== -.. autoexception:: pyrogram.Error +.. autoexception:: pyrogram.RPCError :members: - :show-inheritance: .. toctree:: ../errors/SeeOther diff --git a/docs/source/pyrogram/Types.rst b/docs/source/pyrogram/Types.rst index 6e0a14db..5deb58b2 100644 --- a/docs/source/pyrogram/Types.rst +++ b/docs/source/pyrogram/Types.rst @@ -16,6 +16,7 @@ Users & Chats ChatPhoto ChatMember ChatMembers + ChatPermissions Dialog Dialogs @@ -65,6 +66,7 @@ Input Media .. autosummary:: :nosignatures: + InputMedia InputMediaPhoto InputMediaVideo InputMediaAudio @@ -72,6 +74,25 @@ Input Media InputMediaDocument InputPhoneContact +Inline Mode +------------ + +.. autosummary:: + :nosignatures: + + InlineQuery + InlineQueryResult + InlineQueryResultArticle + +InputMessageContent +------------------- + +.. autosummary:: + :nosignatures: + + InputMessageContent + InputTextMessageContent + .. User & Chats ------------ @@ -96,6 +117,9 @@ Input Media .. autoclass:: ChatMembers :members: +.. autoclass:: ChatPermissions + :members: + .. autoclass:: Dialog :members: @@ -195,6 +219,9 @@ Input Media .. Input Media ----------- +.. autoclass:: InputMedia + :members: + .. autoclass:: InputMediaPhoto :members: @@ -212,3 +239,25 @@ Input Media .. autoclass:: InputPhoneContact :members: + + +.. Inline Mode + ----------- + +.. autoclass:: InlineQuery + :members: + +.. autoclass:: InlineQueryResult + :members: + +.. autoclass:: InlineQueryResultArticle + :members: + +.. InputMessageContent + ------------------- + +.. autoclass:: InputMessageContent + :members: + +.. autoclass:: InputTextMessageContent + :members: diff --git a/docs/source/pyrogram/index.rst b/docs/source/pyrogram/index.rst index 20e7c918..286b5db1 100644 --- a/docs/source/pyrogram/index.rst +++ b/docs/source/pyrogram/index.rst @@ -15,6 +15,6 @@ after the well established `Telegram Bot API`_ methods, thus offering a familiar Filters ChatAction ParseMode - Error + RPCError .. _Telegram Bot API: https://core.telegram.org/bots/api#available-methods diff --git a/docs/source/resources/AdvancedUsage.rst b/docs/source/resources/AdvancedUsage.rst index 02395f26..8b722b2a 100644 --- a/docs/source/resources/AdvancedUsage.rst +++ b/docs/source/resources/AdvancedUsage.rst @@ -1,40 +1,102 @@ Advanced Usage ============== -In this section, you'll be shown the alternative way of communicating with Telegram using Pyrogram: the main Telegram -API with its raw functions and types. +Pyrogram's API, which consists of well documented convenience methods_ and facade types_, exists to provide a much +easier interface to the undocumented and often confusing Telegram API. + +In this section, you'll be shown the alternative way of communicating with Telegram using Pyrogram: the main "raw" +Telegram API with its functions and types. Telegram Raw API ---------------- If you can't find a high-level method for your needs or if you want complete, low-level access to the whole -Telegram API, you have to use the raw :mod:`functions ` and :mod:`types ` -exposed by the ``pyrogram.api`` package and call any Telegram API method you wish using the -:meth:`send() ` method provided by the Client class. +Telegram API, you have to use the raw :mod:`functions ` and :mod:`types `. + +As already hinted, raw functions and types can be really confusing, mainly because people don't realize soon enough they +accept *only* the right types and that all required parameters must be filled in. This section will therefore explain +some pitfalls to take into consideration when working with the raw API. .. hint:: - Every available high-level method mentioned in the previous page is built on top of these raw functions. + Every available high-level methods in Pyrogram is built on top of these raw functions. Nothing stops you from using the raw functions only, but they are rather complex and `plenty of them`_ are already - re-implemented by providing a much simpler and cleaner interface which is very similar to the Bot API. + re-implemented by providing a much simpler and cleaner interface which is very similar to the Bot API (yet much more + powerful). If you think a raw function should be wrapped and added as a high-level method, feel free to ask in our Community_! -Caveats -------- +Invoking Functions +^^^^^^^^^^^^^^^^^^ -As hinted before, raw functions and types can be confusing, mainly because people don't realize they must accept -*exactly* the right values, but also because most of them don't have enough Python experience to fully grasp how things -work. +Unlike the methods_ found in Pyrogram's API, which can be called in the usual simple way, functions to be invoked from +the raw Telegram API have a different way of usage and are more complex. -This section will therefore explain some pitfalls to take into consideration when working with the raw API. +First of all, both `raw functions`_ and `raw types`_ live in their respective packages (and sub-packages): +``pyrogram.api.functions``, ``pyrogram.api.types``. They all exist as Python classes, meaning you need to create an +instance of each every time you need them and fill them in with the correct values using named arguments. + +Next, to actually invoke the raw function you have to use the :meth:`send() ` method provided by +the Client class and pass the function object you created. + +Here's some examples: + +- Update first name, last name and bio: + + .. code-block:: python + + from pyrogram import Client + from pyrogram.api import functions + + with Client("my_account") as app: + app.send( + functions.account.UpdateProfile( + first_name="Dan", last_name="Tès", + about="Bio written from Pyrogram" + ) + ) + +- Disable links to your account when someone forwards your messages: + + .. code-block:: python + + from pyrogram import Client + from pyrogram.api import functions, types + + with Client("my_account") as app: + app.send( + functions.account.SetPrivacy( + key=types.PrivacyKeyForwards(), + rules=[types.InputPrivacyValueDisallowAll()] + ) + ) + +- Invite users to your channel/supergroup: + + .. code-block:: python + + from pyrogram import Client + from pyrogram.api import functions, types + + with Client("my_account") as app: + app.send( + functions.channels.InviteToChannel( + channel=app.resolve_peer(123456789), # ID or Username + users=[ # The users you want to invite + app.resolve_peer(23456789), # By ID + app.resolve_peer("username"), # By username + app.resolve_peer("+393281234567"), # By phone number + ] + ) + ) Chat IDs ^^^^^^^^ The way Telegram works makes it impossible to directly send a message to a user or a chat by using their IDs only. -Instead, a pair of ``id`` and ``access_hash`` wrapped in a so called ``InputPeer`` is always needed. +Instead, a pair of ``id`` and ``access_hash`` wrapped in a so called ``InputPeer`` is always needed. Pyrogram allows +sending messages with IDs only thanks to cached access hashes. There are three different InputPeer types, one for each kind of Telegram entity. Whenever an InputPeer is needed you must pass one of these: @@ -56,66 +118,19 @@ kind of ID. For example, given the ID *123456789*, here's how Pyrogram can tell entities apart: - - ``+ID`` - User: *123456789* - - ``-ID`` - Chat: *-123456789* - - ``-100ID`` - Channel (and Supergroup): *-100123456789* + - ``+ID`` User: *123456789* + - ``-ID`` Chat: *-123456789* + - ``-100ID`` Channel (and Supergroup): *-100123456789* So, every time you take a raw ID, make sure to translate it into the correct ID when you want to use it with an high-level method. -Examples --------- - -- Update first name, last name and bio: - - .. code-block:: python - - from pyrogram import Client - from pyrogram.api import functions - - with Client("my_account") as app: - app.send( - functions.account.UpdateProfile( - first_name="Dan", last_name="Tès", - about="Bio written from Pyrogram" - ) - ) - -- Share your Last Seen time only with your contacts: - - .. code-block:: python - - from pyrogram import Client - from pyrogram.api import functions, types - - with Client("my_account") as app: - app.send( - functions.account.SetPrivacy( - key=types.InputPrivacyKeyStatusTimestamp(), - rules=[types.InputPrivacyValueAllowContacts()] - ) - ) - -- Invite users to your channel/supergroup: - - .. code-block:: python - - from pyrogram import Client - from pyrogram.api import functions, types - - with Client("my_account") as app: - app.send( - functions.channels.InviteToChannel( - channel=app.resolve_peer(123456789), # ID or Username - users=[ # The users you want to invite - app.resolve_peer(23456789), # By ID - app.resolve_peer("username"), # By username - app.resolve_peer("393281234567"), # By phone number - ] - ) - ) + +.. _methods: ../pyrogram/Client.html#messages +.. _types: ../pyrogram/Types.html .. _plenty of them: ../pyrogram/Client.html#messages -.. _Raw Functions: Usage.html#using-raw-functions +.. _raw functions: ../pyrogram/functions +.. _raw types: ../pyrogram/types .. _Community: https://t.me/PyrogramChat \ No newline at end of file diff --git a/docs/source/resources/ConfigurationFile.rst b/docs/source/resources/ConfigurationFile.rst index 759bfd9f..2a50277f 100644 --- a/docs/source/resources/ConfigurationFile.rst +++ b/docs/source/resources/ConfigurationFile.rst @@ -1,13 +1,13 @@ Configuration File ================== -As already mentioned in previous sections, Pyrogram can also be configured by the use of an INI file. +As already mentioned in previous sections, Pyrogram can be configured by the use of an INI file. This page explains how this file is structured in Pyrogram, how to use it and why. Introduction ------------ -The idea behind using a configuration file is to help keeping your code free of settings (private) information such as +The idea behind using a configuration file is to help keeping your code free of private settings information such as the API Key and Proxy without having you to even deal with how to load such settings. The configuration file, usually referred as ``config.ini`` file, is automatically loaded from the root of your working directory; all you need to do is fill in the necessary parts. @@ -46,7 +46,7 @@ These are all the sections Pyrogram uses in its configuration file: Pyrogram ^^^^^^^^ -The ``[pyrogram]`` section contains your Telegram API credentials *api_id* and *api_hash*. +The ``[pyrogram]`` section contains your Telegram API credentials: *api_id* and *api_hash*. .. code-block:: ini diff --git a/docs/source/resources/CustomizeSessions.rst b/docs/source/resources/CustomizeSessions.rst index e98792b7..77765287 100644 --- a/docs/source/resources/CustomizeSessions.rst +++ b/docs/source/resources/CustomizeSessions.rst @@ -1,19 +1,19 @@ Customize Sessions ================== -As you may probably know, Telegram allows Users (and Bots) having more than one session (authorizations) registered +As you may probably know, Telegram allows users (and bots) having more than one session (authorizations) registered in the system at the same time. Briefly explaining, sessions are simply new logins in your account. They can be reviewed in the settings of an official -app (or by invoking `GetAuthorizations <../functions/account/GetAuthorizations.html>`_ with Pyrogram) and store some useful -information about the client who generated them. +app (or by invoking `GetAuthorizations <../functions/account/GetAuthorizations.html>`_ with Pyrogram). They store some +useful information such as the client who's using them and from which country and IP address. .. figure:: https://i.imgur.com/lzGPCdZ.png :width: 70% :align: center - A Pyrogram session running on Linux, Python 3.6. + **A Pyrogram session running on Linux, Python 3.6.** That's how a session looks like on the Android app, showing the three main pieces of information. diff --git a/docs/source/resources/ErrorHandling.rst b/docs/source/resources/ErrorHandling.rst index 1f08c165..7e87b94a 100644 --- a/docs/source/resources/ErrorHandling.rst +++ b/docs/source/resources/ErrorHandling.rst @@ -4,18 +4,18 @@ Error Handling Errors are inevitable when working with the API, and they must be correctly handled with ``try..except`` blocks. There are many errors that Telegram could return, but they all fall in one of these categories -(which are in turn children of the :obj:`pyrogram.Error` superclass) +(which are in turn children of the :obj:`RPCError ` superclass): -- :obj:`303 - See Other ` -- :obj:`400 - Bad Request ` -- :obj:`401 - Unauthorized ` -- :obj:`403 - Forbidden ` -- :obj:`406 - Not Acceptable ` -- :obj:`420 - Flood ` -- :obj:`500 - Internal Server Error ` +- :obj:`303 - See Other ` +- :obj:`400 - Bad Request ` +- :obj:`401 - Unauthorized ` +- :obj:`403 - Forbidden ` +- :obj:`406 - Not Acceptable ` +- :obj:`420 - Flood ` +- :obj:`500 - Internal Server Error ` As stated above, there are really many (too many) errors, and in case Pyrogram does not know anything yet about a -specific one, it raises a special :obj:`520 Unknown Error ` exception and logs it +specific one, it raises a special :obj:`520 Unknown Error ` exception and logs it in the ``unknown_errors.txt`` file. Users are invited to report these unknown errors; in later versions of Pyrogram some kind of automatic error reporting module might be implemented. @@ -24,7 +24,7 @@ Examples .. code-block:: python - from pyrogram.api.errors import ( + from pyrogram.errors import ( BadRequest, Flood, InternalServerError, SeeOther, Unauthorized, UnknownError ) @@ -45,13 +45,13 @@ Examples pass Exception objects may also contain some informative values. -E.g.: :obj:`FloodWait ` holds the amount of seconds you have to wait +E.g.: :obj:`FloodWait ` holds the amount of seconds you have to wait before you can try again. The value is always stored in the ``x`` field of the returned exception object: .. code-block:: python import time - from pyrogram.api.errors import FloodWait + from pyrogram.errors import FloodWait try: ... diff --git a/docs/source/resources/MoreOnUpdates.rst b/docs/source/resources/MoreOnUpdates.rst index 9712a5d2..f3658c6e 100644 --- a/docs/source/resources/MoreOnUpdates.rst +++ b/docs/source/resources/MoreOnUpdates.rst @@ -1,24 +1,22 @@ More on Updates =============== -Here we'll show some advanced usages when working with updates. - -.. note:: - This page makes use of Handlers and Filters to show you how to handle updates. - Learn more at `Update Handling `_ and `Using Filters `_. +Here we'll show some advanced usages when working with `update handlers`_ and `filters`_. Handler Groups -------------- -If you register handlers with overlapping filters, only the first one is executed and any other handler will be ignored. +If you register handlers with overlapping (conflicting) filters, only the first one is executed and any other handler +will be ignored. This is intended by design. -In order to process the same update more than once, you can register your handler in a different group. -Groups are identified by a number (number 0 being the default) and are sorted, that is, a lower group number has a -higher priority. +In order to handle the very same update more than once, you have to register your handler in a different dispatching +group. Dispatching groups hold one or more handlers and are processed sequentially, they are identified by a number +(number 0 being the default) and sorted, that is, a lower group number has a higher priority: -For example, in: +For example, take these two handlers: .. code-block:: python + :emphasize-lines: 1, 6 @app.on_message(Filters.text | Filters.sticker) def text_or_sticker(client, message): @@ -29,8 +27,8 @@ For example, in: def just_text(client, message): print("Just Text") -``just_text`` is never executed because ``text_or_sticker`` already handles texts. To enable it, simply register the -function using a different group: +Here, ``just_text`` is never executed because ``text_or_sticker``, which has been registered first, already handles +texts (``Filters.text`` is shared and conflicting). To enable it, register the function using a different group: .. code-block:: python @@ -69,7 +67,7 @@ continue to propagate the same update to the next groups until all the handlers @app.on_message(Filters.private, group=1) def _(client, message): - print(1 / 0) # Unhandled exception: ZeroDivisionError + raise Exception("Unhandled exception!") # Simulate an unhandled exception @app.on_message(Filters.private, group=2) @@ -82,7 +80,7 @@ The output for each incoming update will therefore be: .. code-block:: text 0 - ZeroDivisionError: division by zero + Exception: Unhandled exception! 2 Stop Propagation @@ -153,9 +151,9 @@ Continue Propagation As opposed to `stopping the update propagation <#stop-propagation>`_ and also as an alternative to the `handler groups <#handler-groups>`_, you can signal the internal dispatcher to continue the update propagation within -the group regardless of the next handler's filters. This allows you to register multiple handlers with overlapping -filters in the same group; to let the dispatcher process the next handler you can do *one* of the following in each -handler you want to grant permission to continue: +**the same group** regardless of the next handler's filters. This allows you to register multiple handlers with +overlapping filters in the same group; to let the dispatcher process the next handler you can do *one* of the following +in each handler you want to grant permission to continue: - Call the update's bound-method ``.continue_propagation()`` (preferred way). - Manually ``raise ContinuePropagation`` exception (more suitable for raw updates only). @@ -218,4 +216,7 @@ The output of both (equivalent) examples will be: 0 1 - 2 \ No newline at end of file + 2 + +.. _`update handlers`: UpdateHandling.html +.. _`filters`: UsingFilters.html \ No newline at end of file diff --git a/docs/source/resources/SmartPlugins.rst b/docs/source/resources/SmartPlugins.rst index 972efdd8..6f266590 100644 --- a/docs/source/resources/SmartPlugins.rst +++ b/docs/source/resources/SmartPlugins.rst @@ -13,8 +13,8 @@ Introduction ------------ Prior to the Smart Plugin system, pluggable handlers were already possible. For example, if you wanted to modularize -your applications, you had to put your function definitions in separate files and register them inside your main script, -like this: +your applications, you had to put your function definitions in separate files and register them inside your main script +after importing your modules, like this: .. note:: @@ -72,7 +72,7 @@ functions. So, what if you could? Smart Plugins solve this issue by taking care Using Smart Plugins ------------------- -Setting up your Pyrogram project to accommodate Smart Plugins is straightforward: +Setting up your Pyrogram project to accommodate Smart Plugins is pretty straightforward: #. Create a new folder to store all the plugins (e.g.: "plugins", "handlers", ...). #. Put your python files full of plugins inside. Organize them as you wish. @@ -129,18 +129,17 @@ Setting up your Pyrogram project to accommodate Smart Plugins is straightforward from pyrogram import Client - plugins = dict( - root="plugins" - ) + plugins = dict(root="plugins") Client("my_account", plugins=plugins).run() + The first important thing to note is the new ``plugins`` folder. You can put *any python file* in *any subfolder* and each file can contain *any decorated function* (handlers) with one limitation: within a single module (file) you must use different names for each decorated function. The second thing is telling Pyrogram where to look for your plugins: you can either use the *config.ini* file or -the Client parameter "plugins"; the *root* value must match the name of your plugins folder. Your Pyrogram Client +the Client parameter "plugins"; the *root* value must match the name of your plugins root folder. Your Pyrogram Client instance will **automatically** scan the folder upon starting to search for valid handlers and register them for you. Then you'll notice you can now use decorators. That's right, you can apply the usual decorators to your callback @@ -161,7 +160,7 @@ found inside each module will be, instead, loaded in the order they are defined, This default loading behaviour is usually enough, but sometimes you want to have more control on what to include (or exclude) and in which exact order to load plugins. The way to do this is to make use of ``include`` and ``exclude`` -keys, either in the *config.ini* file or in the dictionary passed as Client argument. Here's how they work: +directives, either in the *config.ini* file or in the dictionary passed as Client argument. Here's how they work: - If both ``include`` and ``exclude`` are omitted, all plugins are loaded as described above. - If ``include`` is given, only the specified plugins will be loaded, in the order they are passed. @@ -214,9 +213,7 @@ also organized in subfolders: .. code-block:: python - plugins = dict( - root="plugins" - ) + plugins = dict(root="plugins") Client("my_account", plugins=plugins).run() @@ -293,7 +290,7 @@ Load/Unload Plugins at Runtime In the `previous section <#specifying-the-plugins-to-include>`_ we've explained how to specify which plugins to load and which to ignore before your Client starts. Here we'll show, instead, how to unload and load again a previously -registered plugins at runtime. +registered plugin at runtime. Each function decorated with the usual ``on_message`` decorator (or any other decorator that deals with Telegram updates ) will be modified in such a way that, when you reference them later on, they will be actually pointing to a tuple of @@ -314,14 +311,14 @@ attribute. Here's an example: - Printing ``echo`` will show something like ``(, 0)``. -- Printing ``echo[0].callback``, that is, the *callback* attribute of the first eleent of the tuple, which is an +- Printing ``echo[0].callback``, that is, the *callback* attribute of the first element of the tuple, which is an Handler, will reveal the actual callback ````. Unloading ^^^^^^^^^ -In order to unload a plugin, or any other handler, all you need to do is obtain a reference to it (by importing the -relevant module) and call :meth:`remove_handler ` Client's method with your function +In order to unload a plugin, or any other handler, all you need to do is obtain a reference to it by importing the +relevant module and call :meth:`remove_handler() ` Client's method with your function name preceded by the star ``*`` operator as argument. Example: - ``main.py`` @@ -346,7 +343,7 @@ Loading ^^^^^^^ Similarly to the unloading process, in order to load again a previously unloaded plugin you do the same, but this time -using :meth:`add_handler ` instead. Example: +using :meth:`add_handler() ` instead. Example: - ``main.py`` diff --git a/docs/source/resources/UpdateHandling.rst b/docs/source/resources/UpdateHandling.rst index 12afe324..ed0ad909 100644 --- a/docs/source/resources/UpdateHandling.rst +++ b/docs/source/resources/UpdateHandling.rst @@ -1,11 +1,13 @@ Update Handling =============== -Updates are events that happen in your Telegram account (incoming messages, new channel posts, new members join, ...) -and can be handled by registering one or more callback functions in your app by using `Handlers <../pyrogram/Handlers.html>`_. +Let's now dive right into the core of the framework. -To put it simply, whenever an update is received from Telegram it will be dispatched and your previously defined callback -function(s) matching it will be called back with the update itself as argument. +Updates are events that happen in your Telegram account (incoming messages, new channel posts, new members join, ...) +and are handled by registering one or more callback functions in your app using `Handlers <../pyrogram/Handlers.html>`_. + +Each handler deals with a specific event and once a matching update arrives from Telegram, your registered callback +function will be called. Registering an Handler ---------------------- @@ -15,13 +17,34 @@ To explain how handlers work let's have a look at the most used one, the updates coming from all around your chats. Every other handler shares the same setup logic; you should not have troubles settings them up once you learn from this section. +Using add_handler() +------------------- + +The :meth:`add_handler() ` method takes any handler instance that wraps around your defined +callback function and registers it in your Client. Here's a full example that prints out the content of a message as +soon as it arrives: + +.. code-block:: python + + from pyrogram import Client, MessageHandler + + + def my_function(client, message): + print(message) + + + app = Client("my_account") + + my_handler = MessageHandler(my_function) + app.add_handler(my_handler) + + app.run() Using Decorators ---------------- -The easiest and nicest way to register a MessageHandler is by decorating your function with the -:meth:`on_message() ` decorator. Here's a full example that prints out the content -of a message as soon as it arrives. +A much nicer way to register a MessageHandler is by decorating your callback function with the +:meth:`on_message() ` decorator, which will still make use of add_handler() under the hood. .. code-block:: python @@ -37,23 +60,13 @@ of a message as soon as it arrives. app.run() -Using add_handler() -------------------- -If you prefer not to use decorators for any reason, there is an alternative way for registering Handlers. -This is useful, for example, when you want to keep your callback functions in separate files. +.. note:: -.. code-block:: python + Due to how these decorators work in Pyrogram, they will wrap your defined callback function in a tuple consisting of + ``(handler, group)``; this will be the value held by your function identifier (e.g.: *my_function* from the example + above). - from pyrogram import Client, MessageHandler - - - def my_handler(client, message): - print(message) - - - app = Client("my_account") - - app.add_handler(MessageHandler(my_handler)) - - app.run() + In case, for some reason, you want to get your own function back after it has been decorated, you need to access + ``my_function[0].callback``, that is, the *callback* field of the *handler* object which is the first element in the + tuple. \ No newline at end of file diff --git a/docs/source/resources/UsingFilters.rst b/docs/source/resources/UsingFilters.rst index 3fe87b8a..ec3e2e10 100644 --- a/docs/source/resources/UsingFilters.rst +++ b/docs/source/resources/UsingFilters.rst @@ -1,17 +1,19 @@ Using Filters ============= -For a finer grained control over what kind of messages will be allowed or not in your callback functions, you can use -:class:`Filters `. +So far we've seen how to register a callback function that executes every time a specific update comes from the server, +but there's much more than that to come. -.. note:: - This page makes use of Handlers to show you how to handle updates. - Learn more at `Update Handling `_. +Here we'll discuss about :class:`Filters `. Filters enable a fine-grain control over what kind of +updates are allowed or not to be passed in your callback functions, based on their inner details. + +Let's start right away with a simple example: - This example will show you how to **only** handle messages containing an :obj:`Audio ` object and - ignore any other message: + ignore any other message. Filters are passed as the first argument of the decorator: .. code-block:: python + :emphasize-lines: 4 from pyrogram import Filters @@ -20,9 +22,10 @@ For a finer grained control over what kind of messages will be allowed or not in def my_handler(client, message): print(message) -- or, without decorators: +- or, without decorators. Here filters are passed as the second argument of the handler constructor: .. code-block:: python + :emphasize-lines: 8 from pyrogram import Filters, MessageHandler @@ -37,7 +40,7 @@ Combining Filters ----------------- Filters can also be used in a more advanced way by inverting and combining more filters together using bitwise -operators: +operators ``~``, ``&`` and ``|``: - Use ``~`` to invert a filter (behaves like the ``not`` operator). - Use ``&`` and ``|`` to merge two filters (behave like ``and``, ``or`` operators respectively). @@ -74,7 +77,7 @@ can also accept arguments: def my_handler(client, message): print(message) -- Message is a **text** message matching the given **regex** pattern. +- Message is a **text** message or a media **caption** matching the given **regex** pattern. .. code-block:: python @@ -104,17 +107,17 @@ Custom Filters -------------- Pyrogram already provides lots of built-in :class:`Filters ` to work with, but in case you can't find -a specific one for your needs or want to build a custom filter by yourself (to be used in a different handler, for -example) you can use :meth:`Filters.create() `. +a specific one for your needs or want to build a custom filter by yourself (to be used in a different kind of handler, +for example) you can use :meth:`Filters.create() `. .. note:: At the moment, the built-in filters are intended to be used with the :obj:`MessageHandler ` only. An example to demonstrate how custom filters work is to show how to create and use one for the -:obj:`CallbackQueryHandler `. Note that callback queries updates are only received by Bots; -create and `authorize your bot <../start/Setup.html#bot-authorization>`_, then send a message with an inline keyboard to -yourself. This allows you to test your filter by pressing the inline button: +:obj:`CallbackQueryHandler `. Note that callback queries updates are only received by +bots; create and `authorize your bot <../start/Setup.html#bot-authorization>`_, then send a message with an inline +keyboard to yourself. This allows you to test your filter by pressing the inline button: .. code-block:: python @@ -133,26 +136,27 @@ Basic Filters For this basic filter we will be using only the first two parameters of :meth:`Filters.create() `. -The code below creates a simple filter for hardcoded callback data. This filter will only allow callback queries -containing "pyrogram" as data: +The code below creates a simple filter for hardcoded, static callback data. This filter will only allow callback queries +containing "Pyrogram" as data, that is, the function *func* you pass returns True in case the callback query data +equals to ``b"Pyrogram"``. .. code-block:: python - hardcoded_data = Filters.create( - name="HardcodedData", - func=lambda filter, callback_query: callback_query.data == b"pyrogram" + static_data = Filters.create( + name="StaticdData", + func=lambda flt, callback_query: callback_query.data == b"Pyrogram" ) The ``lambda`` operator in python is used to create small anonymous functions and is perfect for this example, the same -could be achieved with a normal function, but we don't really need it as it makes sense only inside the filter itself: +could be achieved with a normal function, but we don't really need it as it makes sense only inside the filter's scope: .. code-block:: python - def func(filter, callback_query): - return callback_query.data == b"pyrogram" + def func(flt, callback_query): + return callback_query.data == b"Pyrogram" - hardcoded_data = Filters.create( - name="HardcodedData", + static_data = Filters.create( + name="StaticData", func=func ) @@ -160,14 +164,14 @@ The filter usage remains the same: .. code-block:: python - @app.on_callback_query(hardcoded_data) + @app.on_callback_query(static_data) def pyrogram_data(client, callback_query): client.answer_callback_query(callback_query.id, "it works!") Filters with Arguments ^^^^^^^^^^^^^^^^^^^^^^ -A much cooler filter would be one that accepts "pyrogram" or any other data as argument at usage time. +A much cooler filter would be one that accepts "Pyrogram" or any other data as argument at usage time. A dynamic filter like this will make use of the third parameter of :meth:`Filters.create() `. This is how a dynamic custom filter looks like: @@ -177,7 +181,7 @@ This is how a dynamic custom filter looks like: def dynamic_data(data): return Filters.create( name="DynamicData", - func=lambda filter, callback_query: filter.data == callback_query.data, + func=lambda flt, callback_query: flt.data == callback_query.data, data=data # "data" kwarg is accessed with "filter.data" ) @@ -185,6 +189,6 @@ And its usage: .. code-block:: python - @app.on_callback_query(dynamic_data(b"pyrogram")) + @app.on_callback_query(dynamic_data(b"Pyrogram")) def pyrogram_data(client, callback_query): client.answer_callback_query(callback_query.id, "it works!") \ No newline at end of file diff --git a/docs/source/start/Installation.rst b/docs/source/start/Installation.rst index c5c6cb31..079e1b1f 100644 --- a/docs/source/start/Installation.rst +++ b/docs/source/start/Installation.rst @@ -1,11 +1,11 @@ Installation ============ -Being a Python library, Pyrogram requires Python to be installed in your system. +Being a Python library, **Pyrogram** requires Python to be installed in your system. We recommend using the latest version of Python 3 and pip. -Get Python 3 from https://www.python.org/downloads/ (or with your package manager) and pip -by following the instructions at https://pip.pypa.io/en/latest/installing/. +- Get **Python 3** from https://www.python.org/downloads/ (or with your package manager) +- Get **pip** by following the instructions at https://pip.pypa.io/en/latest/installing/. .. important:: @@ -29,8 +29,12 @@ Install Pyrogram Bleeding Edge ------------- -If you want the latest development version of Pyrogram, you can install it straight from the develop_ -branch using this command (note "develop.zip" in the link): +Things are constantly evolving in Pyrogram, although new releases are published only when enough changes are added, +but this doesn't mean you can't try new features right now! + +In case you would like to try out the latest Pyrogram features and additions, the `GitHub repo`_ is always kept updated +with new changes; you can install the development version straight from the ``develop`` branch using this command +(note "develop.zip" in the link): .. code-block:: text @@ -39,10 +43,10 @@ branch using this command (note "develop.zip" in the link): Asynchronous ------------ -Pyrogram heavily depends on IO-bound network code (it's a cloud-based messaging client library after all), and here's +Pyrogram heavily depends on IO-bound network code (it's a cloud-based messaging framework after all), and here's where asyncio shines the most by providing extra performance while running on a single OS-level thread only. -**A fully asynchronous variant of Pyrogram is therefore available** (Python 3.5+ required). +**A fully asynchronous variant of Pyrogram is therefore available** (Python 3.5.3+ required). Use this command to install (note "asyncio.zip" in the link): .. code-block:: text @@ -82,7 +86,7 @@ If no error shows up you are good to go. >>> import pyrogram >>> pyrogram.__version__ - '0.11.0' + '0.12.0' .. _TgCrypto: https://docs.pyrogram.ml/resources/TgCrypto -.. _develop: http://github.com/pyrogram/pyrogram +.. _`Github repo`: http://github.com/pyrogram/pyrogram diff --git a/docs/source/start/Setup.rst b/docs/source/start/Setup.rst index 24caa1f4..45a40d16 100644 --- a/docs/source/start/Setup.rst +++ b/docs/source/start/Setup.rst @@ -15,23 +15,22 @@ If you already have one you can skip this step, otherwise: #. Fill out the form to register a new Telegram application. #. Done. The API key consists of two parts: **App api_id** and **App api_hash**. - .. important:: - This API key is personal and should be kept secret. + This API key is personal and must be kept secret. Configuration ------------- The API key obtained in the `previous step <#api-keys>`_ defines a token for your application allowing you to access -the Telegram database using the MTProto API — **it is therefore required for all authorizations of both Users and Bots**. +the Telegram database using the MTProto API — **it is therefore required for all authorizations of both users and bots**. Having it handy, it's time to configure your Pyrogram project. There are two ways to do so, and you can choose what fits better for you: - Create a new ``config.ini`` file at the root of your working directory, copy-paste the following and replace the - **api_id** and **api_hash** values with your own. This is the preferred method because allows you - to keep your credentials out of your code without having to deal with how to load them: + **api_id** and **api_hash** values with your own. This is the preferred method because allows you to keep your + credentials out of your code without having to deal with how to load them: .. code-block:: ini @@ -39,8 +38,8 @@ fits better for you: api_id = 12345 api_hash = 0123456789abcdef0123456789abcdef -- Alternatively, you can pass your API key to Pyrogram by simply using the *api_id* and *api_hash* - parameters of the Client class. This way you can have full control on how to store and load your credentials: +- Alternatively, you can pass your API key to Pyrogram by simply using the *api_id* and *api_hash* parameters of the + Client class. This way you can have full control on how to store and load your credentials: .. code-block:: python @@ -54,16 +53,16 @@ fits better for you: .. note:: - The examples below assume you have created a ``config.ini`` file, thus they won't show the *api_id* - and *api_hash* parameters usage. + From now on, the code snippets assume you are using the ``config.ini`` file, thus they won't show the *api_id* and + *api_hash* parameters usage to keep them as clean as possible. User Authorization ------------------ -In order to use the API, Telegram requires that Users be authorized via their phone numbers. -Pyrogram automatically manages this access, all you need to do is create an instance of -the :class:`Client ` class by passing to it a ``session_name`` of your choice -(e.g.: "my_account") and call the :meth:`run() ` method: +In order to use the API, Telegram requires that users be authorized via their phone numbers. +Pyrogram automatically manages this access, all you need to do is create an instance of the +:class:`Client ` class by passing to it a ``session_name`` of your choice (e.g.: "my_account") and call +the :meth:`run() ` method: .. code-block:: python @@ -80,30 +79,40 @@ and the **phone code** you will receive: Enter phone number: +39********** Is "+39**********" correct? (y/n): y Enter phone code: 32768 + Logged in successfully as Dan -After successfully authorizing yourself, a new file called ``my_account.session`` will be created allowing -Pyrogram executing API calls with your identity. This file will be loaded again when you restart your app, -and as long as you keep the session alive, Pyrogram won't ask you again to enter your phone number. +After successfully authorizing yourself, a new file called ``my_account.session`` will be created allowing Pyrogram +executing API calls with your identity. This file will be loaded again when you restart your app, and as long as you +keep the session alive, Pyrogram won't ask you again to enter your phone number. .. important:: Your ``*.session`` files are personal and must be kept secret. +.. note:: + + The code above does nothing except asking for credentials and keeping the client online, hit ``CTRL+C`` now to stop + your application and keep reading. + Bot Authorization ----------------- -Bots are a special kind of users and are authorized via their tokens (instead of phone numbers), which are created by -BotFather_. Bot tokens replace the Users' phone numbers only — you still need to -`configure a Telegram API key <#configuration>`_ with Pyrogram, even when using Bots. +Bots are a special kind of users that are authorized via their tokens (instead of phone numbers), which are created by +BotFather_. Bot tokens replace the users' phone numbers only — you still need to +`configure a Telegram API key <#configuration>`_ with Pyrogram, even when using bots. -The authorization process is automatically managed. All you need to do is pass the bot token as ``session_name``. -The session file will be named after the Bot user_id, which is ``123456.session`` for the example below. +The authorization process is automatically managed. All you need to do is choose a ``session_name`` (can be anything, +usually your bot username) and pass your bot token using the ``bot_token`` parameter. The session file will be named +after the session name, which will be ``pyrogrambot.session`` for the example below. .. code-block:: python from pyrogram import Client - app = Client("123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11") + app = Client( + "pyrogrambot", + bot_token="123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11" + ) app.run() .. _installed Pyrogram: Installation.html diff --git a/docs/source/start/Usage.rst b/docs/source/start/Usage.rst index 8bb197ab..35ae79a0 100644 --- a/docs/source/start/Usage.rst +++ b/docs/source/start/Usage.rst @@ -1,7 +1,7 @@ Usage ===== -Having your `project set up`_ and your account authorized_, it's time to play with the API. Let's start! +Having your `project set up`_ and your account authorized_, it's time to start playing with the API. Let's start! High-level API -------------- @@ -11,34 +11,36 @@ named after the `Telegram Bot API`_. Here's a simple example: - .. code-block:: python +.. code-block:: python - from pyrogram import Client + from pyrogram import Client - app = Client("my_account") + app = Client("my_account") - app.start() + app.start() - print(app.get_me()) - app.send_message("me", "Hi there! I'm using **Pyrogram**") - app.send_location("me", 51.500729, -0.124583) + print(app.get_me()) + app.send_message("me", "Hi there! I'm using **Pyrogram**") + app.send_location("me", 51.500729, -0.124583) + app.send_sticker("me", "CAADBAADyg4AAvLQYAEYD4F7vcZ43AI") - app.stop() + app.stop() You can also use Pyrogram in a context manager with the ``with`` statement. The Client will automatically :meth:`start ` and :meth:`stop ` gracefully, even in case of unhandled exceptions in your code: - .. code-block:: python +.. code-block:: python - from pyrogram import Client + from pyrogram import Client - app = Client("my_account") + app = Client("my_account") - with app: - print(app.get_me()) - app.send_message("me", "Hi there! I'm using **Pyrogram**") - app.send_location("me", 51.500729, -0.124583) + with app: + print(app.get_me()) + app.send_message("me", "Hi there! I'm using **Pyrogram**") + app.send_location("me", 51.500729, -0.124583) + app.send_sticker("me", "CAADBAADyg4AAvLQYAEYD4F7vcZ43AI") More examples on `GitHub `_. @@ -46,4 +48,4 @@ More examples on `GitHub str: - return dumps(self, cls=Encoder, indent=4) - - def __bool__(self) -> bool: - return True - - def __eq__(self, other) -> bool: - return self.__dict__ == other.__dict__ + return dumps(self, indent=4, default=default, ensure_ascii=False) def __len__(self) -> int: return len(self.write()) - def __call__(self): - pass - def __getitem__(self, item): return getattr(self, item) @@ -62,29 +55,18 @@ def remove_none(obj): return obj -class Encoder(JSONEncoder): - def default(self, o: Object): - try: - content = o.__dict__ - except AttributeError: - if isinstance(o, datetime): - return o.strftime("%d-%b-%Y %H:%M:%S") - else: - return repr(o) +def default(o: "Object"): + try: + content = {i: getattr(o, i) for i in o.__slots__} - name = o.__class__.__name__ - o = objects.get(getattr(o, "ID", None), None) - - if o is not None: - if o.startswith("pyrogram.client"): - r = remove_none(OrderedDict([("_", "pyrogram:" + name)] + [i for i in content.items()])) - r.pop("_client", None) - - return r - else: - return OrderedDict( - [("_", o.replace("pyrogram.api.types.", "telegram:"))] - + [i for i in content.items()] - ) + return remove_none( + OrderedDict( + [("_", o.QUALNAME)] + + [i for i in content.items()] + ) + ) + except AttributeError: + if isinstance(o, datetime): + return o.strftime("%d-%b-%Y %H:%M:%S") else: - return None + return repr(o) diff --git a/pyrogram/client/__init__.py b/pyrogram/client/__init__.py index 2719d82e..d43511d2 100644 --- a/pyrogram/client/__init__.py +++ b/pyrogram/client/__init__.py @@ -19,8 +19,7 @@ from .client import Client from .ext import BaseClient, ChatAction, Emoji, ParseMode from .filters import Filters -from .handlers import ( - MessageHandler, DeletedMessagesHandler, - CallbackQueryHandler, RawUpdateHandler, - DisconnectHandler, UserStatusHandler -) + +__all__ = [ + "Client", "BaseClient", "ChatAction", "Emoji", "ParseMode", "Filters", +] diff --git a/pyrogram/client/client.py b/pyrogram/client/client.py index 33b3f137..7ea06b3e 100644 --- a/pyrogram/client/client.py +++ b/pyrogram/client/client.py @@ -39,7 +39,11 @@ from typing import Union, List, Type from pyrogram.api import functions, types from pyrogram.api.core import Object -from pyrogram.api.errors import ( +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.errors import ( PhoneMigrate, NetworkMigrate, PhoneNumberInvalid, PhoneNumberUnoccupied, PhoneCodeInvalid, PhoneCodeHashEmpty, PhoneCodeExpired, PhoneCodeEmpty, SessionPasswordNeeded, @@ -47,13 +51,8 @@ from pyrogram.api.errors import ( 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 -from .ext import utils, Syncer, BaseClient +from .ext import utils, Syncer, BaseClient, Dispatcher from .methods import Methods from .session_storage import ( SessionDoesNotExist, SessionStorage, MemorySessionStorage, JsonSessionStorage, @@ -70,9 +69,10 @@ class Client(Methods, BaseClient): Args: session_name (``str``): - Name to uniquely identify a session of either a User or a Bot, e.g.: "my_main_account". - You still can use bot token here, but it will be deprecated in next release. - Note: as long as a valid User session file exists, Pyrogram won't ask you again to input your phone number. + Name to uniquely identify a session of either a User or a Bot, e.g.: "my_account". This name will be used + to save a file to disk that stores details needed for reconnecting without asking again for credentials. + Note for bots: You can pass a bot token here, but this usage will be deprecated in next releases. + Use *bot_token* instead. api_id (``int``, *optional*): The *api_id* part of your Telegram API Key, as integer. E.g.: 12345 @@ -182,31 +182,35 @@ class Client(Methods, BaseClient): Defaults to False (normal session). """ - def __init__(self, - session_name: Union[str, SessionStorage], - api_id: Union[int, str] = None, - api_hash: str = None, - app_version: str = None, - device_model: str = None, - system_version: str = None, - lang_code: str = None, - ipv6: bool = False, - proxy: dict = None, - test_mode: bool = False, - phone_number: str = None, - phone_code: Union[str, callable] = None, - password: str = None, - recovery_code: callable = None, - force_sms: bool = False, - bot_token: str = None, - first_name: str = None, - last_name: str = None, - workers: int = BaseClient.WORKERS, - workdir: str = BaseClient.WORKDIR, - config_file: str = BaseClient.CONFIG_FILE, - plugins: dict = None, - no_updates: bool = None, - takeout: bool = None): + terms_of_service_displayed = False + + def __init__( + self, + session_name: str, + api_id: Union[int, str] = None, + api_hash: str = None, + app_version: str = None, + device_model: str = None, + system_version: str = None, + lang_code: str = None, + ipv6: bool = False, + proxy: dict = None, + test_mode: bool = False, + phone_number: str = None, + phone_code: Union[str, callable] = None, + password: str = None, + recovery_code: callable = None, + force_sms: bool = False, + bot_token: str = None, + first_name: str = None, + last_name: str = None, + workers: int = BaseClient.WORKERS, + workdir: str = BaseClient.WORKDIR, + config_file: str = BaseClient.CONFIG_FILE, + plugins: dict = None, + no_updates: bool = None, + takeout: bool = None + ): if isinstance(session_name, str): if session_name == ':memory:': @@ -222,6 +226,8 @@ class Client(Methods, BaseClient): super().__init__(session_storage) + super().__init__(session_storage) + self.session_name = str(session_name) # TODO: build correct session name self.api_id = int(api_id) if api_id else None self.api_hash = api_hash @@ -277,7 +283,7 @@ class Client(Methods, BaseClient): Requires no parameters. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. ``ConnectionError`` in case you try to start an already started Client. """ if self.is_started: @@ -288,10 +294,10 @@ class Client(Methods, BaseClient): self.session_storage.is_bot = True self.bot_token = self.session_storage._session_name self.session_storage._session_name = self.session_storage._session_name.split(":")[0] - warnings.warn('\nYou are using a bot token as session name.\n' - 'It will be deprecated in next update, please use session file name to load ' - 'existing sessions and bot_token argument to create new sessions.', - DeprecationWarning, stacklevel=2) + warnings.warn('\nWARNING: You are using a bot token as session name!\n' + 'This usage will be deprecated soon. Please use a session file name to load ' + 'an existing session and the bot_token argument to create new sessions.\n' + 'More info: https://docs.pyrogram.ml/start/Setup#bot-authorization\n') self.load_config() self.load_session() @@ -309,6 +315,7 @@ class Client(Methods, BaseClient): try: if self.session_storage.user_id is None: if self.bot_token is None: + self.is_bot = False self.authorize_user() else: self.session_storage.is_bot = True @@ -446,7 +453,7 @@ class Client(Methods, BaseClient): Requires no parameters. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ self.start() self.idle() @@ -558,9 +565,10 @@ class Client(Methods, BaseClient): try: r = self.send( functions.auth.SendCode( - self.phone_number, - self.api_id, - self.api_hash + phone_number=self.phone_number, + api_id=self.api_id, + api_hash=self.api_hash, + settings=types.CodeSettings() ) ) except (PhoneMigrate, NetworkMigrate) as e: @@ -604,8 +612,9 @@ class Client(Methods, BaseClient): phone_code_hash = r.phone_code_hash terms_of_service = r.terms_of_service - if terms_of_service: + if terms_of_service and not Client.terms_of_service_displayed: print("\n" + terms_of_service.text + "\n") + Client.terms_of_service_displayed = True if self.force_sms: self.send( @@ -640,9 +649,9 @@ class Client(Methods, BaseClient): try: r = self.send( functions.auth.SignIn( - self.phone_number, - phone_code_hash, - self.phone_code + phone_number=self.phone_number, + phone_code_hash=phone_code_hash, + phone_code=self.phone_code ) ) except PhoneNumberUnoccupied: @@ -653,11 +662,11 @@ class Client(Methods, BaseClient): try: r = self.send( functions.auth.SignUp( - self.phone_number, - phone_code_hash, - self.phone_code, - self.first_name, - self.last_name + phone_number=self.phone_number, + phone_code_hash=phone_code_hash, + phone_code=self.phone_code, + first_name=self.first_name, + last_name=self.last_name ) ) except PhoneNumberOccupied: @@ -751,7 +760,11 @@ class Client(Methods, BaseClient): break if terms_of_service: - assert self.send(functions.help.AcceptTermsOfService(terms_of_service.id)) + assert self.send( + functions.help.AcceptTermsOfService( + id=terms_of_service.id + ) + ) self.password = None self.session_storage.user_id = r.user.id @@ -992,16 +1005,16 @@ class Client(Methods, BaseClient): Timeout in seconds. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ if not self.is_started: raise ConnectionError("Client has not been started") if self.no_updates: - data = functions.InvokeWithoutUpdates(data) + data = functions.InvokeWithoutUpdates(query=data) if self.takeout_id: - data = functions.InvokeWithTakeout(self.takeout_id, data) + data = functions.InvokeWithTakeout(takeout_id=self.takeout_id, query=data) r = self.session.send(data, retries, timeout) @@ -1118,7 +1131,7 @@ class Client(Methods, BaseClient): try: module = import_module(module_path) - except ModuleNotFoundError: + except ImportError: log.warning('[LOAD] Ignoring non-existent module "{}"'.format(module_path)) continue @@ -1154,7 +1167,7 @@ class Client(Methods, BaseClient): try: module = import_module(module_path) - except ModuleNotFoundError: + except ImportError: log.warning('[UNLOAD] Ignoring non-existent module "{}"'.format(module_path)) continue @@ -1241,7 +1254,7 @@ class Client(Methods, BaseClient): On success, the resolved peer id is returned in form of an InputPeer object. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. ``KeyError`` in case the peer doesn't exist in the internal database. """ try: @@ -1276,7 +1289,7 @@ class Client(Methods, BaseClient): self.fetch_peers( self.send( functions.users.GetUsers( - id=[types.InputUser(peer_id, 0)] + id=[types.InputUser(user_id=peer_id, access_hash=0)] ) ) ) @@ -1284,7 +1297,7 @@ class Client(Methods, BaseClient): if str(peer_id).startswith("-100"): self.send( functions.channels.GetChannels( - id=[types.InputChannel(int(str(peer_id)[4:]), 0)] + id=[types.InputChannel(channel_id=int(str(peer_id)[4:]), access_hash=0)] ) ) else: @@ -1348,7 +1361,7 @@ class Client(Methods, BaseClient): On success, the uploaded file is returned in form of an InputFile object. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ part_size = 512 * 1024 file_size = os.path.getsize(path) @@ -1591,8 +1604,8 @@ class Client(Methods, BaseClient): hashes = session.send( functions.upload.GetCdnFileHashes( - r.file_token, - offset + file_token=r.file_token, + offset=offset ) ) diff --git a/pyrogram/client/ext/__init__.py b/pyrogram/client/ext/__init__.py index ce80958a..18c28ac3 100644 --- a/pyrogram/client/ext/__init__.py +++ b/pyrogram/client/ext/__init__.py @@ -18,6 +18,7 @@ from .base_client import BaseClient from .chat_action import ChatAction +from .dispatcher import Dispatcher from .emoji import Emoji from .parse_mode import ParseMode from .syncer import Syncer diff --git a/pyrogram/client/ext/base_client.py b/pyrogram/client/ext/base_client.py index 1ec65c93..a8922846 100644 --- a/pyrogram/client/ext/base_client.py +++ b/pyrogram/client/ext/base_client.py @@ -74,8 +74,8 @@ class BaseClient: self.rnd_id = MsgId self.channels_pts = {} - self.markdown = Markdown(self.session_storage) - self.html = HTML(self.session_storage) + self.markdown = Markdown(self.session_storage, self) + self.html = HTML(self.session_storage, self) self.session = None self.media_sessions = {} @@ -122,3 +122,6 @@ class BaseClient: def get_chat_members_count(self, *args, **kwargs): pass + + def answer_inline_query(self, *args, **kwargs): + pass diff --git a/pyrogram/client/dispatcher/dispatcher.py b/pyrogram/client/ext/dispatcher.py similarity index 94% rename from pyrogram/client/dispatcher/dispatcher.py rename to pyrogram/client/ext/dispatcher.py index 5a463077..7552b034 100644 --- a/pyrogram/client/dispatcher/dispatcher.py +++ b/pyrogram/client/ext/dispatcher.py @@ -24,7 +24,10 @@ from threading import Thread import pyrogram from pyrogram.api import types -from ..handlers import CallbackQueryHandler, MessageHandler, RawUpdateHandler, UserStatusHandler, DeletedMessagesHandler +from ..handlers import ( + CallbackQueryHandler, MessageHandler, DeletedMessagesHandler, + UserStatusHandler, RawUpdateHandler, InlineQueryHandler +) log = logging.getLogger(__name__) @@ -73,7 +76,10 @@ class Dispatcher: (types.UpdateUserStatus,): lambda upd, usr, cht: ( pyrogram.UserStatus._parse(self.client, upd.status, upd.user_id), UserStatusHandler - ) + ), + + (types.UpdateBotInlineQuery,): + lambda upd, usr, cht: (pyrogram.InlineQuery._parse(self.client, upd, usr), InlineQueryHandler) } self.update_parsers = {key: value for key_tuple, value in self.update_parsers.items() for key in key_tuple} diff --git a/pyrogram/client/ext/utils.py b/pyrogram/client/ext/utils.py index 087773f4..981752fa 100644 --- a/pyrogram/client/ext/utils.py +++ b/pyrogram/client/ext/utils.py @@ -67,10 +67,10 @@ def get_peer_id(input_peer) -> int: def get_input_peer(peer_id: int, access_hash: int): return ( - types.InputPeerUser(peer_id, access_hash) if peer_id > 0 - else types.InputPeerChannel(int(str(peer_id)[4:]), access_hash) + types.InputPeerUser(user_id=peer_id, access_hash=access_hash) if peer_id > 0 + else types.InputPeerChannel(channel_id=int(str(peer_id)[4:]), access_hash=access_hash) if (str(peer_id).startswith("-100") and access_hash) - else types.InputPeerChat(-peer_id) + else types.InputPeerChat(chat_id=-peer_id) ) diff --git a/pyrogram/client/filters/filters.py b/pyrogram/client/filters/filters.py index f5bcd5b5..4c6f0ce3 100644 --- a/pyrogram/client/filters/filters.py +++ b/pyrogram/client/filters/filters.py @@ -115,7 +115,7 @@ class Filters: voice = create("Voice", lambda _, m: bool(m.voice)) """Filter messages that contain :obj:`Voice ` note objects.""" - video_note = create("Voice", lambda _, m: bool(m.video_note)) + video_note = create("VideoNote", lambda _, m: bool(m.video_note)) """Filter messages that contain :obj:`VideoNote ` objects.""" contact = create("Contact", lambda _, m: bool(m.contact)) @@ -222,14 +222,16 @@ class Filters: - poll""" @staticmethod - def command(command: str or list, - prefix: str or list = "/", - separator: str = " ", - case_sensitive: bool = False): + def command( + commands: str or list, + prefix: str or list = "/", + separator: str = " ", + case_sensitive: bool = False + ): """Filter commands, i.e.: text messages starting with "/" or any other custom prefix. Args: - command (``str`` | ``list``): + commands (``str`` | ``list``): The command or list of commands as string the filter should look for. Examples: "start", ["start", "help", "settings"]. When a message text containing a command arrives, the command itself and its arguments will be stored in the *command* @@ -249,31 +251,25 @@ class Filters: Examples: when True, command="Start" would trigger /Start but not /start. """ - def f(_, m): - if m.text: - for i in _.p: - if m.text.startswith(i): - t = m.text.split(_.s) - c, a = t[0][len(i):], t[1:] - c = c if _.cs else c.lower() - m.command = ([c] + a) if c in _.c else None + def func(flt, message): + text = message.text or message.caption + + if text: + for p in flt.p: + if text.startswith(p): + s = text.split(flt.s) + c, a = s[0][len(p):], s[1:] + c = c if flt.cs else c.lower() + message.command = ([c] + a) if c in flt.c else None break - return bool(m.command) + return bool(message.command) - return create( - "Command", - f, - c={command if case_sensitive - else command.lower()} - if not isinstance(command, list) - else {c if case_sensitive - else c.lower() - for c in command}, - p=set(prefix) if prefix else {""}, - s=separator, - cs=case_sensitive - ) + commands = commands if type(commands) is list else [commands] + commands = {c if case_sensitive else c.lower() for c in commands} + prefixes = set(prefix) if prefix else {""} + + return create("Command", func=func, c=commands, p=prefixes, s=separator, cs=case_sensitive) @staticmethod def regex(pattern, flags: int = 0): @@ -311,21 +307,20 @@ class Filters: def __init__(self, users: int or str or list = None): users = [] if users is None else users if type(users) is list else [users] + super().__init__( - {"me" if i in ["me", "self"] else i.lower().strip("@") if type(i) is str else i for i in users} - if type(users) is list else - {"me" if users in ["me", "self"] else users.lower().strip("@") if type(users) is str else users} + "me" if u in ["me", "self"] + else u.lower().strip("@") if type(u) is str + else u for u in users ) def __call__(self, message): - return bool( - message.from_user - and (message.from_user.id in self - or (message.from_user.username - and message.from_user.username.lower() in self) - or ("me" in self - and message.from_user.is_self)) - ) + return (message.from_user + and (message.from_user.id in self + or (message.from_user.username + and message.from_user.username.lower() in self) + or ("me" in self + and message.from_user.is_self))) # noinspection PyPep8Naming class chat(Filter, set): @@ -343,21 +338,21 @@ class Filters: def __init__(self, chats: int or str or list = None): chats = [] if chats is None else chats if type(chats) is list else [chats] + super().__init__( - {"me" if i in ["me", "self"] else i.lower().strip("@") if type(i) is str else i for i in chats} - if type(chats) is list else - {"me" if chats in ["me", "self"] else chats.lower().strip("@") if type(chats) is str else chats} + "me" if c in ["me", "self"] + else c.lower().strip("@") if type(c) is str + else c for c in chats ) def __call__(self, message): - return bool( - message.chat - and (message.chat.id in self - or (message.chat.username - and message.chat.username.lower() in self) - or ("me" in self and message.from_user - and message.from_user.is_self - and not message.outgoing)) - ) + return (message.chat + and (message.chat.id in self + or (message.chat.username + and message.chat.username.lower() in self) + or ("me" in self + and message.from_user + and message.from_user.is_self + and not message.outgoing))) dan = create("Dan", lambda _, m: bool(m.from_user and m.from_user.id == 23122162)) diff --git a/pyrogram/client/handlers/__init__.py b/pyrogram/client/handlers/__init__.py index 499ed007..5e392949 100644 --- a/pyrogram/client/handlers/__init__.py +++ b/pyrogram/client/handlers/__init__.py @@ -19,6 +19,12 @@ from .callback_query_handler import CallbackQueryHandler from .deleted_messages_handler import DeletedMessagesHandler from .disconnect_handler import DisconnectHandler +from .inline_query_handler import InlineQueryHandler from .message_handler import MessageHandler from .raw_update_handler import RawUpdateHandler from .user_status_handler import UserStatusHandler + +__all__ = [ + "MessageHandler", "DeletedMessagesHandler", "CallbackQueryHandler", "RawUpdateHandler", "DisconnectHandler", + "UserStatusHandler", "InlineQueryHandler" +] diff --git a/pyrogram/client/handlers/inline_query_handler.py b/pyrogram/client/handlers/inline_query_handler.py new file mode 100644 index 00000000..e59514c0 --- /dev/null +++ b/pyrogram/client/handlers/inline_query_handler.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 .handler import Handler + + +class InlineQueryHandler(Handler): + """The InlineQuery handler class. Used to handle inline queries. + It is intended to be used with :meth:`add_handler() ` + + For a nicer way to register this handler, have a look at the + :meth:`on_inline_query() ` decorator. + + Args: + callback (``callable``): + Pass a function that will be called when a new InlineQuery arrives. It takes *(client, inline_query)* + as positional arguments (look at the section below for a detailed description). + + filters (:obj:`Filters `): + Pass one or more filters to allow only a subset of inline queries to be passed + in your callback function. + + Other parameters: + client (:obj:`Client `): + The Client itself, useful when you want to call other API methods inside the inline query handler. + + inline_query (:obj:`InlineQuery `): + The received inline query. + """ + + def __init__(self, callback: callable, filters=None): + super().__init__(callback, filters) + + def check(self, callback_query): + return ( + self.filters(callback_query) + if callable(self.filters) + else True + ) diff --git a/pyrogram/client/methods/bots/__init__.py b/pyrogram/client/methods/bots/__init__.py index 65d132a0..916a62dc 100644 --- a/pyrogram/client/methods/bots/__init__.py +++ b/pyrogram/client/methods/bots/__init__.py @@ -17,6 +17,7 @@ # along with Pyrogram. If not, see . from .answer_callback_query import AnswerCallbackQuery +from .answer_inline_query import AnswerInlineQuery from .get_game_high_scores import GetGameHighScores from .get_inline_bot_results import GetInlineBotResults from .request_callback_answer import RequestCallbackAnswer @@ -27,6 +28,7 @@ from .set_game_score import SetGameScore class Bots( AnswerCallbackQuery, + AnswerInlineQuery, GetInlineBotResults, RequestCallbackAnswer, SendInlineBotResult, diff --git a/pyrogram/client/methods/bots/answer_callback_query.py b/pyrogram/client/methods/bots/answer_callback_query.py index 5e8468e5..33458db9 100644 --- a/pyrogram/client/methods/bots/answer_callback_query.py +++ b/pyrogram/client/methods/bots/answer_callback_query.py @@ -21,12 +21,14 @@ from pyrogram.client.ext import BaseClient class AnswerCallbackQuery(BaseClient): - def answer_callback_query(self, - callback_query_id: str, - text: str = None, - show_alert: bool = None, - url: str = None, - cache_time: int = 0): + def answer_callback_query( + self, + callback_query_id: str, + text: str = None, + show_alert: bool = None, + url: str = None, + cache_time: int = 0 + ): """Use this method to send answers to callback queries sent from inline keyboards. The answer will be displayed to the user as a notification at the top of the chat screen or as an alert. @@ -55,7 +57,7 @@ class AnswerCallbackQuery(BaseClient): True, on success. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ return self.send( functions.messages.SetBotCallbackAnswer( diff --git a/pyrogram/client/methods/bots/answer_inline_query.py b/pyrogram/client/methods/bots/answer_inline_query.py new file mode 100644 index 00000000..7b3524b2 --- /dev/null +++ b/pyrogram/client/methods/bots/answer_inline_query.py @@ -0,0 +1,91 @@ +# 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 + +from pyrogram.api import functions, types +from pyrogram.client.ext import BaseClient +from ...types.inline_mode import InlineQueryResult + + +class AnswerInlineQuery(BaseClient): + def answer_inline_query( + self, + inline_query_id: str, + results: List[InlineQueryResult], + cache_time: int = 300, + is_personal: bool = None, + next_offset: str = "", + switch_pm_text: str = "", + switch_pm_parameter: str = "" + ): + """Use this method to send answers to an inline query. + No more than 50 results per query are allowed. + + Args: + inline_query_id (``str``): + Unique identifier for the answered query. + + results (List of :obj:`InlineQueryResult `): + A list of results for the inline query. + + cache_time (``int``, *optional*): + The maximum amount of time in seconds that the result of the inline query may be cached on the server. + Defaults to 300. + + is_personal (``bool``, *optional*): + Pass True, if results may be cached on the server side only for the user that sent the query. + By default, results may be returned to any user who sends the same query. + + next_offset (``str``, *optional*): + Pass the offset that a client should send in the next query with the same text to receive more results. + Pass an empty string if there are no more results or if you don‘t support pagination. + Offset length can’t exceed 64 bytes. + + switch_pm_text (``str``, *optional*): + If passed, clients will display a button with specified text that switches the user to a private chat + with the bot and sends the bot a start message with the parameter switch_pm_parameter + + switch_pm_parameter (``str``, *optional*): + `Deep-linking `_ parameter for the /start message sent to + the bot when user presses the switch button. 1-64 characters, only A-Z, a-z, 0-9, _ and - are allowed. + + Example: An inline bot that sends YouTube videos can ask the user to connect the bot to their YouTube + account to adapt search results accordingly. To do this, it displays a "Connect your YouTube account" + button above the results, or even before showing any. The user presses the button, switches to a private + chat with the bot and, in doing so, passes a start parameter that instructs the bot to return an oauth + link. Once done, the bot can offer a switch_inline button so that the user can easily return to the chat + where they wanted to use the bot's inline capabilities. + + Returns: + On success, True is returned. + """ + return self.send( + functions.messages.SetInlineBotResults( + query_id=int(inline_query_id), + results=[r.write() for r in results], + cache_time=cache_time, + gallery=None, + private=is_personal or None, + next_offset=next_offset or None, + switch_pm=types.InlineBotSwitchPM( + text=switch_pm_text, + start_param=switch_pm_parameter + ) if switch_pm_text else None + ) + ) diff --git a/pyrogram/client/methods/bots/get_game_high_scores.py b/pyrogram/client/methods/bots/get_game_high_scores.py index ad4f8b4a..bb2e99db 100644 --- a/pyrogram/client/methods/bots/get_game_high_scores.py +++ b/pyrogram/client/methods/bots/get_game_high_scores.py @@ -24,10 +24,12 @@ from pyrogram.client.ext import BaseClient class GetGameHighScores(BaseClient): - def get_game_high_scores(self, - user_id: Union[int, str], - chat_id: Union[int, str], - message_id: int = None): + def get_game_high_scores( + self, + user_id: Union[int, str], + chat_id: Union[int, str], + message_id: int = None + ): """Use this method to get data for high score tables. Args: @@ -50,7 +52,7 @@ class GetGameHighScores(BaseClient): On success, a :obj:`GameHighScores ` object is returned. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ # TODO: inline_message_id diff --git a/pyrogram/client/methods/bots/get_inline_bot_results.py b/pyrogram/client/methods/bots/get_inline_bot_results.py index e76a0d1d..b12c0439 100644 --- a/pyrogram/client/methods/bots/get_inline_bot_results.py +++ b/pyrogram/client/methods/bots/get_inline_bot_results.py @@ -19,17 +19,19 @@ from typing import Union from pyrogram.api import functions, types -from pyrogram.api.errors import UnknownError +from pyrogram.errors import UnknownError from pyrogram.client.ext import BaseClient class GetInlineBotResults(BaseClient): - def get_inline_bot_results(self, - bot: Union[int, str], - query: str, - offset: str = "", - latitude: float = None, - longitude: float = None): + def get_inline_bot_results( + self, + bot: Union[int, str], + query: str, + offset: str = "", + latitude: float = None, + longitude: float = None + ): """Use this method to get bot results via inline queries. You can then send a result using :obj:`send_inline_bot_result ` @@ -56,7 +58,7 @@ class GetInlineBotResults(BaseClient): On Success, :obj:`BotResults ` is returned. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. ``TimeoutError`` if the bot fails to answer within 10 seconds """ # TODO: Don't return the raw type diff --git a/pyrogram/client/methods/bots/request_callback_answer.py b/pyrogram/client/methods/bots/request_callback_answer.py index 74e2d26d..7b37f51a 100644 --- a/pyrogram/client/methods/bots/request_callback_answer.py +++ b/pyrogram/client/methods/bots/request_callback_answer.py @@ -23,12 +23,14 @@ from pyrogram.client.ext import BaseClient class RequestCallbackAnswer(BaseClient): - def request_callback_answer(self, - chat_id: Union[int, str], - message_id: int, - callback_data: bytes): - """Use this method to request a callback answer from bots. This is the equivalent of clicking an - inline button containing callback data. + def request_callback_answer( + self, + chat_id: Union[int, str], + message_id: int, + callback_data: bytes + ): + """Use this method to request a callback answer from bots. + This is the equivalent of clicking an inline button containing callback data. Args: chat_id (``int`` | ``str``): @@ -47,7 +49,7 @@ class RequestCallbackAnswer(BaseClient): or as an alert. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. ``TimeoutError`` if the bot fails to answer within 10 seconds. """ return self.send( diff --git a/pyrogram/client/methods/bots/send_game.py b/pyrogram/client/methods/bots/send_game.py index 401a5aa6..a690c960 100644 --- a/pyrogram/client/methods/bots/send_game.py +++ b/pyrogram/client/methods/bots/send_game.py @@ -24,15 +24,19 @@ from pyrogram.client.ext import BaseClient class SendGame(BaseClient): - def send_game(self, - chat_id: Union[int, str], - game_short_name: 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": + def send_game( + self, + chat_id: Union[int, str], + game_short_name: 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 game. Args: @@ -59,7 +63,7 @@ class SendGame(BaseClient): On success, the sent :obj:`Message` is returned. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ r = self.send( functions.messages.SendMedia( diff --git a/pyrogram/client/methods/bots/send_inline_bot_result.py b/pyrogram/client/methods/bots/send_inline_bot_result.py index 66caab16..9b375a0a 100644 --- a/pyrogram/client/methods/bots/send_inline_bot_result.py +++ b/pyrogram/client/methods/bots/send_inline_bot_result.py @@ -23,13 +23,15 @@ from pyrogram.client.ext import BaseClient class SendInlineBotResult(BaseClient): - def send_inline_bot_result(self, - chat_id: Union[int, str], - query_id: int, - result_id: str, - disable_notification: bool = None, - reply_to_message_id: int = None, - hide_via: bool = None): + def send_inline_bot_result( + self, + chat_id: Union[int, str], + query_id: int, + result_id: str, + disable_notification: bool = 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 ` @@ -59,7 +61,7 @@ class SendInlineBotResult(BaseClient): On success, the sent Message is returned. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ return self.send( functions.messages.SendInlineBotResult( diff --git a/pyrogram/client/methods/bots/set_game_score.py b/pyrogram/client/methods/bots/set_game_score.py index e9d20844..434720c6 100644 --- a/pyrogram/client/methods/bots/set_game_score.py +++ b/pyrogram/client/methods/bots/set_game_score.py @@ -24,13 +24,15 @@ from pyrogram.client.ext import BaseClient class SetGameScore(BaseClient): - def set_game_score(self, - user_id: Union[int, str], - score: int, - force: bool = None, - disable_edit_message: bool = None, - chat_id: Union[int, str] = None, - message_id: int = None): + def set_game_score( + self, + user_id: Union[int, str], + score: int, + force: bool = None, + disable_edit_message: bool = None, + chat_id: Union[int, str] = None, + message_id: int = None + ): # inline_message_id: str = None): TODO Add inline_message_id """Use this method to set the score of the specified user in a game. @@ -65,7 +67,7 @@ class SetGameScore(BaseClient): otherwise returns True. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. :class:`BotScoreNotModified` if the new score is not greater than the user's current score in the chat and force is False. """ r = self.send( diff --git a/pyrogram/client/methods/chats/__init__.py b/pyrogram/client/methods/chats/__init__.py index 6cc034e4..c708453f 100644 --- a/pyrogram/client/methods/chats/__init__.py +++ b/pyrogram/client/methods/chats/__init__.py @@ -31,12 +31,14 @@ from .kick_chat_member import KickChatMember from .leave_chat import LeaveChat from .pin_chat_message import PinChatMessage from .promote_chat_member import PromoteChatMember +from .restrict_chat import RestrictChat from .restrict_chat_member import RestrictChatMember from .set_chat_description import SetChatDescription from .set_chat_photo import SetChatPhoto from .set_chat_title import SetChatTitle from .unban_chat_member import UnbanChatMember from .unpin_chat_message import UnpinChatMessage +from .update_chat_username import UpdateChatUsername class Chats( @@ -60,6 +62,8 @@ class Chats( GetChatMembersCount, GetChatPreview, IterDialogs, - IterChatMembers + IterChatMembers, + UpdateChatUsername, + RestrictChat ): pass diff --git a/pyrogram/client/methods/chats/delete_chat_photo.py b/pyrogram/client/methods/chats/delete_chat_photo.py index 0164ff43..c11a0d13 100644 --- a/pyrogram/client/methods/chats/delete_chat_photo.py +++ b/pyrogram/client/methods/chats/delete_chat_photo.py @@ -23,8 +23,10 @@ from ...ext import BaseClient class DeleteChatPhoto(BaseClient): - def delete_chat_photo(self, - chat_id: Union[int, str]) -> bool: + def delete_chat_photo( + self, + chat_id: Union[int, str] + ) -> bool: """Use this method to delete a chat photo. Photos can't be changed for private chats. You must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -41,7 +43,7 @@ class DeleteChatPhoto(BaseClient): True on success. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. ``ValueError`` if a chat_id belongs to user. """ peer = self.resolve_peer(chat_id) diff --git a/pyrogram/client/methods/chats/export_chat_invite_link.py b/pyrogram/client/methods/chats/export_chat_invite_link.py index 9ff8c9c0..b84b1d3c 100644 --- a/pyrogram/client/methods/chats/export_chat_invite_link.py +++ b/pyrogram/client/methods/chats/export_chat_invite_link.py @@ -23,8 +23,10 @@ from ...ext import BaseClient class ExportChatInviteLink(BaseClient): - def export_chat_invite_link(self, - chat_id: Union[int, str]) -> str: + def export_chat_invite_link( + self, + chat_id: Union[int, str] + ) -> str: """Use this method to generate a new invite link for a chat; any previously generated link is revoked. You must be an administrator in the chat for this to work and have the appropriate admin rights. @@ -38,14 +40,14 @@ class ExportChatInviteLink(BaseClient): On success, the exported invite link as string is returned. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ peer = self.resolve_peer(chat_id) if isinstance(peer, types.InputPeerChat): return self.send( functions.messages.ExportChatInvite( - chat_id=peer.chat_id + peer=peer.chat_id ) ).link elif isinstance(peer, types.InputPeerChannel): diff --git a/pyrogram/client/methods/chats/get_chat.py b/pyrogram/client/methods/chats/get_chat.py index d30fd9bb..38653459 100644 --- a/pyrogram/client/methods/chats/get_chat.py +++ b/pyrogram/client/methods/chats/get_chat.py @@ -24,10 +24,13 @@ from ...ext import BaseClient class GetChat(BaseClient): - def get_chat(self, - chat_id: Union[int, str]) -> "pyrogram.Chat": - """Use this method to get up to date information about the chat (current name of the user for - one-on-one conversations, current username of a user, group or channel, etc.) + def get_chat( + self, + chat_id: Union[int, str] + ) -> "pyrogram.Chat": + """Use this method to get up to date information about the chat. + Information include current name of the user for one-on-one conversations, current username of a user, group or + channel, etc. Args: chat_id (``int`` | ``str``): @@ -39,7 +42,7 @@ class GetChat(BaseClient): On success, a :obj:`Chat ` object is returned. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. ``ValueError`` in case the chat invite link refers to a chat you haven't joined yet. """ match = self.INVITE_LINK_RE.match(str(chat_id)) @@ -67,10 +70,10 @@ class GetChat(BaseClient): peer = self.resolve_peer(chat_id) if isinstance(peer, types.InputPeerChannel): - r = self.send(functions.channels.GetFullChannel(peer)) + r = self.send(functions.channels.GetFullChannel(channel=peer)) elif isinstance(peer, (types.InputPeerUser, types.InputPeerSelf)): - r = self.send(functions.users.GetFullUser(peer)) + r = self.send(functions.users.GetFullUser(id=peer)) else: - r = self.send(functions.messages.GetFullChat(peer.chat_id)) + r = self.send(functions.messages.GetFullChat(chat_id=peer.chat_id)) return pyrogram.Chat._parse_full(self, r) diff --git a/pyrogram/client/methods/chats/get_chat_member.py b/pyrogram/client/methods/chats/get_chat_member.py index faf0c33b..aec4d233 100644 --- a/pyrogram/client/methods/chats/get_chat_member.py +++ b/pyrogram/client/methods/chats/get_chat_member.py @@ -19,14 +19,17 @@ from typing import Union import pyrogram -from pyrogram.api import functions, types, errors +from pyrogram.api import functions, types +from pyrogram.errors import UserNotParticipant from ...ext import BaseClient class GetChatMember(BaseClient): - def get_chat_member(self, - chat_id: Union[int, str], - user_id: Union[int, str]) -> "pyrogram.ChatMember": + def get_chat_member( + self, + chat_id: Union[int, str], + user_id: Union[int, str] + ) -> "pyrogram.ChatMember": """Use this method to get information about one member of a chat. Args: @@ -42,7 +45,7 @@ class GetChatMember(BaseClient): On success, a :obj:`ChatMember ` object is returned. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ chat_id = self.resolve_peer(chat_id) user_id = self.resolve_peer(user_id) @@ -55,10 +58,10 @@ class GetChatMember(BaseClient): ) for member in pyrogram.ChatMembers._parse(self, full_chat).chat_members: - if member.user.id == user_id.user_id: + if member.user.is_self: return member else: - raise errors.UserNotParticipant + raise UserNotParticipant elif isinstance(chat_id, types.InputPeerChannel): r = self.send( functions.channels.GetParticipant( diff --git a/pyrogram/client/methods/chats/get_chat_members.py b/pyrogram/client/methods/chats/get_chat_members.py index 382d7f0f..726fd14b 100644 --- a/pyrogram/client/methods/chats/get_chat_members.py +++ b/pyrogram/client/methods/chats/get_chat_members.py @@ -16,12 +16,17 @@ # You should have received a copy of the GNU Lesser General Public License # along with Pyrogram. If not, see . +import logging +import time from typing import Union import pyrogram from pyrogram.api import functions, types +from pyrogram.errors import FloodWait from ...ext import BaseClient +log = logging.getLogger(__name__) + class Filters: ALL = "all" @@ -33,12 +38,14 @@ class Filters: class GetChatMembers(BaseClient): - def get_chat_members(self, - chat_id: Union[int, str], - offset: int = 0, - limit: int = 200, - query: str = "", - filter: str = Filters.ALL) -> "pyrogram.ChatMembers": + def get_chat_members( + self, + chat_id: Union[int, str], + offset: int = 0, + limit: int = 200, + query: str = "", + filter: str = Filters.ALL + ) -> "pyrogram.ChatMembers": """Use this method to get a chunk of the members list of a chat. You can get up to 200 chat members at once. @@ -82,7 +89,7 @@ class GetChatMembers(BaseClient): On success, a :obj:`ChatMembers` object is returned. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. ``ValueError`` if you used an invalid filter or a chat_id that belongs to a user. """ peer = self.resolve_peer(chat_id) @@ -92,7 +99,7 @@ class GetChatMembers(BaseClient): self, self.send( functions.messages.GetFullChat( - peer.chat_id + chat_id=peer.chat_id ) ) ) @@ -114,17 +121,22 @@ class GetChatMembers(BaseClient): else: raise ValueError("Invalid filter \"{}\"".format(filter)) - return pyrogram.ChatMembers._parse( - self, - self.send( - functions.channels.GetParticipants( - channel=peer, - filter=filter, - offset=offset, - limit=limit, - hash=0 + while True: + try: + return pyrogram.ChatMembers._parse( + self, + self.send( + functions.channels.GetParticipants( + channel=peer, + filter=filter, + offset=offset, + limit=limit, + hash=0 + ) + ) ) - ) - ) + except FloodWait as e: + log.warning("Sleeping for {}s".format(e.x)) + time.sleep(e.x) else: raise ValueError("The chat_id \"{}\" belongs to a user".format(chat_id)) diff --git a/pyrogram/client/methods/chats/get_chat_members_count.py b/pyrogram/client/methods/chats/get_chat_members_count.py index 9360b64f..fc13ac39 100644 --- a/pyrogram/client/methods/chats/get_chat_members_count.py +++ b/pyrogram/client/methods/chats/get_chat_members_count.py @@ -23,8 +23,10 @@ from ...ext import BaseClient class GetChatMembersCount(BaseClient): - def get_chat_members_count(self, - chat_id: Union[int, str]) -> int: + def get_chat_members_count( + self, + chat_id: Union[int, str] + ) -> int: """Use this method to get the number of members in a chat. Args: @@ -35,7 +37,7 @@ class GetChatMembersCount(BaseClient): On success, an integer is returned. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. ``ValueError`` if a chat_id belongs to user. """ peer = self.resolve_peer(chat_id) diff --git a/pyrogram/client/methods/chats/get_chat_preview.py b/pyrogram/client/methods/chats/get_chat_preview.py index 434c385b..9b6c6955 100644 --- a/pyrogram/client/methods/chats/get_chat_preview.py +++ b/pyrogram/client/methods/chats/get_chat_preview.py @@ -22,8 +22,10 @@ from ...ext import BaseClient class GetChatPreview(BaseClient): - def get_chat_preview(self, - invite_link: str): + def get_chat_preview( + self, + invite_link: str + ): """Use this method to get the preview of a chat using the invite link. This method only returns a chat preview, if you want to join a chat use :meth:`join_chat` @@ -36,7 +38,7 @@ class GetChatPreview(BaseClient): Either :obj:`Chat` or :obj:`ChatPreview`, depending on whether you already joined the chat or not. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. ``ValueError`` in case of an invalid invite_link. """ match = self.INVITE_LINK_RE.match(invite_link) diff --git a/pyrogram/client/methods/chats/get_dialogs.py b/pyrogram/client/methods/chats/get_dialogs.py index c5fe6cfb..3bcf223f 100644 --- a/pyrogram/client/methods/chats/get_dialogs.py +++ b/pyrogram/client/methods/chats/get_dialogs.py @@ -21,18 +21,20 @@ import time import pyrogram from pyrogram.api import functions, types -from pyrogram.api.errors import FloodWait +from pyrogram.errors import FloodWait from ...ext import BaseClient log = logging.getLogger(__name__) class GetDialogs(BaseClient): - def get_dialogs(self, - offset_date: int = 0, - limit: int = 100, - pinned_only: bool = False) -> "pyrogram.Dialogs": - """Use this method to get a chunk of the user's dialogs + def get_dialogs( + self, + offset_date: int = 0, + limit: int = 100, + pinned_only: bool = False + ) -> "pyrogram.Dialogs": + """Use this method to get a chunk of the user's dialogs. You can get up to 100 dialogs at once. For a more convenient way of getting a user's dialogs see :meth:`iter_dialogs`. @@ -54,7 +56,7 @@ class GetDialogs(BaseClient): On success, a :obj:`Dialogs` object is returned. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ while True: diff --git a/pyrogram/client/methods/chats/iter_chat_members.py b/pyrogram/client/methods/chats/iter_chat_members.py index 5d0fa911..2f41763e 100644 --- a/pyrogram/client/methods/chats/iter_chat_members.py +++ b/pyrogram/client/methods/chats/iter_chat_members.py @@ -20,6 +20,7 @@ from string import ascii_lowercase from typing import Union, Generator import pyrogram +from pyrogram.api import types from ...ext import BaseClient @@ -37,11 +38,13 @@ QUERYABLE_FILTERS = (Filters.ALL, Filters.KICKED, Filters.RESTRICTED) class IterChatMembers(BaseClient): - def iter_chat_members(self, - chat_id: Union[int, str], - limit: int = 0, - query: str = "", - filter: str = Filters.ALL) -> Generator["pyrogram.ChatMember", None, None]: + def iter_chat_members( + self, + chat_id: Union[int, str], + limit: int = 0, + query: str = "", + filter: str = Filters.ALL + ) -> Generator["pyrogram.ChatMember", None, None]: """Use this method to iterate through the members of a chat sequentially. This convenience method does the same as repeatedly calling :meth:`get_chat_members` in a loop, thus saving you @@ -75,13 +78,14 @@ class IterChatMembers(BaseClient): A generator yielding :obj:`ChatMember ` objects. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ current = 0 yielded = set() queries = [query] if query else QUERIES total = limit or (1 << 31) - 1 limit = min(200, total) + resolved_chat_id = self.resolve_peer(chat_id) filter = ( Filters.RECENT @@ -107,6 +111,9 @@ class IterChatMembers(BaseClient): if not chat_members: break + if isinstance(resolved_chat_id, types.InputPeerChat): + total = len(chat_members) + offset += len(chat_members) for chat_member in chat_members: diff --git a/pyrogram/client/methods/chats/iter_dialogs.py b/pyrogram/client/methods/chats/iter_dialogs.py index 6058cd17..99437cb4 100644 --- a/pyrogram/client/methods/chats/iter_dialogs.py +++ b/pyrogram/client/methods/chats/iter_dialogs.py @@ -23,9 +23,11 @@ from ...ext import BaseClient class IterDialogs(BaseClient): - def iter_dialogs(self, - offset_date: int = 0, - limit: int = 0) -> Generator["pyrogram.Dialog", None, None]: + def iter_dialogs( + self, + offset_date: int = 0, + limit: int = 0 + ) -> Generator["pyrogram.Dialog", None, None]: """Use this method to iterate through a user's dialogs sequentially. This convenience method does the same as repeatedly calling :meth:`get_dialogs` in a loop, thus saving you from @@ -44,7 +46,7 @@ class IterDialogs(BaseClient): A generator yielding :obj:`Dialog ` objects. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ current = 0 total = limit or (1 << 31) - 1 diff --git a/pyrogram/client/methods/chats/join_chat.py b/pyrogram/client/methods/chats/join_chat.py index f5b632fc..a7933bea 100644 --- a/pyrogram/client/methods/chats/join_chat.py +++ b/pyrogram/client/methods/chats/join_chat.py @@ -16,13 +16,16 @@ # You should have received a copy of the GNU Lesser General Public License # along with Pyrogram. If not, see . +import pyrogram from pyrogram.api import functions, types from ...ext import BaseClient class JoinChat(BaseClient): - def join_chat(self, - chat_id: str): + def join_chat( + self, + chat_id: str + ): """Use this method to join a group chat or channel. Args: @@ -30,17 +33,24 @@ class JoinChat(BaseClient): Unique identifier for the target chat in form of a *t.me/joinchat/* link or username of the target channel/supergroup (in the format @username). + Returns: + On success, a :obj:`Chat ` object is returned. + Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ match = self.INVITE_LINK_RE.match(chat_id) if match: - return self.send( + chat = self.send( functions.messages.ImportChatInvite( hash=match.group(1) ) ) + if isinstance(chat.chats[0], types.Chat): + return pyrogram.Chat._parse_chat_chat(self, chat.chats[0]) + elif isinstance(chat.chats[0], types.Channel): + return pyrogram.Chat._parse_channel_chat(self, chat.chats[0]) else: resolved_peer = self.send( functions.contacts.ResolveUsername( @@ -53,8 +63,10 @@ class JoinChat(BaseClient): access_hash=resolved_peer.chats[0].access_hash ) - return self.send( + chat = self.send( functions.channels.JoinChannel( channel=channel ) ) + + return pyrogram.Chat._parse_channel_chat(self, chat.chats[0]) diff --git a/pyrogram/client/methods/chats/kick_chat_member.py b/pyrogram/client/methods/chats/kick_chat_member.py index 4cd66ec4..7b10ddea 100644 --- a/pyrogram/client/methods/chats/kick_chat_member.py +++ b/pyrogram/client/methods/chats/kick_chat_member.py @@ -24,10 +24,12 @@ from ...ext import BaseClient class KickChatMember(BaseClient): - def kick_chat_member(self, - chat_id: Union[int, str], - user_id: Union[int, str], - until_date: int = 0) -> Union["pyrogram.Message", bool]: + def kick_chat_member( + self, + chat_id: Union[int, str], + user_id: Union[int, str], + until_date: int = 0 + ) -> Union["pyrogram.Message", bool]: """Use this method to kick a user from a group, a supergroup or a channel. In the case of supergroups and channels, the user will not be able to return to the group on their own using invite links, etc., unless unbanned first. You must be an administrator in the chat for this to work and must @@ -55,7 +57,7 @@ class KickChatMember(BaseClient): On success, either True or a service :obj:`Message ` will be returned (when applicable). Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ chat_peer = self.resolve_peer(chat_id) user_peer = self.resolve_peer(user_id) @@ -65,7 +67,7 @@ class KickChatMember(BaseClient): functions.channels.EditBanned( channel=chat_peer, user_id=user_peer, - banned_rights=types.ChannelBannedRights( + banned_rights=types.ChatBannedRights( until_date=until_date, view_messages=True, send_messages=True, diff --git a/pyrogram/client/methods/chats/leave_chat.py b/pyrogram/client/methods/chats/leave_chat.py index 5b765ac0..8ba3a3d1 100644 --- a/pyrogram/client/methods/chats/leave_chat.py +++ b/pyrogram/client/methods/chats/leave_chat.py @@ -23,9 +23,11 @@ from ...ext import BaseClient class LeaveChat(BaseClient): - def leave_chat(self, - chat_id: Union[int, str], - delete: bool = False): + def leave_chat( + self, + chat_id: Union[int, str], + delete: bool = False + ): """Use this method to leave a group chat or channel. Args: @@ -37,7 +39,7 @@ class LeaveChat(BaseClient): Deletes the group chat dialog after leaving (for simple group chats, not supergroups). Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ peer = self.resolve_peer(chat_id) diff --git a/pyrogram/client/methods/chats/pin_chat_message.py b/pyrogram/client/methods/chats/pin_chat_message.py index bb70f9e8..1d5466ba 100644 --- a/pyrogram/client/methods/chats/pin_chat_message.py +++ b/pyrogram/client/methods/chats/pin_chat_message.py @@ -23,10 +23,12 @@ from ...ext import BaseClient class PinChatMessage(BaseClient): - def pin_chat_message(self, - chat_id: Union[int, str], - message_id: int, - disable_notification: bool = None) -> bool: + def pin_chat_message( + self, + chat_id: Union[int, str], + message_id: int, + disable_notification: bool = None + ) -> bool: """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. @@ -46,7 +48,7 @@ class PinChatMessage(BaseClient): True on success. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ self.send( functions.messages.UpdatePinnedMessage( @@ -55,3 +57,5 @@ class PinChatMessage(BaseClient): silent=disable_notification or None ) ) + + return True diff --git a/pyrogram/client/methods/chats/promote_chat_member.py b/pyrogram/client/methods/chats/promote_chat_member.py index 9f2260f0..26d49516 100644 --- a/pyrogram/client/methods/chats/promote_chat_member.py +++ b/pyrogram/client/methods/chats/promote_chat_member.py @@ -23,18 +23,21 @@ from ...ext import BaseClient class PromoteChatMember(BaseClient): - def promote_chat_member(self, - chat_id: Union[int, str], - user_id: Union[int, str], - can_change_info: bool = True, - can_post_messages: bool = False, - can_edit_messages: bool = False, - can_delete_messages: bool = True, - can_invite_users: bool = True, - can_restrict_members: bool = True, - can_pin_messages: bool = False, - can_promote_members: bool = False) -> bool: + def promote_chat_member( + self, + chat_id: Union[int, str], + user_id: Union[int, str], + can_change_info: bool = True, + can_post_messages: bool = False, + can_edit_messages: bool = False, + can_delete_messages: bool = True, + can_restrict_members: bool = True, + can_invite_users: bool = True, + can_pin_messages: bool = False, + can_promote_members: bool = False + ) -> bool: """Use this method to promote or demote a user in a supergroup or a channel. + You must be an administrator in the chat for this to work and must have the appropriate admin rights. Pass False for all boolean parameters to demote a user. @@ -58,12 +61,12 @@ class PromoteChatMember(BaseClient): can_delete_messages (``bool``, *optional*): Pass True, if the administrator can delete messages of other users. - can_invite_users (``bool``, *optional*): - Pass True, if the administrator can invite new users to the chat. - can_restrict_members (``bool``, *optional*): Pass True, if the administrator can restrict, ban or unban chat members. + can_invite_users (``bool``, *optional*): + Pass True, if the administrator can invite new users to the chat. + can_pin_messages (``bool``, *optional*): Pass True, if the administrator can pin messages, supergroups only. @@ -76,23 +79,21 @@ class PromoteChatMember(BaseClient): True on success. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ self.send( functions.channels.EditAdmin( channel=self.resolve_peer(chat_id), user_id=self.resolve_peer(user_id), - admin_rights=types.ChannelAdminRights( + admin_rights=types.ChatAdminRights( change_info=can_change_info or None, post_messages=can_post_messages or None, edit_messages=can_edit_messages or None, delete_messages=can_delete_messages or None, ban_users=can_restrict_members or None, invite_users=can_invite_users or None, - invite_link=can_invite_users or None, pin_messages=can_pin_messages or None, add_admins=can_promote_members or None, - manage_call=None ) ) ) diff --git a/pyrogram/client/methods/chats/restrict_chat.py b/pyrogram/client/methods/chats/restrict_chat.py new file mode 100644 index 00000000..40d46d34 --- /dev/null +++ b/pyrogram/client/methods/chats/restrict_chat.py @@ -0,0 +1,143 @@ +# 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 Union + +from pyrogram.api import functions, types +from ...ext import BaseClient +from ...types.user_and_chats import Chat + + +class RestrictChat(BaseClient): + def restrict_chat( + self, + chat_id: Union[int, str], + can_send_messages: bool = False, + can_send_media_messages: bool = False, + can_send_other_messages: bool = False, + can_add_web_page_previews: bool = False, + can_send_polls: bool = False, + can_change_info: bool = False, + can_invite_users: bool = False, + can_pin_messages: bool = False + ) -> Chat: + """Use this method to restrict a chat. + Pass True for all boolean parameters to lift restrictions from a chat. + + Args: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + + can_send_messages (``bool``, *optional*): + Pass True, if the user can send text messages, contacts, locations and venues. + + can_send_media_messages (``bool``, *optional*): + Pass True, if the user can send audios, documents, photos, videos, video notes and voice notes, + implies can_send_messages. + + can_send_other_messages (``bool``, *optional*): + Pass True, if the user can send animations, games, stickers and use inline bots, + implies can_send_media_messages. + + can_add_web_page_previews (``bool``, *optional*): + Pass True, if the user may add web page previews to their messages, implies can_send_media_messages. + + can_send_polls (``bool``, *optional*): + Pass True, if the user can send polls, implies can_send_media_messages. + + can_change_info (``bool``, *optional*): + Pass True, if the user can change the chat title, photo and other settings. + + can_invite_users (``bool``, *optional*): + Pass True, if the user can invite new users to the chat. + + can_pin_messages (``bool``, *optional*): + Pass True, if the user can pin messages. + + Returns: + On success, a :obj:`Chat ` object is returned. + + Raises: + :class:`RPCError ` in case of a Telegram RPC error. + """ + send_messages = True + send_media = True + send_stickers = True + send_gifs = True + send_games = True + send_inline = True + embed_links = True + send_polls = True + change_info = True + invite_users = True + pin_messages = True + + if can_send_messages: + send_messages = None + + if can_send_media_messages: + send_messages = None + send_media = None + + if can_send_other_messages: + send_messages = None + send_media = None + send_stickers = None + send_gifs = None + send_games = None + send_inline = None + + if can_add_web_page_previews: + send_messages = None + send_media = None + embed_links = None + + if can_send_polls: + send_messages = None + send_polls = None + + if can_change_info: + change_info = None + + if can_invite_users: + invite_users = None + + if can_pin_messages: + pin_messages = None + + r = self.send( + functions.messages.EditChatDefaultBannedRights( + peer=self.resolve_peer(chat_id), + banned_rights=types.ChatBannedRights( + until_date=0, + send_messages=send_messages, + send_media=send_media, + send_stickers=send_stickers, + send_gifs=send_gifs, + send_games=send_games, + send_inline=send_inline, + embed_links=embed_links, + send_polls=send_polls, + change_info=change_info, + invite_users=invite_users, + pin_messages=pin_messages + ) + ) + ) + + return Chat._parse_chat(self, r.chats[0]) diff --git a/pyrogram/client/methods/chats/restrict_chat_member.py b/pyrogram/client/methods/chats/restrict_chat_member.py index dbe0054b..8688ecca 100644 --- a/pyrogram/client/methods/chats/restrict_chat_member.py +++ b/pyrogram/client/methods/chats/restrict_chat_member.py @@ -20,20 +20,28 @@ from typing import Union from pyrogram.api import functions, types from ...ext import BaseClient +from ...types.user_and_chats import Chat class RestrictChatMember(BaseClient): - def restrict_chat_member(self, - chat_id: Union[int, str], - user_id: Union[int, str], - until_date: int = 0, - can_send_messages: bool = False, - can_send_media_messages: bool = False, - can_send_other_messages: bool = False, - can_add_web_page_previews: bool = False) -> bool: - """Use this method to restrict a user in a supergroup. The bot must be an administrator in the supergroup for - this to work and must have the appropriate admin rights. Pass True for all boolean parameters to lift - restrictions from a user. + def restrict_chat_member( + self, + chat_id: Union[int, str], + user_id: Union[int, str], + until_date: int = 0, + can_send_messages: bool = False, + can_send_media_messages: bool = False, + can_send_other_messages: bool = False, + can_add_web_page_previews: bool = False, + can_send_polls: bool = False, + can_change_info: bool = False, + can_invite_users: bool = False, + can_pin_messages: bool = False + ) -> Chat: + """Use this method to restrict a user in a supergroup. + + The bot must be an administrator in the supergroup for this to work and must have the appropriate admin rights. + Pass True for all boolean parameters to lift restrictions from a user. Args: chat_id (``int`` | ``str``): @@ -60,13 +68,25 @@ class RestrictChatMember(BaseClient): implies can_send_media_messages. can_add_web_page_previews (``bool``, *optional*): - Pass True, if the user may add web page previews to their messages, implies can_send_media_messages + Pass True, if the user may add web page previews to their messages, implies can_send_media_messages. + + can_send_polls (``bool``, *optional*): + Pass True, if the user can send polls, implies can_send_media_messages. + + can_change_info (``bool``, *optional*): + Pass True, if the user can change the chat title, photo and other settings. + + can_invite_users (``bool``, *optional*): + Pass True, if the user can invite new users to the chat. + + can_pin_messages (``bool``, *optional*): + Pass True, if the user can pin messages. Returns: - True on success. + On success, a :obj:`Chat ` object is returned. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ send_messages = True send_media = True @@ -75,6 +95,10 @@ class RestrictChatMember(BaseClient): send_games = True send_inline = True embed_links = True + send_polls = True + change_info = True + invite_users = True + pin_messages = True if can_send_messages: send_messages = None @@ -84,6 +108,7 @@ class RestrictChatMember(BaseClient): send_media = None if can_send_other_messages: + send_messages = None send_media = None send_stickers = None send_gifs = None @@ -91,14 +116,28 @@ class RestrictChatMember(BaseClient): send_inline = None if can_add_web_page_previews: + send_messages = None send_media = None embed_links = None - self.send( + if can_send_polls: + send_messages = None + send_polls = None + + if can_change_info: + change_info = None + + if can_invite_users: + invite_users = None + + if can_pin_messages: + pin_messages = None + + r = self.send( functions.channels.EditBanned( channel=self.resolve_peer(chat_id), user_id=self.resolve_peer(user_id), - banned_rights=types.ChannelBannedRights( + banned_rights=types.ChatBannedRights( until_date=until_date, send_messages=send_messages, send_media=send_media, @@ -106,9 +145,13 @@ class RestrictChatMember(BaseClient): send_gifs=send_gifs, send_games=send_games, send_inline=send_inline, - embed_links=embed_links + embed_links=embed_links, + send_polls=send_polls, + change_info=change_info, + invite_users=invite_users, + pin_messages=pin_messages ) ) ) - return True + return Chat._parse_chat(self, r.chats[0]) diff --git a/pyrogram/client/methods/chats/set_chat_description.py b/pyrogram/client/methods/chats/set_chat_description.py index e6c5bba1..9d4e130b 100644 --- a/pyrogram/client/methods/chats/set_chat_description.py +++ b/pyrogram/client/methods/chats/set_chat_description.py @@ -23,9 +23,11 @@ from ...ext import BaseClient class SetChatDescription(BaseClient): - def set_chat_description(self, - chat_id: Union[int, str], - description: str) -> bool: + def set_chat_description( + self, + chat_id: Union[int, str], + description: str + ) -> bool: """Use this method to change the description of a supergroup or a channel. You must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -40,20 +42,18 @@ class SetChatDescription(BaseClient): True on success. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. ``ValueError`` if a chat_id doesn't belong to a supergroup or a channel. """ peer = self.resolve_peer(chat_id) - if isinstance(peer, types.InputPeerChannel): + if isinstance(peer, (types.InputPeerChannel, types.InputPeerChat)): self.send( - functions.channels.EditAbout( - channel=peer, + functions.messages.EditChatAbout( + peer=peer, about=description ) ) - 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)) diff --git a/pyrogram/client/methods/chats/set_chat_photo.py b/pyrogram/client/methods/chats/set_chat_photo.py index cc5c776a..87fe1b72 100644 --- a/pyrogram/client/methods/chats/set_chat_photo.py +++ b/pyrogram/client/methods/chats/set_chat_photo.py @@ -26,9 +26,11 @@ from ...ext import BaseClient class SetChatPhoto(BaseClient): - def set_chat_photo(self, - chat_id: Union[int, str], - photo: str) -> bool: + def set_chat_photo( + self, + chat_id: Union[int, str], + photo: str + ) -> bool: """Use this method to set a new profile photo for the chat. Photos can't be changed for private chats. You must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -48,7 +50,7 @@ class SetChatPhoto(BaseClient): True on success. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. ``ValueError`` if a chat_id belongs to user. """ peer = self.resolve_peer(chat_id) diff --git a/pyrogram/client/methods/chats/set_chat_title.py b/pyrogram/client/methods/chats/set_chat_title.py index fff330ee..e94f16a8 100644 --- a/pyrogram/client/methods/chats/set_chat_title.py +++ b/pyrogram/client/methods/chats/set_chat_title.py @@ -23,9 +23,11 @@ from ...ext import BaseClient class SetChatTitle(BaseClient): - def set_chat_title(self, - chat_id: Union[int, str], - title: str) -> bool: + def set_chat_title( + self, + chat_id: Union[int, str], + title: str + ) -> bool: """Use this method to change the title of a chat. Titles can't be changed for private chats. You must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -45,7 +47,7 @@ class SetChatTitle(BaseClient): True on success. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. ``ValueError`` if a chat_id belongs to user. """ peer = self.resolve_peer(chat_id) diff --git a/pyrogram/client/methods/chats/unban_chat_member.py b/pyrogram/client/methods/chats/unban_chat_member.py index da706a1f..0576c028 100644 --- a/pyrogram/client/methods/chats/unban_chat_member.py +++ b/pyrogram/client/methods/chats/unban_chat_member.py @@ -23,9 +23,11 @@ from ...ext import BaseClient class UnbanChatMember(BaseClient): - def unban_chat_member(self, - chat_id: Union[int, str], - user_id: Union[int, str]) -> bool: + def unban_chat_member( + self, + chat_id: Union[int, str], + user_id: Union[int, str] + ) -> bool: """Use this method to unban a previously kicked user in a supergroup or channel. The user will **not** return to the group or channel automatically, but will be able to join via link, etc. You must be an administrator for this to work. @@ -42,13 +44,13 @@ class UnbanChatMember(BaseClient): True on success. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ self.send( functions.channels.EditBanned( channel=self.resolve_peer(chat_id), user_id=self.resolve_peer(user_id), - banned_rights=types.ChannelBannedRights( + banned_rights=types.ChatBannedRights( until_date=0 ) ) diff --git a/pyrogram/client/methods/chats/unpin_chat_message.py b/pyrogram/client/methods/chats/unpin_chat_message.py index 4355010d..9753d656 100644 --- a/pyrogram/client/methods/chats/unpin_chat_message.py +++ b/pyrogram/client/methods/chats/unpin_chat_message.py @@ -23,8 +23,10 @@ from ...ext import BaseClient class UnpinChatMessage(BaseClient): - def unpin_chat_message(self, - chat_id: Union[int, str]) -> bool: + def unpin_chat_message( + self, + chat_id: Union[int, str] + ) -> bool: """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. @@ -37,7 +39,7 @@ class UnpinChatMessage(BaseClient): True on success. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ self.send( functions.messages.UpdatePinnedMessage( diff --git a/pyrogram/client/methods/chats/update_chat_username.py b/pyrogram/client/methods/chats/update_chat_username.py new file mode 100644 index 00000000..39cdfaeb --- /dev/null +++ b/pyrogram/client/methods/chats/update_chat_username.py @@ -0,0 +1,61 @@ +# 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 Union + +from pyrogram.api import functions, types +from ...ext import BaseClient + + +class UpdateChatUsername(BaseClient): + def update_chat_username( + self, + chat_id: Union[int, str], + username: Union[str, None] + ) -> bool: + """Use this method to update a channel or a supergroup username. + + To update your own username (for users only, not bots) you can use :meth:`update_username`. + + Args: + chat_id (``int`` | ``str``) + Unique identifier (int) or username (str) of the target chat. + username (``str`` | ``None``): + Username to set. Pass "" (empty string) or None to remove the username. + + Returns: + True on success. + + Raises: + :class:`RPCError ` in case of a Telegram RPC error. + ``ValueError`` if a chat_id belongs to a user or chat. + """ + + peer = self.resolve_peer(chat_id) + + if isinstance(peer, types.InputPeerChannel): + return bool( + self.send( + functions.channels.UpdateUsername( + channel=peer, + username=username or "" + ) + ) + ) + else: + raise ValueError("The chat_id \"{}\" belongs to a user or chat".format(chat_id)) diff --git a/pyrogram/client/methods/contacts/add_contacts.py b/pyrogram/client/methods/contacts/add_contacts.py index 48575a78..d1a97c99 100644 --- a/pyrogram/client/methods/contacts/add_contacts.py +++ b/pyrogram/client/methods/contacts/add_contacts.py @@ -24,8 +24,10 @@ from ...ext import BaseClient class AddContacts(BaseClient): - def add_contacts(self, - contacts: List["pyrogram.InputPhoneContact"]): + def add_contacts( + self, + contacts: List["pyrogram.InputPhoneContact"] + ): """Use this method to add contacts to your Telegram address book. Args: @@ -36,7 +38,7 @@ class AddContacts(BaseClient): On success, the added contacts are returned. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ imported_contacts = self.send( functions.contacts.ImportContacts( diff --git a/pyrogram/client/methods/contacts/delete_contacts.py b/pyrogram/client/methods/contacts/delete_contacts.py index dba2581d..af8f453e 100644 --- a/pyrogram/client/methods/contacts/delete_contacts.py +++ b/pyrogram/client/methods/contacts/delete_contacts.py @@ -19,14 +19,16 @@ from typing import List from pyrogram.api import functions, types -from pyrogram.api.errors import PeerIdInvalid +from pyrogram.errors import PeerIdInvalid from ...ext import BaseClient class DeleteContacts(BaseClient): - def delete_contacts(self, - ids: List[int]): - """Use this method to delete contacts from your Telegram address book + def delete_contacts( + self, + ids: List[int] + ): + """Use this method to delete contacts from your Telegram address book. Args: ids (List of ``int``): @@ -37,7 +39,7 @@ class DeleteContacts(BaseClient): True on success. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ contacts = [] diff --git a/pyrogram/client/methods/contacts/get_contacts.py b/pyrogram/client/methods/contacts/get_contacts.py index 12419106..7eaf6906 100644 --- a/pyrogram/client/methods/contacts/get_contacts.py +++ b/pyrogram/client/methods/contacts/get_contacts.py @@ -21,7 +21,7 @@ import time import pyrogram from pyrogram.api import functions -from pyrogram.api.errors import FloodWait +from pyrogram.errors import FloodWait from ...ext import BaseClient log = logging.getLogger(__name__) @@ -35,11 +35,11 @@ class GetContacts(BaseClient): On success, a list of :obj:`User` objects is returned. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ while True: try: - contacts = self.send(functions.contacts.GetContacts(0)) + contacts = self.send(functions.contacts.GetContacts(hash=0)) except FloodWait as e: log.warning("get_contacts flood: waiting {} seconds".format(e.x)) time.sleep(e.x) diff --git a/pyrogram/client/methods/decorators/__init__.py b/pyrogram/client/methods/decorators/__init__.py index 07f84b54..33f55a3d 100644 --- a/pyrogram/client/methods/decorators/__init__.py +++ b/pyrogram/client/methods/decorators/__init__.py @@ -19,6 +19,7 @@ from .on_callback_query import OnCallbackQuery from .on_deleted_messages import OnDeletedMessages from .on_disconnect import OnDisconnect +from .on_inline_query import OnInlineQuery from .on_message import OnMessage from .on_raw_update import OnRawUpdate from .on_user_status import OnUserStatus @@ -30,6 +31,7 @@ class Decorators( OnCallbackQuery, OnRawUpdate, OnDisconnect, - OnUserStatus + OnUserStatus, + OnInlineQuery ): pass diff --git a/pyrogram/client/methods/decorators/on_callback_query.py b/pyrogram/client/methods/decorators/on_callback_query.py index bf0a823a..3c747c5f 100644 --- a/pyrogram/client/methods/decorators/on_callback_query.py +++ b/pyrogram/client/methods/decorators/on_callback_query.py @@ -25,19 +25,13 @@ from ...ext import BaseClient class OnCallbackQuery(BaseClient): - def on_callback_query(self=None, - filters=None, - group: int = 0) -> callable: - """Use this decorator to automatically register a function for handling - callback queries. This does the same thing as :meth:`add_handler` using the - :class:`CallbackQueryHandler`. - - .. note:: - This decorator will wrap your defined function in a tuple consisting of *(Handler, group)*. - - To reference your own function after it has been decorated, you need to access - *my_function[0].callback*, that is, the *callback* field of Handler object which is the the - first element in the tuple. + def on_callback_query( + self=None, + filters=None, + group: int = 0 + ) -> callable: + """Use this decorator to automatically register a function for handling callback queries. + This does the same thing as :meth:`add_handler` using the :class:`CallbackQueryHandler`. Args: filters (:obj:`Filters `): diff --git a/pyrogram/client/methods/decorators/on_deleted_messages.py b/pyrogram/client/methods/decorators/on_deleted_messages.py index 1b1a602b..cf8f9cf2 100644 --- a/pyrogram/client/methods/decorators/on_deleted_messages.py +++ b/pyrogram/client/methods/decorators/on_deleted_messages.py @@ -25,19 +25,13 @@ from ...ext import BaseClient class OnDeletedMessages(BaseClient): - def on_deleted_messages(self=None, - filters=None, - group: int = 0) -> callable: - """Use this decorator to automatically register a function for handling - deleted messages. This does the same thing as :meth:`add_handler` using the - :class:`DeletedMessagesHandler`. - - .. note:: - This decorator will wrap your defined function in a tuple consisting of *(Handler, group)*. - - To reference your own function after it has been decorated, you need to access - *my_function[0].callback*, that is, the *callback* field of Handler object which is the the - first element in the tuple. + def on_deleted_messages( + self=None, + filters=None, + group: int = 0 + ) -> callable: + """Use this decorator to automatically register a function for handling deleted messages. + This does the same thing as :meth:`add_handler` using the :class:`DeletedMessagesHandler`. Args: filters (:obj:`Filters `): diff --git a/pyrogram/client/methods/decorators/on_disconnect.py b/pyrogram/client/methods/decorators/on_disconnect.py index 4657af3b..515a28c1 100644 --- a/pyrogram/client/methods/decorators/on_disconnect.py +++ b/pyrogram/client/methods/decorators/on_disconnect.py @@ -23,9 +23,8 @@ from ...ext import BaseClient class OnDisconnect(BaseClient): def on_disconnect(self=None) -> callable: - """Use this decorator to automatically register a function for handling - disconnections. This does the same thing as :meth:`add_handler` using the - :class:`DisconnectHandler`. + """Use this decorator to automatically register a function for handling disconnections. + This does the same thing as :meth:`add_handler` using the :class:`DisconnectHandler`. """ def decorator(func: callable) -> Handler: diff --git a/pyrogram/client/methods/decorators/on_inline_query.py b/pyrogram/client/methods/decorators/on_inline_query.py new file mode 100644 index 00000000..81f0f676 --- /dev/null +++ b/pyrogram/client/methods/decorators/on_inline_query.py @@ -0,0 +1,59 @@ +# 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 Tuple + +import pyrogram +from pyrogram.client.filters.filter import Filter +from pyrogram.client.handlers.handler import Handler +from ...ext import BaseClient + + +class OnInlineQuery(BaseClient): + def on_inline_query( + self=None, + filters=None, + group: int = 0 + ) -> callable: + """Use this decorator to automatically register a function for handling inline queries. + This does the same thing as :meth:`add_handler` using the :class:`InlineQueryHandler`. + + Args: + filters (:obj:`Filters `): + Pass one or more filters to allow only a subset of inline queries to be passed + in your function. + + group (``int``, *optional*): + The group identifier, defaults to 0. + """ + + def decorator(func: callable) -> Tuple[Handler, int]: + if isinstance(func, tuple): + func = func[0].callback + + handler = pyrogram.InlineQueryHandler(func, filters) + + if isinstance(self, Filter): + return pyrogram.InlineQueryHandler(func, self), group if filters is None else filters + + if self is not None: + self.add_handler(handler, group) + + return handler, group + + return decorator diff --git a/pyrogram/client/methods/decorators/on_message.py b/pyrogram/client/methods/decorators/on_message.py index c2f35a19..e6563893 100644 --- a/pyrogram/client/methods/decorators/on_message.py +++ b/pyrogram/client/methods/decorators/on_message.py @@ -25,19 +25,13 @@ from ...ext import BaseClient class OnMessage(BaseClient): - def on_message(self=None, - filters=None, - group: int = 0) -> callable: - """Use this decorator to automatically register a function for handling - messages. This does the same thing as :meth:`add_handler` using the - :class:`MessageHandler`. - - .. note:: - This decorator will wrap your defined function in a tuple consisting of *(Handler, group)*. - - To reference your own function after it has been decorated, you need to access - *my_function[0].callback*, that is, the *callback* field of Handler object which is the the - first element in the tuple. + def on_message( + self=None, + filters=None, + group: int = 0 + ) -> callable: + """Use this decorator to automatically register a function for handling messages. + This does the same thing as :meth:`add_handler` using the :class:`MessageHandler`. Args: filters (:obj:`Filters `): diff --git a/pyrogram/client/methods/decorators/on_raw_update.py b/pyrogram/client/methods/decorators/on_raw_update.py index 2deedb00..1494a319 100644 --- a/pyrogram/client/methods/decorators/on_raw_update.py +++ b/pyrogram/client/methods/decorators/on_raw_update.py @@ -24,18 +24,12 @@ from ...ext import BaseClient class OnRawUpdate(BaseClient): - def on_raw_update(self=None, - group: int = 0) -> callable: - """Use this decorator to automatically register a function for handling - raw updates. This does the same thing as :meth:`add_handler` using the - :class:`RawUpdateHandler`. - - .. note:: - This decorator will wrap your defined function in a tuple consisting of *(Handler, group)*. - - To reference your own function after it has been decorated, you need to access - *my_function[0].callback*, that is, the *callback* field of Handler object which is the the - first element in the tuple. + def on_raw_update( + self=None, + group: int = 0 + ) -> callable: + """Use this decorator to automatically register a function for handling raw updates. + This does the same thing as :meth:`add_handler` using the :class:`RawUpdateHandler`. Args: group (``int``, *optional*): diff --git a/pyrogram/client/methods/decorators/on_user_status.py b/pyrogram/client/methods/decorators/on_user_status.py index 6198c9cd..4d8185b1 100644 --- a/pyrogram/client/methods/decorators/on_user_status.py +++ b/pyrogram/client/methods/decorators/on_user_status.py @@ -25,19 +25,13 @@ from ...ext import BaseClient class OnUserStatus(BaseClient): - def on_user_status(self=None, - filters=None, - group: int = 0) -> callable: - """Use this decorator to automatically register a function for handling - user status updates. This does the same thing as :meth:`add_handler` using the - :class:`UserStatusHandler`. - - .. note:: - This decorator will wrap your defined function in a tuple consisting of *(Handler, group)*. - - To reference your own function after it has been decorated, you need to access - *my_function[0].callback*, that is, the *callback* field of Handler object which is the the - first element in the tuple. + def on_user_status( + self=None, + filters=None, + group: int = 0 + ) -> callable: + """Use this decorator to automatically register a function for handling user status updates. + This does the same thing as :meth:`add_handler` using the :class:`UserStatusHandler`. Args: filters (:obj:`Filters `): diff --git a/pyrogram/client/methods/messages/__init__.py b/pyrogram/client/methods/messages/__init__.py index f76d0a22..dde50b7b 100644 --- a/pyrogram/client/methods/messages/__init__.py +++ b/pyrogram/client/methods/messages/__init__.py @@ -30,6 +30,7 @@ from .iter_history import IterHistory from .retract_vote import RetractVote from .send_animation import SendAnimation from .send_audio import SendAudio +from .send_cached_media import SendCachedMedia from .send_chat_action import SendChatAction from .send_contact import SendContact from .send_document import SendDocument @@ -74,6 +75,7 @@ class Messages( ClosePoll, RetractVote, DownloadMedia, - IterHistory + IterHistory, + SendCachedMedia ): pass diff --git a/pyrogram/client/methods/messages/close_poll.py b/pyrogram/client/methods/messages/close_poll.py index c2d2706b..1b1164c2 100644 --- a/pyrogram/client/methods/messages/close_poll.py +++ b/pyrogram/client/methods/messages/close_poll.py @@ -23,9 +23,11 @@ from pyrogram.client.ext import BaseClient class ClosePoll(BaseClient): - def close_poll(self, - chat_id: Union[int, str], - message_id: id) -> bool: + def close_poll( + self, + chat_id: Union[int, str], + message_id: id + ) -> bool: """Use this method to close (stop) a poll. Closed polls can't be reopened and nobody will be able to vote in it anymore. @@ -43,7 +45,7 @@ class ClosePoll(BaseClient): On success, True is returned. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ poll = self.get_messages(chat_id, message_id).poll diff --git a/pyrogram/client/methods/messages/delete_messages.py b/pyrogram/client/methods/messages/delete_messages.py index 06acbb1f..bbd838ee 100644 --- a/pyrogram/client/methods/messages/delete_messages.py +++ b/pyrogram/client/methods/messages/delete_messages.py @@ -23,17 +23,13 @@ from pyrogram.client.ext import BaseClient class DeleteMessages(BaseClient): - def delete_messages(self, - chat_id: Union[int, str], - message_ids: Iterable[int], - revoke: bool = True) -> bool: - """Use this method to delete messages, including service messages, with the following limitations: - - - A message can only be deleted if it was sent less than 48 hours ago. - - Users can delete outgoing messages in groups and supergroups. - - Users granted *can_post_messages* permissions can delete outgoing messages in channels. - - If the user is an administrator of a group, it can delete any message there. - - If the user has *can_delete_messages* permission in a supergroup or a channel, it can delete any message there. + def delete_messages( + self, + chat_id: Union[int, str], + message_ids: Iterable[int], + revoke: bool = True + ) -> bool: + """Use this method to delete messages, including service messages. Args: chat_id (``int`` | ``str``): @@ -55,7 +51,7 @@ class DeleteMessages(BaseClient): True on success. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ peer = self.resolve_peer(chat_id) message_ids = list(message_ids) if not isinstance(message_ids, int) else [message_ids] diff --git a/pyrogram/client/methods/messages/download_media.py b/pyrogram/client/methods/messages/download_media.py index 181daa14..35959d4a 100644 --- a/pyrogram/client/methods/messages/download_media.py +++ b/pyrogram/client/methods/messages/download_media.py @@ -24,13 +24,15 @@ from pyrogram.client.ext import BaseClient class DownloadMedia(BaseClient): - def download_media(self, - message: Union["pyrogram.Message", str], - file_name: str = "", - block: bool = True, - progress: callable = None, - progress_args: tuple = ()) -> Union[str, None]: - """Use this method to download the media from a Message. + def download_media( + self, + message: Union["pyrogram.Message", str], + file_name: str = "", + block: bool = True, + progress: callable = None, + progress_args: tuple = () + ) -> Union[str, None]: + """Use this method to download the media from a message. Args: message (:obj:`Message ` | ``str``): @@ -75,7 +77,7 @@ class DownloadMedia(BaseClient): In case the download is deliberately stopped with :meth:`stop_transmission`, None is returned as well. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. ``ValueError`` if the message doesn't contain any downloadable media """ error_message = "This message doesn't contain any downloadable media" @@ -106,15 +108,15 @@ class DownloadMedia(BaseClient): else: raise ValueError(error_message) elif isinstance(message, ( - pyrogram.Photo, - pyrogram.PhotoSize, - pyrogram.Audio, - pyrogram.Document, - pyrogram.Video, - pyrogram.Voice, - pyrogram.VideoNote, - pyrogram.Sticker, - pyrogram.Animation + pyrogram.Photo, + pyrogram.PhotoSize, + pyrogram.Audio, + pyrogram.Document, + pyrogram.Video, + pyrogram.Voice, + pyrogram.VideoNote, + pyrogram.Sticker, + pyrogram.Animation )): if isinstance(message, pyrogram.Photo): media = pyrogram.Document( diff --git a/pyrogram/client/methods/messages/edit_message_caption.py b/pyrogram/client/methods/messages/edit_message_caption.py index fe704e41..c7bcbd70 100644 --- a/pyrogram/client/methods/messages/edit_message_caption.py +++ b/pyrogram/client/methods/messages/edit_message_caption.py @@ -24,12 +24,14 @@ from pyrogram.client.ext import BaseClient class EditMessageCaption(BaseClient): - def edit_message_caption(self, - chat_id: Union[int, str], - message_id: int, - caption: str, - parse_mode: str = "", - reply_markup: "pyrogram.InlineKeyboardMarkup" = None) -> "pyrogram.Message": + def edit_message_caption( + self, + chat_id: Union[int, str], + message_id: int, + caption: str, + parse_mode: str = "", + reply_markup: "pyrogram.InlineKeyboardMarkup" = None + ) -> "pyrogram.Message": """Use this method to edit captions of messages. Args: @@ -56,7 +58,7 @@ class EditMessageCaption(BaseClient): On success, the edited :obj:`Message ` is returned. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ style = self.html if parse_mode.lower() == "html" else self.markdown diff --git a/pyrogram/client/methods/messages/edit_message_media.py b/pyrogram/client/methods/messages/edit_message_media.py index 9ad2f199..ea5870fc 100644 --- a/pyrogram/client/methods/messages/edit_message_media.py +++ b/pyrogram/client/methods/messages/edit_message_media.py @@ -23,7 +23,7 @@ from typing import Union import pyrogram from pyrogram.api import functions, types -from pyrogram.api.errors import FileIdInvalid +from pyrogram.errors import FileIdInvalid from pyrogram.client.ext import BaseClient, utils from pyrogram.client.types import ( InputMediaPhoto, InputMediaVideo, InputMediaAudio, @@ -33,11 +33,13 @@ from pyrogram.client.types.input_media import InputMedia class EditMessageMedia(BaseClient): - def edit_message_media(self, - chat_id: Union[int, str], - message_id: int, - media: InputMedia, - reply_markup: "pyrogram.InlineKeyboardMarkup" = None) -> "pyrogram.Message": + def edit_message_media( + self, + chat_id: Union[int, str], + message_id: int, + media: InputMedia, + reply_markup: "pyrogram.InlineKeyboardMarkup" = None + ) -> "pyrogram.Message": """Use this method to edit audio, document, photo, or video messages. If a message is a part of a message album, then it can be edited only to a photo or a video. Otherwise, @@ -54,7 +56,7 @@ class EditMessageMedia(BaseClient): message_id (``int``): Message identifier in the chat specified in chat_id. - media (:obj:`InputMediaAnimation` | :obj:`InputMediaAudio` | :obj:`InputMediaDocument` | :obj:`InputMediaPhoto` | :obj:`InputMediaVideo`) + media (:obj:`InputMedia`) One of the InputMedia objects describing an animation, audio, document, photo or video. reply_markup (:obj:`InlineKeyboardMarkup`, *optional*): @@ -64,7 +66,7 @@ class EditMessageMedia(BaseClient): On success, the edited :obj:`Message ` is returned. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ style = self.html if media.parse_mode.lower() == "html" else self.markdown caption = media.caption @@ -131,7 +133,9 @@ class EditMessageMedia(BaseClient): w=media.width, h=media.height ), - types.DocumentAttributeFilename(os.path.basename(media.media)) + types.DocumentAttributeFilename( + file_name=os.path.basename(media.media) + ) ] ) ) @@ -187,7 +191,9 @@ class EditMessageMedia(BaseClient): performer=media.performer, title=media.title ), - types.DocumentAttributeFilename(os.path.basename(media.media)) + types.DocumentAttributeFilename( + file_name=os.path.basename(media.media) + ) ] ) ) @@ -244,7 +250,9 @@ class EditMessageMedia(BaseClient): w=media.width, h=media.height ), - types.DocumentAttributeFilename(os.path.basename(media.media)), + types.DocumentAttributeFilename( + file_name=os.path.basename(media.media) + ), types.DocumentAttributeAnimated() ] ) @@ -296,7 +304,9 @@ class EditMessageMedia(BaseClient): thumb=None if media.thumb is None else self.save_file(media.thumb), file=self.save_file(media.media), attributes=[ - types.DocumentAttributeFilename(os.path.basename(media.media)) + types.DocumentAttributeFilename( + file_name=os.path.basename(media.media) + ) ] ) ) diff --git a/pyrogram/client/methods/messages/edit_message_reply_markup.py b/pyrogram/client/methods/messages/edit_message_reply_markup.py index f8b5c0a5..e3495476 100644 --- a/pyrogram/client/methods/messages/edit_message_reply_markup.py +++ b/pyrogram/client/methods/messages/edit_message_reply_markup.py @@ -24,10 +24,12 @@ from pyrogram.client.ext import BaseClient class EditMessageReplyMarkup(BaseClient): - def edit_message_reply_markup(self, - chat_id: Union[int, str], - message_id: int, - reply_markup: "pyrogram.InlineKeyboardMarkup" = None) -> "pyrogram.Message": + def edit_message_reply_markup( + self, + chat_id: Union[int, str], + message_id: int, + reply_markup: "pyrogram.InlineKeyboardMarkup" = None + ) -> "pyrogram.Message": """Use this method to edit only the reply markup of messages sent by the bot or via the bot (for inline bots). Args: @@ -47,7 +49,7 @@ class EditMessageReplyMarkup(BaseClient): :obj:`Message ` is returned, otherwise True is returned. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ r = self.send( diff --git a/pyrogram/client/methods/messages/edit_message_text.py b/pyrogram/client/methods/messages/edit_message_text.py index 1d2a065b..8e23b1de 100644 --- a/pyrogram/client/methods/messages/edit_message_text.py +++ b/pyrogram/client/methods/messages/edit_message_text.py @@ -24,13 +24,15 @@ from pyrogram.client.ext import BaseClient class EditMessageText(BaseClient): - def edit_message_text(self, - chat_id: Union[int, str], - message_id: int, - text: str, - parse_mode: str = "", - disable_web_page_preview: bool = None, - reply_markup: "pyrogram.InlineKeyboardMarkup" = None) -> "pyrogram.Message": + def edit_message_text( + self, + chat_id: Union[int, str], + message_id: int, + text: str, + parse_mode: str = "", + disable_web_page_preview: bool = None, + reply_markup: "pyrogram.InlineKeyboardMarkup" = None + ) -> "pyrogram.Message": """Use this method to edit text messages. Args: @@ -60,7 +62,7 @@ class EditMessageText(BaseClient): On success, the edited :obj:`Message ` is returned. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ style = self.html if parse_mode.lower() == "html" else self.markdown diff --git a/pyrogram/client/methods/messages/forward_messages.py b/pyrogram/client/methods/messages/forward_messages.py index 72e43e08..5540b38a 100644 --- a/pyrogram/client/methods/messages/forward_messages.py +++ b/pyrogram/client/methods/messages/forward_messages.py @@ -24,11 +24,15 @@ from ...ext import BaseClient class ForwardMessages(BaseClient): - def forward_messages(self, - chat_id: Union[int, str], - from_chat_id: Union[int, str], - message_ids: Iterable[int], - disable_notification: bool = None) -> "pyrogram.Messages": + def forward_messages( + self, + chat_id: Union[int, str], + from_chat_id: Union[int, str], + message_ids: Iterable[int], + disable_notification: bool = None, + as_copy: bool = False, + remove_caption: bool = False + ) -> "pyrogram.Messages": """Use this method to forward messages of any kind. Args: @@ -50,6 +54,15 @@ class ForwardMessages(BaseClient): Sends the message silently. Users will receive a notification with no sound. + as_copy (``bool``, *optional*): + Pass True to forward messages without the forward header (i.e.: send a copy of the message content). + Defaults to False. + + remove_caption (``bool``, *optional*): + If set to True and *as_copy* is enabled as well, media captions are not preserved when copying the + message. Has no effect if *as_copy* is not enabled. + Defaults to False. + Returns: On success and in case *message_ids* was an iterable, the returned value will be a list of the forwarded :obj:`Messages ` even if a list contains just one element, otherwise if @@ -57,37 +70,60 @@ class ForwardMessages(BaseClient): is returned. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ + is_iterable = not isinstance(message_ids, int) message_ids = list(message_ids) if is_iterable else [message_ids] - r = self.send( - functions.messages.ForwardMessages( - to_peer=self.resolve_peer(chat_id), - from_peer=self.resolve_peer(from_chat_id), - id=message_ids, - silent=disable_notification or None, - random_id=[self.rnd_id() for _ in message_ids] - ) - ) + if as_copy: + forwarded_messages = [] - messages = [] + for chunk in [message_ids[i:i + 200] for i in range(0, len(message_ids), 200)]: + messages = self.get_messages(chat_id=from_chat_id, message_ids=chunk) # type: pyrogram.Messages - users = {i.id: i for i in r.users} - chats = {i.id: i for i in r.chats} - - for i in r.updates: - if isinstance(i, (types.UpdateNewMessage, types.UpdateNewChannelMessage)): - messages.append( - pyrogram.Message._parse( - self, i.message, - users, chats + for message in messages.messages: + forwarded_messages.append( + message.forward( + chat_id, + disable_notification=disable_notification, + as_copy=True, + remove_caption=remove_caption + ) ) - ) - return pyrogram.Messages( - client=self, - total_count=len(messages), - messages=messages - ) if is_iterable else messages[0] + return pyrogram.Messages( + client=self, + total_count=len(forwarded_messages), + messages=forwarded_messages + ) if is_iterable else forwarded_messages[0] + else: + r = self.send( + functions.messages.ForwardMessages( + to_peer=self.resolve_peer(chat_id), + from_peer=self.resolve_peer(from_chat_id), + id=message_ids, + silent=disable_notification or None, + random_id=[self.rnd_id() for _ in message_ids] + ) + ) + + forwarded_messages = [] + + users = {i.id: i for i in r.users} + chats = {i.id: i for i in r.chats} + + for i in r.updates: + if isinstance(i, (types.UpdateNewMessage, types.UpdateNewChannelMessage)): + forwarded_messages.append( + pyrogram.Message._parse( + self, i.message, + users, chats + ) + ) + + return pyrogram.Messages( + client=self, + total_count=len(forwarded_messages), + messages=forwarded_messages + ) if is_iterable else forwarded_messages[0] diff --git a/pyrogram/client/methods/messages/get_history.py b/pyrogram/client/methods/messages/get_history.py index 73923b44..fda8f11f 100644 --- a/pyrogram/client/methods/messages/get_history.py +++ b/pyrogram/client/methods/messages/get_history.py @@ -22,20 +22,22 @@ from typing import Union import pyrogram from pyrogram.api import functions -from pyrogram.api.errors import FloodWait +from pyrogram.errors import FloodWait from ...ext import BaseClient log = logging.getLogger(__name__) class GetHistory(BaseClient): - def get_history(self, - chat_id: Union[int, str], - limit: int = 100, - offset: int = 0, - offset_id: int = 0, - offset_date: int = 0, - reverse: bool = False): + def get_history( + self, + chat_id: Union[int, str], + limit: int = 100, + offset: int = 0, + offset_id: int = 0, + offset_date: int = 0, + reverse: bool = False + ): """Use this method to retrieve a chunk of the history of a chat. You can get up to 100 messages at once. @@ -68,7 +70,7 @@ class GetHistory(BaseClient): On success, a :obj:`Messages ` object is returned. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ while True: diff --git a/pyrogram/client/methods/messages/get_messages.py b/pyrogram/client/methods/messages/get_messages.py index b1c90d10..c018a9eb 100644 --- a/pyrogram/client/methods/messages/get_messages.py +++ b/pyrogram/client/methods/messages/get_messages.py @@ -22,18 +22,20 @@ from typing import Union, Iterable import pyrogram from pyrogram.api import functions, types -from pyrogram.api.errors import FloodWait +from pyrogram.errors import FloodWait from ...ext import BaseClient log = logging.getLogger(__name__) class GetMessages(BaseClient): - def get_messages(self, - chat_id: Union[int, str], - message_ids: Union[int, Iterable[int]] = None, - reply_to_message_ids: Union[int, Iterable[int]] = None, - replies: int = 1) -> Union["pyrogram.Message", "pyrogram.Messages"]: + def get_messages( + self, + chat_id: Union[int, str], + message_ids: Union[int, Iterable[int]] = None, + reply_to_message_ids: Union[int, Iterable[int]] = None, + replies: int = 1 + ) -> Union["pyrogram.Message", "pyrogram.Messages"]: """Use this method to get one or more messages that belong to a specific chat. You can retrieve up to 200 messages at once. @@ -61,7 +63,7 @@ class GetMessages(BaseClient): *reply_to_message_ids* was an integer, the single requested :obj:`Message ` is returned. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ ids, ids_type = ( (message_ids, types.InputMessageID) if message_ids @@ -76,7 +78,7 @@ class GetMessages(BaseClient): is_iterable = not isinstance(ids, int) ids = list(ids) if is_iterable else [ids] - ids = [ids_type(i) for i in ids] + ids = [ids_type(id=i) for i in ids] if isinstance(peer, types.InputPeerChannel): rpc = functions.channels.GetMessages(channel=peer, id=ids) diff --git a/pyrogram/client/methods/messages/iter_history.py b/pyrogram/client/methods/messages/iter_history.py index ab587988..f7a8a74e 100644 --- a/pyrogram/client/methods/messages/iter_history.py +++ b/pyrogram/client/methods/messages/iter_history.py @@ -23,13 +23,15 @@ from ...ext import BaseClient class IterHistory(BaseClient): - def iter_history(self, - chat_id: Union[int, str], - limit: int = 0, - offset: int = 0, - offset_id: int = 0, - offset_date: int = 0, - reverse: bool = False) -> Generator["pyrogram.Message", None, None]: + def iter_history( + self, + chat_id: Union[int, str], + limit: int = 0, + offset: int = 0, + offset_id: int = 0, + offset_date: int = 0, + reverse: bool = False + ) -> Generator["pyrogram.Message", None, None]: """Use this method to iterate through a chat history sequentially. This convenience method does the same as repeatedly calling :meth:`get_history` in a loop, thus saving you from @@ -62,7 +64,7 @@ class IterHistory(BaseClient): A generator yielding :obj:`Message ` objects. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ offset_id = offset_id or (1 if reverse else 0) current = 0 diff --git a/pyrogram/client/methods/messages/retract_vote.py b/pyrogram/client/methods/messages/retract_vote.py index 7893f768..8fa8996c 100644 --- a/pyrogram/client/methods/messages/retract_vote.py +++ b/pyrogram/client/methods/messages/retract_vote.py @@ -23,9 +23,11 @@ from pyrogram.client.ext import BaseClient class RetractVote(BaseClient): - def retract_vote(self, - chat_id: Union[int, str], - message_id: id) -> bool: + def retract_vote( + self, + chat_id: Union[int, str], + message_id: id + ) -> bool: """Use this method to retract your vote in a poll. Args: @@ -41,7 +43,7 @@ class RetractVote(BaseClient): On success, True is returned. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ self.send( functions.messages.SendVote( diff --git a/pyrogram/client/methods/messages/send_animation.py b/pyrogram/client/methods/messages/send_animation.py index 8fc31e08..798d236d 100644 --- a/pyrogram/client/methods/messages/send_animation.py +++ b/pyrogram/client/methods/messages/send_animation.py @@ -23,28 +23,32 @@ from typing import Union import pyrogram from pyrogram.api import functions, types -from pyrogram.api.errors import FileIdInvalid, FilePartMissing +from pyrogram.errors import FileIdInvalid, FilePartMissing from pyrogram.client.ext import BaseClient, utils class SendAnimation(BaseClient): - def send_animation(self, - chat_id: Union[int, str], - animation: str, - caption: str = "", - parse_mode: str = "", - duration: int = 0, - width: int = 0, - height: int = 0, - thumb: str = None, - disable_notification: bool = None, - reply_to_message_id: int = None, - reply_markup: Union["pyrogram.InlineKeyboardMarkup", - "pyrogram.ReplyKeyboardMarkup", - "pyrogram.ReplyKeyboardRemove", - "pyrogram.ForceReply"] = None, - progress: callable = None, - progress_args: tuple = ()) -> Union["pyrogram.Message", None]: + def send_animation( + self, + chat_id: Union[int, str], + animation: str, + caption: str = "", + parse_mode: str = "", + duration: int = 0, + width: int = 0, + height: int = 0, + thumb: str = None, + disable_notification: bool = None, + reply_to_message_id: int = None, + reply_markup: Union[ + "pyrogram.InlineKeyboardMarkup", + "pyrogram.ReplyKeyboardMarkup", + "pyrogram.ReplyKeyboardRemove", + "pyrogram.ForceReply" + ] = None, + progress: callable = None, + progress_args: tuple = () + ) -> Union["pyrogram.Message", None]: """Use this method to send animation files (animation or H.264/MPEG-4 AVC video without sound). Args: @@ -121,7 +125,7 @@ class SendAnimation(BaseClient): In case the upload is deliberately stopped with :meth:`stop_transmission`, None is returned instead. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ file = None style = self.html if parse_mode.lower() == "html" else self.markdown @@ -141,7 +145,7 @@ class SendAnimation(BaseClient): w=width, h=height ), - types.DocumentAttributeFilename(os.path.basename(animation)), + types.DocumentAttributeFilename(file_name=os.path.basename(animation)), types.DocumentAttributeAnimated() ] ) diff --git a/pyrogram/client/methods/messages/send_audio.py b/pyrogram/client/methods/messages/send_audio.py index a956ba85..d514b737 100644 --- a/pyrogram/client/methods/messages/send_audio.py +++ b/pyrogram/client/methods/messages/send_audio.py @@ -23,28 +23,32 @@ from typing import Union import pyrogram from pyrogram.api import functions, types -from pyrogram.api.errors import FileIdInvalid, FilePartMissing +from pyrogram.errors import FileIdInvalid, FilePartMissing from pyrogram.client.ext import BaseClient, utils class SendAudio(BaseClient): - def send_audio(self, - chat_id: Union[int, str], - audio: str, - caption: str = "", - parse_mode: str = "", - duration: int = 0, - performer: str = None, - title: str = None, - thumb: str = None, - disable_notification: bool = None, - reply_to_message_id: int = None, - reply_markup: Union["pyrogram.InlineKeyboardMarkup", - "pyrogram.ReplyKeyboardMarkup", - "pyrogram.ReplyKeyboardRemove", - "pyrogram.ForceReply"] = None, - progress: callable = None, - progress_args: tuple = ()) -> Union["pyrogram.Message", None]: + def send_audio( + self, + chat_id: Union[int, str], + audio: str, + caption: str = "", + parse_mode: str = "", + duration: int = 0, + performer: str = None, + title: str = None, + thumb: str = None, + disable_notification: bool = None, + reply_to_message_id: int = None, + reply_markup: Union[ + "pyrogram.InlineKeyboardMarkup", + "pyrogram.ReplyKeyboardMarkup", + "pyrogram.ReplyKeyboardRemove", + "pyrogram.ForceReply" + ] = None, + progress: callable = None, + progress_args: tuple = () + ) -> Union["pyrogram.Message", None]: """Use this method to send audio files. For sending voice messages, use the :obj:`send_voice()` method instead. @@ -123,7 +127,7 @@ class SendAudio(BaseClient): In case the upload is deliberately stopped with :meth:`stop_transmission`, None is returned instead. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ file = None style = self.html if parse_mode.lower() == "html" else self.markdown @@ -142,7 +146,7 @@ class SendAudio(BaseClient): performer=performer, title=title ), - types.DocumentAttributeFilename(os.path.basename(audio)) + types.DocumentAttributeFilename(file_name=os.path.basename(audio)) ] ) elif audio.startswith("http"): diff --git a/pyrogram/client/methods/messages/send_cached_media.py b/pyrogram/client/methods/messages/send_cached_media.py new file mode 100644 index 00000000..f0c690d9 --- /dev/null +++ b/pyrogram/client/methods/messages/send_cached_media.py @@ -0,0 +1,135 @@ +# 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 . + +import binascii +import struct +from typing import Union + +import pyrogram +from pyrogram.api import functions, types +from pyrogram.errors import FileIdInvalid +from pyrogram.client.ext import BaseClient, utils + + +class SendCachedMedia(BaseClient): + def send_cached_media( + self, + chat_id: Union[int, str], + file_id: str, + caption: str = "", + parse_mode: str = "", + disable_notification: bool = None, + reply_to_message_id: int = None, + reply_markup: Union[ + "pyrogram.InlineKeyboardMarkup", + "pyrogram.ReplyKeyboardMarkup", + "pyrogram.ReplyKeyboardRemove", + "pyrogram.ForceReply" + ] = None + ) -> Union["pyrogram.Message", None]: + """Use this method to send any media stored on the Telegram servers using a file_id. + + This convenience method works with any valid file_id only. + It does the same as calling the relevant method for sending media using a file_id, thus saving you from the + hassle of using the correct method for the media the file_id is pointing to. + + 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). + + file_id (``str``): + Media to send. + Pass a file_id as string to send a media that exists on the Telegram servers. + + caption (``bool``, *optional*): + Media caption, 0-1024 characters. + + parse_mode (``str``, *optional*): + Use :obj:`MARKDOWN ` or :obj:`HTML ` + if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your caption. + Defaults to Markdown. + + 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:`RPCError ` in case of a Telegram RPC error. + """ + style = self.html if parse_mode.lower() == "html" else self.markdown + + try: + decoded = utils.decode(file_id) + fmt = " 24 else "` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. ``ValueError`` if the provided string is not a valid ChatAction. """ @@ -60,7 +62,7 @@ class SendChatAction(BaseClient): action = action.value if "Upload" in action.__name__: - action = action(progress) + action = action(progress=progress) else: action = action() diff --git a/pyrogram/client/methods/messages/send_contact.py b/pyrogram/client/methods/messages/send_contact.py index 96b23a9b..9143440e 100644 --- a/pyrogram/client/methods/messages/send_contact.py +++ b/pyrogram/client/methods/messages/send_contact.py @@ -24,18 +24,22 @@ from pyrogram.client.ext import BaseClient class SendContact(BaseClient): - def send_contact(self, - chat_id: Union[int, str], - phone_number: str, - first_name: str, - last_name: str = "", - vcard: 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": + def send_contact( + self, + chat_id: Union[int, str], + phone_number: str, + first_name: str, + last_name: str = None, + vcard: str = None, + 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 phone contacts. Args: @@ -71,7 +75,7 @@ class SendContact(BaseClient): On success, the sent :obj:`Message ` is returned. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ r = self.send( functions.messages.SendMedia( @@ -79,8 +83,8 @@ class SendContact(BaseClient): media=types.InputMediaContact( phone_number=phone_number, first_name=first_name, - last_name=last_name, - vcard=vcard + last_name=last_name or "", + vcard=vcard or "" ), message="", silent=disable_notification or None, diff --git a/pyrogram/client/methods/messages/send_document.py b/pyrogram/client/methods/messages/send_document.py index 35fe8c0e..a36a0fbb 100644 --- a/pyrogram/client/methods/messages/send_document.py +++ b/pyrogram/client/methods/messages/send_document.py @@ -23,25 +23,29 @@ from typing import Union import pyrogram from pyrogram.api import functions, types -from pyrogram.api.errors import FileIdInvalid, FilePartMissing +from pyrogram.errors import FileIdInvalid, FilePartMissing from pyrogram.client.ext import BaseClient, utils class SendDocument(BaseClient): - def send_document(self, - chat_id: Union[int, str], - document: str, - thumb: str = None, - caption: str = "", - parse_mode: str = "", - disable_notification: bool = None, - reply_to_message_id: int = None, - reply_markup: Union["pyrogram.InlineKeyboardMarkup", - "pyrogram.ReplyKeyboardMarkup", - "pyrogram.ReplyKeyboardRemove", - "pyrogram.ForceReply"] = None, - progress: callable = None, - progress_args: tuple = ()) -> Union["pyrogram.Message", None]: + def send_document( + self, + chat_id: Union[int, str], + document: str, + thumb: str = None, + caption: str = "", + parse_mode: str = "", + disable_notification: bool = None, + reply_to_message_id: int = None, + reply_markup: Union[ + "pyrogram.InlineKeyboardMarkup", + "pyrogram.ReplyKeyboardMarkup", + "pyrogram.ReplyKeyboardRemove", + "pyrogram.ForceReply" + ] = None, + progress: callable = None, + progress_args: tuple = () + ) -> Union["pyrogram.Message", None]: """Use this method to send general files. Args: @@ -56,7 +60,7 @@ class SendDocument(BaseClient): pass an HTTP URL as a string for Telegram to get a file from the Internet, or pass a file path as string to upload a new file that exists on your local machine. - thumb (``str``): + thumb (``str``, *optional*): Thumbnail of the file sent. The thumbnail should be in JPEG format and less than 200 KB in size. A thumbnail's width and height should not exceed 90 pixels. @@ -109,7 +113,7 @@ class SendDocument(BaseClient): In case the upload is deliberately stopped with :meth:`stop_transmission`, None is returned instead. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ file = None style = self.html if parse_mode.lower() == "html" else self.markdown @@ -123,7 +127,7 @@ class SendDocument(BaseClient): file=file, thumb=thumb, attributes=[ - types.DocumentAttributeFilename(os.path.basename(document)) + types.DocumentAttributeFilename(file_name=os.path.basename(document)) ] ) elif document.startswith("http"): diff --git a/pyrogram/client/methods/messages/send_location.py b/pyrogram/client/methods/messages/send_location.py index b899a938..f3ed81df 100644 --- a/pyrogram/client/methods/messages/send_location.py +++ b/pyrogram/client/methods/messages/send_location.py @@ -24,16 +24,20 @@ from pyrogram.client.ext import BaseClient class SendLocation(BaseClient): - def send_location(self, - chat_id: Union[int, str], - latitude: float, - longitude: float, - disable_notification: bool = None, - reply_to_message_id: int = None, - reply_markup: Union["pyrogram.InlineKeyboardMarkup", - "pyrogram.ReplyKeyboardMarkup", - "pyrogram.ReplyKeyboardRemove", - "pyrogram.ForceReply"] = None) -> "pyrogram.Message": + def send_location( + self, + chat_id: Union[int, str], + latitude: float, + longitude: float, + 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 points on the map. Args: @@ -63,15 +67,15 @@ class SendLocation(BaseClient): On success, the sent :obj:`Message ` is returned. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ r = self.send( functions.messages.SendMedia( peer=self.resolve_peer(chat_id), media=types.InputMediaGeoPoint( - types.InputGeoPoint( - latitude, - longitude + geo_point=types.InputGeoPoint( + lat=latitude, + long=longitude ) ), message="", diff --git a/pyrogram/client/methods/messages/send_media_group.py b/pyrogram/client/methods/messages/send_media_group.py index 0df273bc..4fdc1132 100644 --- a/pyrogram/client/methods/messages/send_media_group.py +++ b/pyrogram/client/methods/messages/send_media_group.py @@ -25,7 +25,7 @@ from typing import Union, List import pyrogram from pyrogram.api import functions, types -from pyrogram.api.errors import FileIdInvalid, FloodWait +from pyrogram.errors import FileIdInvalid, FloodWait from pyrogram.client.ext import BaseClient, utils log = logging.getLogger(__name__) @@ -34,11 +34,13 @@ log = logging.getLogger(__name__) class SendMediaGroup(BaseClient): # TODO: Add progress parameter # TODO: Figure out how to send albums using URLs - def send_media_group(self, - chat_id: Union[int, str], - media: List[Union["pyrogram.InputMediaPhoto", "pyrogram.InputMediaVideo"]], - disable_notification: bool = None, - reply_to_message_id: int = None): + def send_media_group( + self, + chat_id: Union[int, str], + media: List[Union["pyrogram.InputMediaPhoto", "pyrogram.InputMediaVideo"]], + disable_notification: bool = None, + reply_to_message_id: int = None + ): """Use this method to send a group of photos or videos as an album. Args: @@ -47,10 +49,8 @@ class SendMediaGroup(BaseClient): 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). - media (``list``): - A list containing either :obj:`InputMediaPhoto ` or - :obj:`InputMediaVideo ` objects - describing photos and videos to be sent, must include 2–10 items. + media (List of :obj:`InputMediaPhoto` and :obj:`InputMediaVideo`): + A list describing photos and videos to be sent, must include 2–10 items. disable_notification (``bool``, *optional*): Sends the message silently. @@ -64,7 +64,7 @@ class SendMediaGroup(BaseClient): single messages sent. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ multi_media = [] @@ -137,7 +137,7 @@ class SendMediaGroup(BaseClient): w=i.width, h=i.height ), - types.DocumentAttributeFilename(os.path.basename(i.media)) + types.DocumentAttributeFilename(file_name=os.path.basename(i.media)) ] ) ) diff --git a/pyrogram/client/methods/messages/send_message.py b/pyrogram/client/methods/messages/send_message.py index 6589fcd6..b45b7192 100644 --- a/pyrogram/client/methods/messages/send_message.py +++ b/pyrogram/client/methods/messages/send_message.py @@ -24,17 +24,21 @@ from ...ext import BaseClient class SendMessage(BaseClient): - def send_message(self, - chat_id: Union[int, str], - text: str, - parse_mode: str = "", - disable_web_page_preview: bool = None, - disable_notification: bool = None, - reply_to_message_id: int = None, - reply_markup: Union["pyrogram.InlineKeyboardMarkup", - "pyrogram.ReplyKeyboardMarkup", - "pyrogram.ReplyKeyboardRemove", - "pyrogram.ForceReply"] = None) -> "pyrogram.Message": + def send_message( + self, + chat_id: Union[int, str], + text: str, + parse_mode: str = "", + disable_web_page_preview: bool = None, + 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 text messages. Args: @@ -69,7 +73,7 @@ class SendMessage(BaseClient): On success, the sent :obj:`Message` is returned. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ style = self.html if parse_mode.lower() == "html" else self.markdown message, entities = style.parse(text).values() diff --git a/pyrogram/client/methods/messages/send_photo.py b/pyrogram/client/methods/messages/send_photo.py index 84647245..7e327cbd 100644 --- a/pyrogram/client/methods/messages/send_photo.py +++ b/pyrogram/client/methods/messages/send_photo.py @@ -23,25 +23,29 @@ from typing import Union import pyrogram from pyrogram.api import functions, types -from pyrogram.api.errors import FileIdInvalid, FilePartMissing +from pyrogram.errors import FileIdInvalid, FilePartMissing from pyrogram.client.ext import BaseClient, utils class SendPhoto(BaseClient): - def send_photo(self, - chat_id: Union[int, str], - photo: str, - caption: str = "", - parse_mode: str = "", - ttl_seconds: int = None, - disable_notification: bool = None, - reply_to_message_id: int = None, - reply_markup: Union["pyrogram.InlineKeyboardMarkup", - "pyrogram.ReplyKeyboardMarkup", - "pyrogram.ReplyKeyboardRemove", - "pyrogram.ForceReply"] = None, - progress: callable = None, - progress_args: tuple = ()) -> Union["pyrogram.Message", None]: + def send_photo( + self, + chat_id: Union[int, str], + photo: str, + caption: str = "", + parse_mode: str = "", + ttl_seconds: int = None, + disable_notification: bool = None, + reply_to_message_id: int = None, + reply_markup: Union[ + "pyrogram.InlineKeyboardMarkup", + "pyrogram.ReplyKeyboardMarkup", + "pyrogram.ReplyKeyboardRemove", + "pyrogram.ForceReply" + ] = None, + progress: callable = None, + progress_args: tuple = () + ) -> Union["pyrogram.Message", None]: """Use this method to send photos. Args: @@ -108,7 +112,7 @@ class SendPhoto(BaseClient): In case the upload is deliberately stopped with :meth:`stop_transmission`, None is returned instead. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ file = None style = self.html if parse_mode.lower() == "html" else self.markdown diff --git a/pyrogram/client/methods/messages/send_poll.py b/pyrogram/client/methods/messages/send_poll.py index 8e938a1a..13e55b08 100644 --- a/pyrogram/client/methods/messages/send_poll.py +++ b/pyrogram/client/methods/messages/send_poll.py @@ -24,16 +24,20 @@ 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": + 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: @@ -63,7 +67,7 @@ class SendPoll(BaseClient): On success, the sent :obj:`Message ` is returned. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ r = self.send( functions.messages.SendMedia( diff --git a/pyrogram/client/methods/messages/send_sticker.py b/pyrogram/client/methods/messages/send_sticker.py index e41c074e..e556aae3 100644 --- a/pyrogram/client/methods/messages/send_sticker.py +++ b/pyrogram/client/methods/messages/send_sticker.py @@ -23,22 +23,26 @@ from typing import Union import pyrogram from pyrogram.api import functions, types -from pyrogram.api.errors import FileIdInvalid, FilePartMissing +from pyrogram.errors import FileIdInvalid, FilePartMissing from pyrogram.client.ext import BaseClient, utils class SendSticker(BaseClient): - def send_sticker(self, - chat_id: Union[int, str], - sticker: str, - disable_notification: bool = None, - reply_to_message_id: int = None, - reply_markup: Union["pyrogram.InlineKeyboardMarkup", - "pyrogram.ReplyKeyboardMarkup", - "pyrogram.ReplyKeyboardRemove", - "pyrogram.ForceReply"] = None, - progress: callable = None, - progress_args: tuple = ()) -> Union["pyrogram.Message", None]: + def send_sticker( + self, + chat_id: Union[int, str], + sticker: str, + disable_notification: bool = None, + reply_to_message_id: int = None, + reply_markup: Union[ + "pyrogram.InlineKeyboardMarkup", + "pyrogram.ReplyKeyboardMarkup", + "pyrogram.ReplyKeyboardRemove", + "pyrogram.ForceReply" + ] = None, + progress: callable = None, + progress_args: tuple = () + ) -> Union["pyrogram.Message", None]: """Use this method to send .webp stickers. Args: @@ -92,7 +96,7 @@ class SendSticker(BaseClient): In case the upload is deliberately stopped with :meth:`stop_transmission`, None is returned instead. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ file = None @@ -103,7 +107,7 @@ class SendSticker(BaseClient): mime_type="image/webp", file=file, attributes=[ - types.DocumentAttributeFilename(os.path.basename(sticker)) + types.DocumentAttributeFilename(file_name=os.path.basename(sticker)) ] ) elif sticker.startswith("http"): diff --git a/pyrogram/client/methods/messages/send_venue.py b/pyrogram/client/methods/messages/send_venue.py index 163be38e..1c9ca630 100644 --- a/pyrogram/client/methods/messages/send_venue.py +++ b/pyrogram/client/methods/messages/send_venue.py @@ -24,20 +24,24 @@ from pyrogram.client.ext import BaseClient class SendVenue(BaseClient): - def send_venue(self, - chat_id: Union[int, str], - latitude: float, - longitude: float, - title: str, - address: str, - foursquare_id: str = "", - foursquare_type: 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": + def send_venue( + self, + chat_id: Union[int, str], + latitude: float, + longitude: float, + title: str, + address: str, + foursquare_id: str = "", + foursquare_type: 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 information about a venue. Args: @@ -80,7 +84,7 @@ class SendVenue(BaseClient): On success, the sent :obj:`Message ` is returned. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ r = self.send( functions.messages.SendMedia( diff --git a/pyrogram/client/methods/messages/send_video.py b/pyrogram/client/methods/messages/send_video.py index b69b2185..08d8b7ab 100644 --- a/pyrogram/client/methods/messages/send_video.py +++ b/pyrogram/client/methods/messages/send_video.py @@ -23,29 +23,33 @@ from typing import Union import pyrogram from pyrogram.api import functions, types -from pyrogram.api.errors import FileIdInvalid, FilePartMissing +from pyrogram.errors import FileIdInvalid, FilePartMissing from pyrogram.client.ext import BaseClient, utils class SendVideo(BaseClient): - def send_video(self, - chat_id: Union[int, str], - video: str, - caption: str = "", - parse_mode: str = "", - duration: int = 0, - width: int = 0, - height: int = 0, - thumb: str = None, - supports_streaming: bool = True, - disable_notification: bool = None, - reply_to_message_id: int = None, - reply_markup: Union["pyrogram.InlineKeyboardMarkup", - "pyrogram.ReplyKeyboardMarkup", - "pyrogram.ReplyKeyboardRemove", - "pyrogram.ForceReply"] = None, - progress: callable = None, - progress_args: tuple = ()) -> Union["pyrogram.Message", None]: + def send_video( + self, + chat_id: Union[int, str], + video: str, + caption: str = "", + parse_mode: str = "", + duration: int = 0, + width: int = 0, + height: int = 0, + thumb: str = None, + supports_streaming: bool = True, + disable_notification: bool = None, + reply_to_message_id: int = None, + reply_markup: Union[ + "pyrogram.InlineKeyboardMarkup", + "pyrogram.ReplyKeyboardMarkup", + "pyrogram.ReplyKeyboardRemove", + "pyrogram.ForceReply" + ] = None, + progress: callable = None, + progress_args: tuple = () + ) -> Union["pyrogram.Message", None]: """Use this method to send video files. Args: @@ -125,7 +129,7 @@ class SendVideo(BaseClient): In case the upload is deliberately stopped with :meth:`stop_transmission`, None is returned instead. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ file = None style = self.html if parse_mode.lower() == "html" else self.markdown @@ -145,7 +149,7 @@ class SendVideo(BaseClient): w=width, h=height ), - types.DocumentAttributeFilename(os.path.basename(video)) + types.DocumentAttributeFilename(file_name=os.path.basename(video)) ] ) elif video.startswith("http"): diff --git a/pyrogram/client/methods/messages/send_video_note.py b/pyrogram/client/methods/messages/send_video_note.py index b35dbea6..4844dd65 100644 --- a/pyrogram/client/methods/messages/send_video_note.py +++ b/pyrogram/client/methods/messages/send_video_note.py @@ -23,25 +23,29 @@ from typing import Union import pyrogram from pyrogram.api import functions, types -from pyrogram.api.errors import FileIdInvalid, FilePartMissing +from pyrogram.errors import FileIdInvalid, FilePartMissing from pyrogram.client.ext import BaseClient, utils class SendVideoNote(BaseClient): - def send_video_note(self, - chat_id: Union[int, str], - video_note: str, - duration: int = 0, - length: int = 1, - thumb: str = None, - disable_notification: bool = None, - reply_to_message_id: int = None, - reply_markup: Union["pyrogram.InlineKeyboardMarkup", - "pyrogram.ReplyKeyboardMarkup", - "pyrogram.ReplyKeyboardRemove", - "pyrogram.ForceReply"] = None, - progress: callable = None, - progress_args: tuple = ()) -> Union["pyrogram.Message", None]: + def send_video_note( + self, + chat_id: Union[int, str], + video_note: str, + duration: int = 0, + length: int = 1, + thumb: str = None, + disable_notification: bool = None, + reply_to_message_id: int = None, + reply_markup: Union[ + "pyrogram.InlineKeyboardMarkup", + "pyrogram.ReplyKeyboardMarkup", + "pyrogram.ReplyKeyboardRemove", + "pyrogram.ForceReply" + ] = None, + progress: callable = None, + progress_args: tuple = () + ) -> Union["pyrogram.Message", None]: """Use this method to send video messages. Args: @@ -107,7 +111,7 @@ class SendVideoNote(BaseClient): In case the upload is deliberately stopped with :meth:`stop_transmission`, None is returned instead. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ file = None diff --git a/pyrogram/client/methods/messages/send_voice.py b/pyrogram/client/methods/messages/send_voice.py index 33261db6..110b0704 100644 --- a/pyrogram/client/methods/messages/send_voice.py +++ b/pyrogram/client/methods/messages/send_voice.py @@ -23,25 +23,29 @@ from typing import Union import pyrogram from pyrogram.api import functions, types -from pyrogram.api.errors import FileIdInvalid, FilePartMissing +from pyrogram.errors import FileIdInvalid, FilePartMissing from pyrogram.client.ext import BaseClient, utils class SendVoice(BaseClient): - def send_voice(self, - chat_id: Union[int, str], - voice: str, - caption: str = "", - parse_mode: str = "", - duration: int = 0, - disable_notification: bool = None, - reply_to_message_id: int = None, - reply_markup: Union["pyrogram.InlineKeyboardMarkup", - "pyrogram.ReplyKeyboardMarkup", - "pyrogram.ReplyKeyboardRemove", - "pyrogram.ForceReply"] = None, - progress: callable = None, - progress_args: tuple = ()) -> Union["pyrogram.Message", None]: + def send_voice( + self, + chat_id: Union[int, str], + voice: str, + caption: str = "", + parse_mode: str = "", + duration: int = 0, + disable_notification: bool = None, + reply_to_message_id: int = None, + reply_markup: Union[ + "pyrogram.InlineKeyboardMarkup", + "pyrogram.ReplyKeyboardMarkup", + "pyrogram.ReplyKeyboardRemove", + "pyrogram.ForceReply" + ] = None, + progress: callable = None, + progress_args: tuple = () + ) -> Union["pyrogram.Message", None]: """Use this method to send audio files. Args: @@ -106,7 +110,7 @@ class SendVoice(BaseClient): In case the upload is deliberately stopped with :meth:`stop_transmission`, None is returned instead. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ file = None style = self.html if parse_mode.lower() == "html" else self.markdown diff --git a/pyrogram/client/methods/messages/vote_poll.py b/pyrogram/client/methods/messages/vote_poll.py index 9e400e62..2a9de874 100644 --- a/pyrogram/client/methods/messages/vote_poll.py +++ b/pyrogram/client/methods/messages/vote_poll.py @@ -23,10 +23,12 @@ from pyrogram.client.ext import BaseClient class VotePoll(BaseClient): - def vote_poll(self, - chat_id: Union[int, str], - message_id: id, - option: int) -> bool: + def vote_poll( + self, + chat_id: Union[int, str], + message_id: id, + option: int + ) -> bool: """Use this method to vote a poll. Args: @@ -45,7 +47,7 @@ class VotePoll(BaseClient): On success, True is returned. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ poll = self.get_messages(chat_id, message_id).poll diff --git a/pyrogram/client/methods/password/change_cloud_password.py b/pyrogram/client/methods/password/change_cloud_password.py index 3504dab1..2f8cfbd6 100644 --- a/pyrogram/client/methods/password/change_cloud_password.py +++ b/pyrogram/client/methods/password/change_cloud_password.py @@ -24,10 +24,12 @@ from ...ext import BaseClient class ChangeCloudPassword(BaseClient): - def change_cloud_password(self, - current_password: str, - new_password: str, - new_hint: str = "") -> bool: + def change_cloud_password( + self, + 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: @@ -44,7 +46,7 @@ class ChangeCloudPassword(BaseClient): True on success. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. ``ValueError`` in case there is no cloud password to change. """ r = self.send(functions.account.GetPassword()) diff --git a/pyrogram/client/methods/password/enable_cloud_password.py b/pyrogram/client/methods/password/enable_cloud_password.py index 980f50fd..b29dcfd3 100644 --- a/pyrogram/client/methods/password/enable_cloud_password.py +++ b/pyrogram/client/methods/password/enable_cloud_password.py @@ -24,10 +24,12 @@ from ...ext import BaseClient class EnableCloudPassword(BaseClient): - def enable_cloud_password(self, - password: str, - hint: str = "", - email: str = None) -> bool: + def enable_cloud_password( + self, + password: str, + hint: str = "", + 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. @@ -46,7 +48,7 @@ class EnableCloudPassword(BaseClient): True on success. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. ``ValueError`` in case there is already a cloud password enabled. """ r = self.send(functions.account.GetPassword()) diff --git a/pyrogram/client/methods/password/remove_cloud_password.py b/pyrogram/client/methods/password/remove_cloud_password.py index 6817ab12..6e9a0ab4 100644 --- a/pyrogram/client/methods/password/remove_cloud_password.py +++ b/pyrogram/client/methods/password/remove_cloud_password.py @@ -22,8 +22,10 @@ from ...ext import BaseClient class RemoveCloudPassword(BaseClient): - def remove_cloud_password(self, - password: str) -> bool: + def remove_cloud_password( + self, + password: str + ) -> bool: """Use this method to turn off the Two-Step Verification security feature (Cloud Password) on your account. Args: @@ -34,7 +36,7 @@ class RemoveCloudPassword(BaseClient): True on success. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. ``ValueError`` in case there is no cloud password to remove. """ r = self.send(functions.account.GetPassword()) diff --git a/pyrogram/client/methods/password/utils.py b/pyrogram/client/methods/password/utils.py index 24c4dd28..3a29976a 100644 --- a/pyrogram/client/methods/password/utils.py +++ b/pyrogram/client/methods/password/utils.py @@ -101,4 +101,4 @@ def compute_check(r: types.account.Password, password: str) -> types.InputCheckP + K_bytes ) - return types.InputCheckPasswordSRP(srp_id, A_bytes, M1_bytes) + return types.InputCheckPasswordSRP(srp_id=srp_id, A=A_bytes, M1=M1_bytes) diff --git a/pyrogram/client/methods/users/__init__.py b/pyrogram/client/methods/users/__init__.py index db5e5869..f8c39650 100644 --- a/pyrogram/client/methods/users/__init__.py +++ b/pyrogram/client/methods/users/__init__.py @@ -21,6 +21,7 @@ from .get_me import GetMe from .get_user_profile_photos import GetUserProfilePhotos from .get_users import GetUsers from .set_user_profile_photo import SetUserProfilePhoto +from .update_username import UpdateUsername class Users( @@ -28,6 +29,7 @@ class Users( SetUserProfilePhoto, DeleteUserProfilePhotos, GetUsers, - GetMe + GetMe, + UpdateUsername ): pass diff --git a/pyrogram/client/methods/users/delete_user_profile_photos.py b/pyrogram/client/methods/users/delete_user_profile_photos.py index 025a5e95..bd9fc98e 100644 --- a/pyrogram/client/methods/users/delete_user_profile_photos.py +++ b/pyrogram/client/methods/users/delete_user_profile_photos.py @@ -25,9 +25,11 @@ from ...ext import BaseClient class DeleteUserProfilePhotos(BaseClient): - def delete_user_profile_photos(self, - id: Union[str, List[str]]) -> bool: - """Use this method to delete your own profile photos + def delete_user_profile_photos( + self, + id: Union[str, List[str]] + ) -> bool: + """Use this method to delete your own profile photos. Args: id (``str`` | ``list``): @@ -38,7 +40,7 @@ class DeleteUserProfilePhotos(BaseClient): True on success. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ id = id if isinstance(id, list) else [id] input_photos = [] diff --git a/pyrogram/client/methods/users/get_me.py b/pyrogram/client/methods/users/get_me.py index beea1243..c8b6c1f1 100644 --- a/pyrogram/client/methods/users/get_me.py +++ b/pyrogram/client/methods/users/get_me.py @@ -29,13 +29,13 @@ class GetMe(BaseClient): Basic information about the user or bot in form of a :obj:`User` object Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ return pyrogram.User._parse( self, self.send( functions.users.GetFullUser( - types.InputPeerSelf() + id=types.InputPeerSelf() ) ).user ) diff --git a/pyrogram/client/methods/users/get_user_profile_photos.py b/pyrogram/client/methods/users/get_user_profile_photos.py index 2129dba3..ac7a872e 100644 --- a/pyrogram/client/methods/users/get_user_profile_photos.py +++ b/pyrogram/client/methods/users/get_user_profile_photos.py @@ -24,10 +24,12 @@ from ...ext import BaseClient class GetUserProfilePhotos(BaseClient): - def get_user_profile_photos(self, - user_id: Union[int, str], - offset: int = 0, - limit: int = 100) -> "pyrogram.UserProfilePhotos": + def get_user_profile_photos( + self, + user_id: Union[int, str], + offset: int = 0, + limit: int = 100 + ) -> "pyrogram.UserProfilePhotos": """Use this method to get a list of profile pictures for a user. Args: @@ -48,7 +50,7 @@ class GetUserProfilePhotos(BaseClient): On success, a :obj:`UserProfilePhotos` object is returned. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ return pyrogram.UserProfilePhotos._parse( self, diff --git a/pyrogram/client/methods/users/get_users.py b/pyrogram/client/methods/users/get_users.py index 6c340ffa..7e6ebd6b 100644 --- a/pyrogram/client/methods/users/get_users.py +++ b/pyrogram/client/methods/users/get_users.py @@ -24,8 +24,10 @@ from ...ext import BaseClient class GetUsers(BaseClient): - def get_users(self, - user_ids: Iterable[Union[int, str]]) -> Union["pyrogram.User", List["pyrogram.User"]]: + def get_users( + self, + user_ids: Iterable[Union[int, str]] + ) -> Union["pyrogram.User", List["pyrogram.User"]]: """Use this method to get information about a user. You can retrieve up to 200 users at once. @@ -41,7 +43,7 @@ class GetUsers(BaseClient): *user_ids* was an integer or string, the single requested :obj:`User` is returned. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ is_iterable = not isinstance(user_ids, (int, str)) user_ids = list(user_ids) if is_iterable else [user_ids] diff --git a/pyrogram/client/methods/users/set_user_profile_photo.py b/pyrogram/client/methods/users/set_user_profile_photo.py index 0863c695..af02a12d 100644 --- a/pyrogram/client/methods/users/set_user_profile_photo.py +++ b/pyrogram/client/methods/users/set_user_profile_photo.py @@ -21,8 +21,10 @@ from ...ext import BaseClient class SetUserProfilePhoto(BaseClient): - def set_user_profile_photo(self, - photo: str) -> bool: + def set_user_profile_photo( + self, + photo: str + ) -> bool: """Use this method to set a new profile photo. This method only works for Users. @@ -37,13 +39,13 @@ class SetUserProfilePhoto(BaseClient): True on success. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ return bool( self.send( functions.photos.UploadProfilePhoto( - self.save_file(photo) + file=self.save_file(photo) ) ) ) diff --git a/pyrogram/client/methods/users/update_username.py b/pyrogram/client/methods/users/update_username.py new file mode 100644 index 00000000..d0c87eb2 --- /dev/null +++ b/pyrogram/client/methods/users/update_username.py @@ -0,0 +1,53 @@ +# 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 Union + +from pyrogram.api import functions +from ...ext import BaseClient + + +class UpdateUsername(BaseClient): + def update_username( + self, + username: Union[str, None] + ) -> bool: + """Use this method to update your own username. + + This method only works for users, not bots. Bot usernames must be changed via Bot Support or by recreating + them from scratch using BotFather. To update a channel or supergroup username you can use + :meth:`update_chat_username`. + + Args: + username (``str`` | ``None``): + Username to set. "" (empty string) or None to remove the username. + + Returns: + True on success. + + Raises: + :class:`RPCError ` in case of a Telegram RPC error. + """ + + return bool( + self.send( + functions.account.UpdateUsername( + username=username or "" + ) + ) + ) diff --git a/pyrogram/client/style/html.py b/pyrogram/client/style/html.py index 88e317cd..894dbd6c 100644 --- a/pyrogram/client/style/html.py +++ b/pyrogram/client/style/html.py @@ -19,6 +19,7 @@ import re from collections import OrderedDict +import pyrogram from pyrogram.api.types import ( MessageEntityBold as Bold, MessageEntityItalic as Italic, @@ -28,6 +29,7 @@ from pyrogram.api.types import ( MessageEntityMentionName as MentionInvalid, InputMessageEntityMentionName as Mention, ) +from pyrogram.errors import PeerIdInvalid from . import utils from ..session_storage import SessionStorage @@ -36,12 +38,13 @@ class HTML: HTML_RE = re.compile(r"<(\w+)(?: href=([\"'])([^<]+)\2)?>([^>]+)") MENTION_RE = re.compile(r"tg://user\?id=(\d+)") - def __init__(self, session_storage: SessionStorage): + def __init__(self, session_storage: SessionStorage, client: "pyrogram.BaseClient" = None): + self.client = client self.session_storage = session_storage def parse(self, message: str): entities = [] - message = utils.add_surrogates(str(message)) + message = utils.add_surrogates(str(message or "")) offset = 0 for match in self.HTML_RE.finditer(message): @@ -59,20 +62,20 @@ class HTML: input_user = None entity = ( - Mention(start, len(body), input_user) - if input_user else MentionInvalid(start, len(body), user_id) + Mention(offset=start, length=len(body), user_id=input_user) + if input_user else MentionInvalid(offset=start, length=len(body), user_id=user_id) ) else: - entity = Url(start, len(body), url) + entity = Url(offset=start, length=len(body), url=url) else: if style == "b" or style == "strong": - entity = Bold(start, len(body)) + entity = Bold(offset=start, length=len(body)) elif style == "i" or style == "em": - entity = Italic(start, len(body)) + entity = Italic(offset=start, length=len(body)) elif style == "code": - entity = Code(start, len(body)) + entity = Code(offset=start, length=len(body)) elif style == "pre": - entity = Pre(start, len(body), "") + entity = Pre(offset=start, length=len(body), language="") else: continue diff --git a/pyrogram/client/style/markdown.py b/pyrogram/client/style/markdown.py index 6793b643..68b54bbb 100644 --- a/pyrogram/client/style/markdown.py +++ b/pyrogram/client/style/markdown.py @@ -19,6 +19,7 @@ import re from collections import OrderedDict +import pyrogram from pyrogram.api.types import ( MessageEntityBold as Bold, MessageEntityItalic as Italic, @@ -28,6 +29,7 @@ from pyrogram.api.types import ( MessageEntityMentionName as MentionInvalid, InputMessageEntityMentionName as Mention ) +from pyrogram.errors import PeerIdInvalid from . import utils from ..session_storage import SessionStorage @@ -53,11 +55,12 @@ class Markdown: )) MENTION_RE = re.compile(r"tg://user\?id=(\d+)") - def __init__(self, session_storage: SessionStorage): + def __init__(self, session_storage: SessionStorage, client: "pyrogram.BaseClient" = None): + self.client = client self.session_storage = session_storage def parse(self, message: str): - message = utils.add_surrogates(str(message)).strip() + message = utils.add_surrogates(str(message or "")).strip() entities = [] offset = 0 @@ -76,24 +79,23 @@ class Markdown: input_user = None entity = ( - Mention(start, len(text), input_user) - if input_user - else MentionInvalid(start, len(text), user_id) + Mention(offset=start, length=len(text), user_id=input_user) + if input_user else MentionInvalid(offset=start, length=len(text), user_id=user_id) ) else: - entity = Url(start, len(text), url) + entity = Url(offset=start, length=len(text), url=url) body = text offset += len(url) + 4 else: if style == self.BOLD_DELIMITER: - entity = Bold(start, len(body)) + entity = Bold(offset=start, length=len(body)) elif style == self.ITALIC_DELIMITER: - entity = Italic(start, len(body)) + entity = Italic(offset=start, length=len(body)) elif style == self.CODE_DELIMITER: - entity = Code(start, len(body)) + entity = Code(offset=start, length=len(body)) elif style == self.PRE_DELIMITER: - entity = Pre(start, len(body), "") + entity = Pre(offset=start, length=len(body), language="") else: continue diff --git a/pyrogram/client/types/__init__.py b/pyrogram/client/types/__init__.py index a29b0816..120c7ff5 100644 --- a/pyrogram/client/types/__init__.py +++ b/pyrogram/client/types/__init__.py @@ -16,22 +16,10 @@ # You should have received a copy of the GNU Lesser General Public License # along with Pyrogram. If not, see . -from .bots import ( - CallbackQuery, ForceReply, InlineKeyboardButton, InlineKeyboardMarkup, - KeyboardButton, ReplyKeyboardMarkup, ReplyKeyboardRemove, CallbackGame, - GameHighScore, GameHighScores -) -from .input_media import ( - InputMediaAudio, InputPhoneContact, InputMediaVideo, InputMediaPhoto, - InputMediaDocument, InputMediaAnimation -) -from .messages_and_media import ( - Audio, Contact, Document, Animation, Location, Photo, PhotoSize, - Sticker, Venue, Video, VideoNote, Voice, UserProfilePhotos, - Message, Messages, MessageEntity, Poll, PollOption, Game -) -from .update import StopPropagation, ContinuePropagation -from .user_and_chats import ( - Chat, ChatMember, ChatMembers, ChatPhoto, - Dialog, Dialogs, User, UserStatus, ChatPreview -) +from .bots import * +from .inline_mode import * +from .input_media import * +from .input_message_content import * +from .messages_and_media import * +from .update import * +from .user_and_chats import * diff --git a/pyrogram/client/types/bots/__init__.py b/pyrogram/client/types/bots/__init__.py index 81767945..dae33e10 100644 --- a/pyrogram/client/types/bots/__init__.py +++ b/pyrogram/client/types/bots/__init__.py @@ -26,3 +26,8 @@ from .inline_keyboard_markup import InlineKeyboardMarkup from .keyboard_button import KeyboardButton from .reply_keyboard_markup import ReplyKeyboardMarkup from .reply_keyboard_remove import ReplyKeyboardRemove + +__all__ = [ + "CallbackGame", "CallbackQuery", "ForceReply", "GameHighScore", "GameHighScores", "InlineKeyboardButton", + "InlineKeyboardMarkup", "KeyboardButton", "ReplyKeyboardMarkup", "ReplyKeyboardRemove" +] diff --git a/pyrogram/client/types/bots/callback_game.py b/pyrogram/client/types/bots/callback_game.py index be026360..fc2d9884 100644 --- a/pyrogram/client/types/bots/callback_game.py +++ b/pyrogram/client/types/bots/callback_game.py @@ -25,5 +25,7 @@ class CallbackGame(PyrogramType): Use BotFather to set up your game. """ + __slots__ = [] + def __init__(self): super().__init__(None) diff --git a/pyrogram/client/types/bots/callback_query.py b/pyrogram/client/types/bots/callback_query.py index c2558844..4497747e 100644 --- a/pyrogram/client/types/bots/callback_query.py +++ b/pyrogram/client/types/bots/callback_query.py @@ -58,16 +58,20 @@ class CallbackQuery(PyrogramType, Update): """ - def __init__(self, - *, - client: "pyrogram.client.ext.BaseClient", - id: str, - from_user: User, - chat_instance: str, - message: "pyrogram.Message" = None, - inline_message_id: str = None, - data: bytes = None, - game_short_name: str = None): + __slots__ = ["id", "from_user", "chat_instance", "message", "inline_message_id", "data", "game_short_name"] + + def __init__( + self, + *, + client: "pyrogram.client.ext.BaseClient", + id: str, + from_user: User, + chat_instance: str, + message: "pyrogram.Message" = None, + inline_message_id: str = None, + data: bytes = None, + game_short_name: str = None + ): super().__init__(client) self.id = id diff --git a/pyrogram/client/types/bots/force_reply.py b/pyrogram/client/types/bots/force_reply.py index 2e5c2f42..12969742 100644 --- a/pyrogram/client/types/bots/force_reply.py +++ b/pyrogram/client/types/bots/force_reply.py @@ -21,8 +21,9 @@ from ..pyrogram_type import PyrogramType class ForceReply(PyrogramType): - """Upon receiving a message with this object, Telegram clients will display a reply interface to the user - (act as if the user has selected the bot's message and tapped 'Reply'). + """Upon receiving a message with this object, Telegram clients will display a reply interface to the user. + + This acts as if the user has selected the bot's message and tapped "Reply". This can be extremely useful if you want to create user-friendly step-by-step interfaces without having to sacrifice privacy mode. @@ -33,8 +34,12 @@ class ForceReply(PyrogramType): 2) if the bot's message is a reply (has reply_to_message_id), sender of the original message. """ - def __init__(self, - selective: bool = None): + __slots__ = ["selective"] + + def __init__( + self, + selective: bool = None + ): super().__init__(None) self.selective = selective diff --git a/pyrogram/client/types/bots/game_high_score.py b/pyrogram/client/types/bots/game_high_score.py index 0541c18c..da6b2881 100644 --- a/pyrogram/client/types/bots/game_high_score.py +++ b/pyrogram/client/types/bots/game_high_score.py @@ -37,12 +37,16 @@ class GameHighScore(PyrogramType): Position in high score table for the game. """ - def __init__(self, - *, - client: "pyrogram.client.ext.BaseClient", - user: User, - score: int, - position: int = None): + __slots__ = ["user", "score", "position"] + + def __init__( + self, + *, + client: "pyrogram.client.ext.BaseClient", + user: User, + score: int, + position: int = None + ): super().__init__(client) self.user = user diff --git a/pyrogram/client/types/bots/game_high_scores.py b/pyrogram/client/types/bots/game_high_scores.py index 1717effa..3c197969 100644 --- a/pyrogram/client/types/bots/game_high_scores.py +++ b/pyrogram/client/types/bots/game_high_scores.py @@ -35,11 +35,15 @@ class GameHighScores(PyrogramType): Game scores. """ - def __init__(self, - *, - client: "pyrogram.client.ext.BaseClient", - total_count: int, - game_high_scores: List[GameHighScore]): + __slots__ = ["total_count", "game_high_scores"] + + def __init__( + self, + *, + client: "pyrogram.client.ext.BaseClient", + total_count: int, + game_high_scores: List[GameHighScore] + ): super().__init__(client) self.total_count = total_count diff --git a/pyrogram/client/types/bots/inline_keyboard_button.py b/pyrogram/client/types/bots/inline_keyboard_button.py index cc829cb1..c0c3eb8c 100644 --- a/pyrogram/client/types/bots/inline_keyboard_button.py +++ b/pyrogram/client/types/bots/inline_keyboard_button.py @@ -54,13 +54,19 @@ class InlineKeyboardButton(PyrogramType): # TODO: Add callback_game and pay fields - def __init__(self, - text: str, - callback_data: bytes = None, - url: str = None, - switch_inline_query: str = None, - switch_inline_query_current_chat: str = None, - callback_game: CallbackGame = None): + __slots__ = [ + "text", "url", "callback_data", "switch_inline_query", "switch_inline_query_current_chat", "callback_game" + ] + + def __init__( + self, + text: str, + callback_data: bytes = None, + url: str = None, + switch_inline_query: str = None, + switch_inline_query_current_chat: str = None, + callback_game: CallbackGame = None + ): super().__init__(None) self.text = str(text) @@ -104,17 +110,21 @@ class InlineKeyboardButton(PyrogramType): ) def write(self): - if self.callback_data: - return KeyboardButtonCallback(self.text, self.callback_data) + if self.callback_data is not None: + return KeyboardButtonCallback(text=self.text, data=self.callback_data) - if self.url: - return KeyboardButtonUrl(self.text, self.url) + if self.url is not None: + return KeyboardButtonUrl(text=self.text, url=self.url) - if self.switch_inline_query: - return KeyboardButtonSwitchInline(self.text, self.switch_inline_query) + if self.switch_inline_query is not None: + return KeyboardButtonSwitchInline(text=self.text, query=self.switch_inline_query) - if self.switch_inline_query_current_chat: - return KeyboardButtonSwitchInline(self.text, self.switch_inline_query_current_chat, same_peer=True) + if self.switch_inline_query_current_chat is not None: + return KeyboardButtonSwitchInline( + text=self.text, + query=self.switch_inline_query_current_chat, + same_peer=True + ) - if self.callback_game: - return KeyboardButtonGame(self.text) + if self.callback_game is not None: + return KeyboardButtonGame(text=self.text) diff --git a/pyrogram/client/types/bots/inline_keyboard_markup.py b/pyrogram/client/types/bots/inline_keyboard_markup.py index f18bd605..54476c5e 100644 --- a/pyrogram/client/types/bots/inline_keyboard_markup.py +++ b/pyrogram/client/types/bots/inline_keyboard_markup.py @@ -31,8 +31,12 @@ class InlineKeyboardMarkup(PyrogramType): List of button rows, each represented by a List of InlineKeyboardButton objects. """ - def __init__(self, - inline_keyboard: List[List[InlineKeyboardButton]]): + __slots__ = ["inline_keyboard"] + + def __init__( + self, + inline_keyboard: List[List[InlineKeyboardButton]] + ): super().__init__(None) self.inline_keyboard = inline_keyboard @@ -55,7 +59,7 @@ class InlineKeyboardMarkup(PyrogramType): def write(self): return ReplyInlineMarkup( - [KeyboardButtonRow( - [j.write() for j in i] + rows=[KeyboardButtonRow( + buttons=[j.write() for j in i] ) for i in self.inline_keyboard] ) diff --git a/pyrogram/client/types/bots/keyboard_button.py b/pyrogram/client/types/bots/keyboard_button.py index 3c7c2bd6..477442cc 100644 --- a/pyrogram/client/types/bots/keyboard_button.py +++ b/pyrogram/client/types/bots/keyboard_button.py @@ -40,10 +40,14 @@ class KeyboardButton(PyrogramType): Available in private chats only. """ - def __init__(self, - text: str, - request_contact: bool = None, - request_location: bool = None): + __slots__ = ["text", "request_contact", "request_location"] + + def __init__( + self, + text: str, + request_contact: bool = None, + request_location: bool = None + ): super().__init__(None) self.text = str(text) @@ -71,8 +75,8 @@ class KeyboardButton(PyrogramType): # TODO: Enforce optional args mutual exclusiveness if self.request_contact: - return KeyboardButtonRequestPhone(self.text) + return KeyboardButtonRequestPhone(text=self.text) elif self.request_location: - return KeyboardButtonRequestGeoLocation(self.text) + return KeyboardButtonRequestGeoLocation(text=self.text) else: - return RawKeyboardButton(self.text) + return RawKeyboardButton(text=self.text) diff --git a/pyrogram/client/types/bots/reply_keyboard_markup.py b/pyrogram/client/types/bots/reply_keyboard_markup.py index afae236d..b0216803 100644 --- a/pyrogram/client/types/bots/reply_keyboard_markup.py +++ b/pyrogram/client/types/bots/reply_keyboard_markup.py @@ -49,11 +49,15 @@ class ReplyKeyboardMarkup(PyrogramType): select the new language. Other users in the group don't see the keyboard. """ - def __init__(self, - keyboard: List[List[Union[KeyboardButton, str]]], - resize_keyboard: bool = None, - one_time_keyboard: bool = None, - selective: bool = None): + __slots__ = ["keyboard", "resize_keyboard", "one_time_keyboard", "selective"] + + def __init__( + self, + keyboard: List[List[Union[KeyboardButton, str]]], + resize_keyboard: bool = None, + one_time_keyboard: bool = None, + selective: bool = None + ): super().__init__(None) self.keyboard = keyboard @@ -83,9 +87,11 @@ class ReplyKeyboardMarkup(PyrogramType): def write(self): return RawReplyKeyboardMarkup( rows=[KeyboardButtonRow( - [KeyboardButton(j).write() - if isinstance(j, str) else j.write() - for j in i] + buttons=[ + KeyboardButton(j).write() + if isinstance(j, str) else j.write() + for j in i + ] ) for i in self.keyboard], resize=self.resize_keyboard or None, single_use=self.one_time_keyboard or None, diff --git a/pyrogram/client/types/bots/reply_keyboard_remove.py b/pyrogram/client/types/bots/reply_keyboard_remove.py index 5b67fbb4..75f2a7b5 100644 --- a/pyrogram/client/types/bots/reply_keyboard_remove.py +++ b/pyrogram/client/types/bots/reply_keyboard_remove.py @@ -21,10 +21,9 @@ from ..pyrogram_type import PyrogramType class ReplyKeyboardRemove(PyrogramType): - """Upon receiving a message with this object, Telegram clients will remove the current custom keyboard and - display the default letter-keyboard. By default, custom keyboards are displayed until a new keyboard is sent - by a bot. An exception is made for one-time keyboards that are hidden immediately after the user presses a - button (see ReplyKeyboardMarkup). + """Upon receiving a message with this object, Telegram clients will remove the current custom keyboard and display the default letter-keyboard. + By default, custom keyboards are displayed until a new keyboard is sent by a bot. An exception is made for one-time + keyboards that are hidden immediately after the user presses a button (see ReplyKeyboardMarkup). Args: selective (``bool``, *optional*): @@ -35,8 +34,12 @@ class ReplyKeyboardRemove(PyrogramType): keyboard for that user, while still showing the keyboard with poll options to users who haven't voted yet. """ - def __init__(self, - selective: bool = None): + __slots__ = ["selective"] + + def __init__( + self, + selective: bool = None + ): super().__init__(None) self.selective = selective diff --git a/pyrogram/client/types/inline_mode/__init__.py b/pyrogram/client/types/inline_mode/__init__.py new file mode 100644 index 00000000..7a3b3023 --- /dev/null +++ b/pyrogram/client/types/inline_mode/__init__.py @@ -0,0 +1,25 @@ +# 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 .inline_query import InlineQuery +from .inline_query_result import InlineQueryResult +from .inline_query_result_article import InlineQueryResultArticle + +__all__ = [ + "InlineQuery", "InlineQueryResult", "InlineQueryResultArticle" +] diff --git a/pyrogram/client/types/inline_mode/inline_query.py b/pyrogram/client/types/inline_mode/inline_query.py new file mode 100644 index 00000000..9c1c02ac --- /dev/null +++ b/pyrogram/client/types/inline_mode/inline_query.py @@ -0,0 +1,152 @@ +# 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 .inline_query_result import InlineQueryResult +from ..messages_and_media import Location +from ..pyrogram_type import PyrogramType +from ..update import Update +from ..user_and_chats import User + + +class InlineQuery(PyrogramType, Update): + """This object represents an incoming inline query. + When the user sends an empty query, your bot could return some default or trending results. + + Args: + id (``str``): + Unique identifier for this query. + + from_user (:obj:`User `): + Sender. + + query (``str``): + Text of the query (up to 512 characters). + + offset (``str``): + Offset of the results to be returned, can be controlled by the bot. + + location (:obj:`Location `. *optional*): + Sender location, only for bots that request user location. + """ + __slots__ = ["id", "from_user", "query", "offset", "location"] + + def __init__( + self, + *, + client: "pyrogram.client.ext.BaseClient", + id: str, + from_user: User, + query: str, + offset: str, + location: Location = None + ): + super().__init__(client) + + self._client = client + self.id = id + self.from_user = from_user + self.query = query + self.offset = offset + self.location = location + + @staticmethod + def _parse(client, inline_query: types.UpdateBotInlineQuery, users: dict) -> "InlineQuery": + return InlineQuery( + id=str(inline_query.query_id), + from_user=User._parse(client, users[inline_query.user_id]), + query=inline_query.query, + offset=inline_query.offset, + location=Location( + longitude=inline_query.geo.long, + latitude=inline_query.geo.lat, + client=client + ) if inline_query.geo else None, + client=client + ) + + def answer( + self, + results: List[InlineQueryResult], + cache_time: int = 300, + is_personal: bool = None, + next_offset: str = "", + switch_pm_text: str = "", + switch_pm_parameter: str = "" + ): + """Bound method *answer* of :obj:`InlineQuery `. + + Use this method as a shortcut for: + + .. code-block:: python + + client.answer_inline_query( + inline_query.id, + results=[...] + ) + + Example: + .. code-block:: python + + inline_query.answer([...]) + + Args: + results (List of :obj:`InlineQueryResult `): + A list of results for the inline query. + + cache_time (``int``, *optional*): + The maximum amount of time in seconds that the result of the inline query may be cached on the server. + Defaults to 300. + + is_personal (``bool``, *optional*): + Pass True, if results may be cached on the server side only for the user that sent the query. + By default, results may be returned to any user who sends the same query. + + next_offset (``str``, *optional*): + Pass the offset that a client should send in the next query with the same text to receive more results. + Pass an empty string if there are no more results or if you don‘t support pagination. + Offset length can’t exceed 64 bytes. + + switch_pm_text (``str``, *optional*): + If passed, clients will display a button with specified text that switches the user to a private chat + with the bot and sends the bot a start message with the parameter switch_pm_parameter + + switch_pm_parameter (``str``, *optional*): + `Deep-linking `_ parameter for the /start message sent to + the bot when user presses the switch button. 1-64 characters, only A-Z, a-z, 0-9, _ and - are allowed. + + Example: An inline bot that sends YouTube videos can ask the user to connect the bot to their YouTube + account to adapt search results accordingly. To do this, it displays a "Connect your YouTube account" + button above the results, or even before showing any. The user presses the button, switches to a private + chat with the bot and, in doing so, passes a start parameter that instructs the bot to return an oauth + link. Once done, the bot can offer a switch_inline button so that the user can easily return to the chat + where they wanted to use the bot's inline capabilities. + """ + + return self._client.answer_inline_query( + inline_query_id=self.id, + results=results, + cache_time=cache_time, + is_personal=is_personal, + next_offset=next_offset, + switch_pm_text=switch_pm_text, + switch_pm_parameter=switch_pm_parameter + ) diff --git a/pyrogram/client/types/inline_mode/inline_query_result.py b/pyrogram/client/types/inline_mode/inline_query_result.py new file mode 100644 index 00000000..3e7fcb02 --- /dev/null +++ b/pyrogram/client/types/inline_mode/inline_query_result.py @@ -0,0 +1,59 @@ +# 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 ..pyrogram_type import PyrogramType + +"""- :obj:`InlineQueryResultCachedAudio` + - :obj:`InlineQueryResultCachedDocument` + - :obj:`InlineQueryResultCachedGif` + - :obj:`InlineQueryResultCachedMpeg4Gif` + - :obj:`InlineQueryResultCachedPhoto` + - :obj:`InlineQueryResultCachedSticker` + - :obj:`InlineQueryResultCachedVideo` + - :obj:`InlineQueryResultCachedVoice` + - :obj:`InlineQueryResultAudio` + - :obj:`InlineQueryResultContact` + - :obj:`InlineQueryResultGame` + - :obj:`InlineQueryResultDocument` + - :obj:`InlineQueryResultGif` + - :obj:`InlineQueryResultLocation` + - :obj:`InlineQueryResultMpeg4Gif` + - :obj:`InlineQueryResultPhoto` + - :obj:`InlineQueryResultVenue` + - :obj:`InlineQueryResultVideo` + - :obj:`InlineQueryResultVoice`""" + + +class InlineQueryResult(PyrogramType): + """This object represents one result of an inline query. + + Pyrogram currently supports results of the following 20 types: + + - :obj:`InlineQueryResultArticle` + """ + + __slots__ = ["type", "id"] + + def __init__(self, type: str, id: str): + super().__init__(None) + + self.type = type + self.id = id + + def write(self): + pass diff --git a/pyrogram/client/types/inline_mode/inline_query_result_article.py b/pyrogram/client/types/inline_mode/inline_query_result_article.py new file mode 100644 index 00000000..8d0089c3 --- /dev/null +++ b/pyrogram/client/types/inline_mode/inline_query_result_article.py @@ -0,0 +1,106 @@ +# 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 Any + +from pyrogram.api import types +from .inline_query_result import InlineQueryResult + + +class InlineQueryResultArticle(InlineQueryResult): + """Represents a link to an article or web page. + + TODO: Hide url? + + Args: + id (``str``): + Unique identifier for this result, 1-64 bytes. + + title (``str``): + Title for the result. + + input_message_content (:obj:`InputMessageContent `): + Content of the message to be sent. + + reply_markup (:obj:`InlineKeyboardMarkup `, *optional*): + Inline keyboard attached to the message. + + url (``str``, *optional*): + URL of the result. + + description (``str``, *optional*): + Short description of the result. + + thumb_url (``str``, *optional*): + Url of the thumbnail for the result. + + thumb_width (``int``, *optional*): + Thumbnail width. + + thumb_height (``int``, *optional*): + Thumbnail height. + """ + + __slots__ = [ + "title", "input_message_content", "reply_markup", "url", "description", "thumb_url", "thumb_width", + "thumb_height" + ] + + def __init__( + self, + id: Any, + title: str, + input_message_content, + reply_markup=None, + url: str = None, + description: str = None, + thumb_url: str = None, + thumb_width: int = 0, + thumb_height: int = 0 + ): + super().__init__("article", id) + + self.title = title + self.input_message_content = input_message_content + self.reply_markup = reply_markup + self.url = url + self.description = description + self.thumb_url = thumb_url + self.thumb_width = thumb_width + self.thumb_height = thumb_height + + def write(self): + return types.InputBotInlineResult( + id=str(self.id), + type=self.type, + send_message=self.input_message_content.write(self.reply_markup), + title=self.title, + description=self.description, + url=self.url, + thumb=types.InputWebDocument( + url=self.thumb_url, + size=0, + mime_type="image/jpeg", + attributes=[ + types.DocumentAttributeImageSize( + w=self.thumb_width, + h=self.thumb_height + ) + ] + ) if self.thumb_url else None + ) diff --git a/pyrogram/client/types/inline_mode/todo/inline_query_result_audio.py b/pyrogram/client/types/inline_mode/todo/inline_query_result_audio.py new file mode 100644 index 00000000..a67163c6 --- /dev/null +++ b/pyrogram/client/types/inline_mode/todo/inline_query_result_audio.py @@ -0,0 +1,71 @@ +# 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 pyrogram.client.types.pyrogram_type import PyrogramType + + +class InlineQueryResultAudio(PyrogramType): + """Represents a link to an mp3 audio file. By default, this audio file will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the audio. + + Attributes: + ID: ``0xb0700004`` + + Args: + type (``str``): + Type of the result, must be audio. + + id (``str``): + Unique identifier for this result, 1-64 bytes. + + audio_url (``str``): + A valid URL for the audio file. + + title (``str``): + Title. + + caption (``str``, optional): + Caption, 0-200 characters. + + parse_mode (``str``, optional): + Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. + + performer (``str``, optional): + Performer. + + audio_duration (``int`` ``32-bit``, optional): + Audio duration in seconds. + + reply_markup (:obj:`InlineKeyboardMarkup `, optional): + Inline keyboard attached to the message. + + input_message_content (:obj:`InputMessageContent `, optional): + Content of the message to be sent instead of the audio. + + """ + + def __init__(self, type: str, id: str, audio_url: str, title: str, caption: str = None, parse_mode: str = None, performer: str = None, audio_duration: int = None, reply_markup=None, input_message_content=None): + self.type = type # string + self.id = id # string + self.audio_url = audio_url # string + self.title = title # string + self.caption = caption # flags.0?string + self.parse_mode = parse_mode # flags.1?string + self.performer = performer # flags.2?string + self.audio_duration = audio_duration # flags.3?int + self.reply_markup = reply_markup # flags.4?InlineKeyboardMarkup + self.input_message_content = input_message_content # flags.5?InputMessageContent diff --git a/pyrogram/client/types/inline_mode/todo/inline_query_result_cached_audio.py b/pyrogram/client/types/inline_mode/todo/inline_query_result_cached_audio.py new file mode 100644 index 00000000..f6ed1f15 --- /dev/null +++ b/pyrogram/client/types/inline_mode/todo/inline_query_result_cached_audio.py @@ -0,0 +1,103 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-2018 Dan Tès +# +# This file is part of Pyrogram. +# +# Pyrogram is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrogram is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrogram. If not, see . + +import binascii +import struct + +from pyrogram.api import types +from pyrogram.errors import FileIdInvalid +from pyrogram.client.ext import utils, BaseClient +from pyrogram.client.style import HTML, Markdown +from pyrogram.client.types.pyrogram_type import PyrogramType + + +class InlineQueryResultCachedAudio(PyrogramType): + """Represents a link to an audio file stored on the Telegram servers. + By default, this audio file will be sent by the user. Alternatively, you can use *input_message_content* to send a + message with the specified content instead of the audio. + + Args: + id (``str``): + Unique identifier for this result, 1-64 bytes. + + audio_file_id (``str``): + A valid file identifier for the audio file. + + caption (``str``, *optional*): + Caption, 0-200 characters. + + parse_mode (``str``, *optional*): + Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in + the media caption. + + reply_markup (:obj:`InlineKeyboardMarkup `, *optional*): + Inline keyboard attached to the message. + + input_message_content (:obj:`InputMessageContent `, *optional*): + Content of the message to be sent instead of the audio. + + """ + + def __init__( + self, + id: str, + audio_file_id: str, + caption: str = "", + parse_mode: str = "", + reply_markup=None, + input_message_content=None + ): + self.id = id + self.audio_file_id = audio_file_id + self.caption = caption + self.parse_mode = parse_mode + self.reply_markup = reply_markup + self.input_message_content = input_message_content + + self.style = HTML() if parse_mode.lower() == "html" else Markdown() + + def write(self): + try: + decoded = utils.decode(self.audio_file_id) + fmt = " 24 else " +# +# 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.client.types.pyrogram_type import PyrogramType + + +class InlineQueryResultCachedDocument(PyrogramType): + """Represents a link to a file stored on the Telegram servers. By default, this file will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the file. + + Attributes: + ID: ``0xb0700015`` + + Args: + type (``str``): + Type of the result, must be document. + + id (``str``): + Unique identifier for this result, 1-64 bytes. + + title (``str``): + Title for the result. + + document_file_id (``str``): + A valid file identifier for the file. + + description (``str``, optional): + Short description of the result. + + caption (``str``, optional): + Caption of the document to be sent, 0-200 characters. + + parse_mode (``str``, optional): + Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. + + reply_markup (:obj:`InlineKeyboardMarkup `, optional): + Inline keyboard attached to the message. + + input_message_content (:obj:`InputMessageContent `, optional): + Content of the message to be sent instead of the file. + + """ + ID = 0xb0700015 + + def __init__(self, type: str, id: str, title: str, document_file_id: str, description: str = None, caption: str = None, parse_mode: str = None, reply_markup=None, input_message_content=None): + self.type = type # string + self.id = id # string + self.title = title # string + self.document_file_id = document_file_id # string + self.description = description # flags.0?string + self.caption = caption # flags.1?string + self.parse_mode = parse_mode # flags.2?string + self.reply_markup = reply_markup # flags.3?InlineKeyboardMarkup + self.input_message_content = input_message_content # flags.4?InputMessageContent diff --git a/pyrogram/client/types/inline_mode/todo/inline_query_result_cached_gif.py b/pyrogram/client/types/inline_mode/todo/inline_query_result_cached_gif.py new file mode 100644 index 00000000..4c457873 --- /dev/null +++ b/pyrogram/client/types/inline_mode/todo/inline_query_result_cached_gif.py @@ -0,0 +1,64 @@ +# 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 pyrogram.client.types.pyrogram_type import PyrogramType + + +class InlineQueryResultCachedGif(PyrogramType): + """Represents a link to an animated GIF file stored on the Telegram servers. By default, this animated GIF file will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with specified content instead of the animation. + + Attributes: + ID: ``0xb0700012`` + + Args: + type (``str``): + Type of the result, must be gif. + + id (``str``): + Unique identifier for this result, 1-64 bytes. + + gif_file_id (``str``): + A valid file identifier for the GIF file. + + title (``str``, optional): + Title for the result. + + caption (``str``, optional): + Caption of the GIF file to be sent, 0-200 characters. + + parse_mode (``str``, optional): + Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. + + reply_markup (:obj:`InlineKeyboardMarkup `, optional): + Inline keyboard attached to the message. + + input_message_content (:obj:`InputMessageContent `, optional): + Content of the message to be sent instead of the GIF animation. + + """ + ID = 0xb0700012 + + def __init__(self, type: str, id: str, gif_file_id: str, title: str = None, caption: str = None, parse_mode: str = None, reply_markup=None, input_message_content=None): + self.type = type # string + self.id = id # string + self.gif_file_id = gif_file_id # string + self.title = title # flags.0?string + self.caption = caption # flags.1?string + self.parse_mode = parse_mode # flags.2?string + self.reply_markup = reply_markup # flags.3?InlineKeyboardMarkup + self.input_message_content = input_message_content # flags.4?InputMessageContent diff --git a/pyrogram/client/types/inline_mode/todo/inline_query_result_cached_mpeg4_gif.py b/pyrogram/client/types/inline_mode/todo/inline_query_result_cached_mpeg4_gif.py new file mode 100644 index 00000000..93ec1efb --- /dev/null +++ b/pyrogram/client/types/inline_mode/todo/inline_query_result_cached_mpeg4_gif.py @@ -0,0 +1,64 @@ +# 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 pyrogram.client.types.pyrogram_type import PyrogramType + + +class InlineQueryResultCachedMpeg4Gif(PyrogramType): + """Represents a link to a video animation (H.264/MPEG-4 AVC video without sound) stored on the Telegram servers. By default, this animated MPEG-4 file will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the animation. + + Attributes: + ID: ``0xb0700013`` + + Args: + type (``str``): + Type of the result, must be mpeg4_gif. + + id (``str``): + Unique identifier for this result, 1-64 bytes. + + mpeg4_file_id (``str``): + A valid file identifier for the MP4 file. + + title (``str``, optional): + Title for the result. + + caption (``str``, optional): + Caption of the MPEG-4 file to be sent, 0-200 characters. + + parse_mode (``str``, optional): + Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. + + reply_markup (:obj:`InlineKeyboardMarkup `, optional): + Inline keyboard attached to the message. + + input_message_content (:obj:`InputMessageContent `, optional): + Content of the message to be sent instead of the video animation. + + """ + ID = 0xb0700013 + + def __init__(self, type: str, id: str, mpeg4_file_id: str, title: str = None, caption: str = None, parse_mode: str = None, reply_markup=None, input_message_content=None): + self.type = type # string + self.id = id # string + self.mpeg4_file_id = mpeg4_file_id # string + self.title = title # flags.0?string + self.caption = caption # flags.1?string + self.parse_mode = parse_mode # flags.2?string + self.reply_markup = reply_markup # flags.3?InlineKeyboardMarkup + self.input_message_content = input_message_content # flags.4?InputMessageContent diff --git a/pyrogram/client/types/inline_mode/todo/inline_query_result_cached_photo.py b/pyrogram/client/types/inline_mode/todo/inline_query_result_cached_photo.py new file mode 100644 index 00000000..ee6b2654 --- /dev/null +++ b/pyrogram/client/types/inline_mode/todo/inline_query_result_cached_photo.py @@ -0,0 +1,68 @@ +# 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 pyrogram.client.types.pyrogram_type import PyrogramType + + +class InlineQueryResultCachedPhoto(PyrogramType): + """Represents a link to a photo stored on the Telegram servers. By default, this photo will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the photo. + + Attributes: + ID: ``0xb0700011`` + + Args: + type (``str``): + Type of the result, must be photo. + + id (``str``): + Unique identifier for this result, 1-64 bytes. + + photo_file_id (``str``): + A valid file identifier of the photo. + + title (``str``, optional): + Title for the result. + + description (``str``, optional): + Short description of the result. + + caption (``str``, optional): + Caption of the photo to be sent, 0-200 characters. + + parse_mode (``str``, optional): + Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. + + reply_markup (:obj:`InlineKeyboardMarkup `, optional): + Inline keyboard attached to the message. + + input_message_content (:obj:`InputMessageContent `, optional): + Content of the message to be sent instead of the photo. + + """ + ID = 0xb0700011 + + def __init__(self, type: str, id: str, photo_file_id: str, title: str = None, description: str = None, caption: str = None, parse_mode: str = None, reply_markup=None, input_message_content=None): + self.type = type # string + self.id = id # string + self.photo_file_id = photo_file_id # string + self.title = title # flags.0?string + self.description = description # flags.1?string + self.caption = caption # flags.2?string + self.parse_mode = parse_mode # flags.3?string + self.reply_markup = reply_markup # flags.4?InlineKeyboardMarkup + self.input_message_content = input_message_content # flags.5?InputMessageContent diff --git a/pyrogram/client/types/inline_mode/todo/inline_query_result_cached_sticker.py b/pyrogram/client/types/inline_mode/todo/inline_query_result_cached_sticker.py new file mode 100644 index 00000000..6142b1fa --- /dev/null +++ b/pyrogram/client/types/inline_mode/todo/inline_query_result_cached_sticker.py @@ -0,0 +1,52 @@ +# 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 pyrogram.client.types.pyrogram_type import PyrogramType + + +class InlineQueryResultCachedSticker(PyrogramType): + """Represents a link to a sticker stored on the Telegram servers. By default, this sticker will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the sticker. + + Attributes: + ID: ``0xb0700014`` + + Args: + type (``str``): + Type of the result, must be sticker. + + id (``str``): + Unique identifier for this result, 1-64 bytes. + + sticker_file_id (``str``): + A valid file identifier of the sticker. + + reply_markup (:obj:`InlineKeyboardMarkup `, optional): + Inline keyboard attached to the message. + + input_message_content (:obj:`InputMessageContent `, optional): + Content of the message to be sent instead of the sticker. + + """ + ID = 0xb0700014 + + def __init__(self, type: str, id: str, sticker_file_id: str, reply_markup=None, input_message_content=None): + self.type = type # string + self.id = id # string + self.sticker_file_id = sticker_file_id # string + self.reply_markup = reply_markup # flags.0?InlineKeyboardMarkup + self.input_message_content = input_message_content # flags.1?InputMessageContent diff --git a/pyrogram/client/types/inline_mode/todo/inline_query_result_cached_video.py b/pyrogram/client/types/inline_mode/todo/inline_query_result_cached_video.py new file mode 100644 index 00000000..8c00c61a --- /dev/null +++ b/pyrogram/client/types/inline_mode/todo/inline_query_result_cached_video.py @@ -0,0 +1,68 @@ +# 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 pyrogram.client.types.pyrogram_type import PyrogramType + + +class InlineQueryResultCachedVideo(PyrogramType): + """Represents a link to a video file stored on the Telegram servers. By default, this video file will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the video. + + Attributes: + ID: ``0xb0700016`` + + Args: + type (``str``): + Type of the result, must be video. + + id (``str``): + Unique identifier for this result, 1-64 bytes. + + video_file_id (``str``): + A valid file identifier for the video file. + + title (``str``): + Title for the result. + + description (``str``, optional): + Short description of the result. + + caption (``str``, optional): + Caption of the video to be sent, 0-200 characters. + + parse_mode (``str``, optional): + Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. + + reply_markup (:obj:`InlineKeyboardMarkup `, optional): + Inline keyboard attached to the message. + + input_message_content (:obj:`InputMessageContent `, optional): + Content of the message to be sent instead of the video. + + """ + ID = 0xb0700016 + + def __init__(self, type: str, id: str, video_file_id: str, title: str, description: str = None, caption: str = None, parse_mode: str = None, reply_markup=None, input_message_content=None): + self.type = type # string + self.id = id # string + self.video_file_id = video_file_id # string + self.title = title # string + self.description = description # flags.0?string + self.caption = caption # flags.1?string + self.parse_mode = parse_mode # flags.2?string + self.reply_markup = reply_markup # flags.3?InlineKeyboardMarkup + self.input_message_content = input_message_content # flags.4?InputMessageContent diff --git a/pyrogram/client/types/inline_mode/todo/inline_query_result_cached_voice.py b/pyrogram/client/types/inline_mode/todo/inline_query_result_cached_voice.py new file mode 100644 index 00000000..741df389 --- /dev/null +++ b/pyrogram/client/types/inline_mode/todo/inline_query_result_cached_voice.py @@ -0,0 +1,64 @@ +# 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 pyrogram.client.types.pyrogram_type import PyrogramType + + +class InlineQueryResultCachedVoice(PyrogramType): + """Represents a link to a voice message stored on the Telegram servers. By default, this voice message will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the voice message. + + Attributes: + ID: ``0xb0700017`` + + Args: + type (``str``): + Type of the result, must be voice. + + id (``str``): + Unique identifier for this result, 1-64 bytes. + + voice_file_id (``str``): + A valid file identifier for the voice message. + + title (``str``): + Voice message title. + + caption (``str``, optional): + Caption, 0-200 characters. + + parse_mode (``str``, optional): + Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. + + reply_markup (:obj:`InlineKeyboardMarkup `, optional): + Inline keyboard attached to the message. + + input_message_content (:obj:`InputMessageContent `, optional): + Content of the message to be sent instead of the voice message. + + """ + ID = 0xb0700017 + + def __init__(self, type: str, id: str, voice_file_id: str, title: str, caption: str = None, parse_mode: str = None, reply_markup=None, input_message_content=None): + self.type = type # string + self.id = id # string + self.voice_file_id = voice_file_id # string + self.title = title # string + self.caption = caption # flags.0?string + self.parse_mode = parse_mode # flags.1?string + self.reply_markup = reply_markup # flags.2?InlineKeyboardMarkup + self.input_message_content = input_message_content # flags.3?InputMessageContent diff --git a/pyrogram/client/types/inline_mode/todo/inline_query_result_contact.py b/pyrogram/client/types/inline_mode/todo/inline_query_result_contact.py new file mode 100644 index 00000000..e26af4ea --- /dev/null +++ b/pyrogram/client/types/inline_mode/todo/inline_query_result_contact.py @@ -0,0 +1,76 @@ +# 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 pyrogram.client.types.pyrogram_type import PyrogramType + + +class InlineQueryResultContact(PyrogramType): + """Represents a contact with a phone number. By default, this contact will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the contact. + + Attributes: + ID: ``0xb0700009`` + + Args: + type (``str``): + Type of the result, must be contact. + + id (``str``): + Unique identifier for this result, 1-64 Bytes. + + phone_number (``str``): + Contact's phone number. + + first_name (``str``): + Contact's first name. + + last_name (``str``, optional): + Contact's last name. + + vcard (``str``, optional): + Additional data about the contact in the form of a vCard, 0-2048 bytes. + + reply_markup (:obj:`InlineKeyboardMarkup `, optional): + Inline keyboard attached to the message. + + input_message_content (:obj:`InputMessageContent `, optional): + Content of the message to be sent instead of the contact. + + thumb_url (``str``, optional): + Url of the thumbnail for the result. + + thumb_width (``int`` ``32-bit``, optional): + Thumbnail width. + + thumb_height (``int`` ``32-bit``, optional): + Thumbnail height. + + """ + ID = 0xb0700009 + + def __init__(self, type: str, id: str, phone_number: str, first_name: str, last_name: str = None, vcard: str = None, reply_markup=None, input_message_content=None, thumb_url: str = None, thumb_width: int = None, thumb_height: int = None): + self.type = type # string + self.id = id # string + self.phone_number = phone_number # string + self.first_name = first_name # string + self.last_name = last_name # flags.0?string + self.vcard = vcard # flags.1?string + self.reply_markup = reply_markup # flags.2?InlineKeyboardMarkup + self.input_message_content = input_message_content # flags.3?InputMessageContent + self.thumb_url = thumb_url # flags.4?string + self.thumb_width = thumb_width # flags.5?int + self.thumb_height = thumb_height # flags.6?int diff --git a/pyrogram/client/types/inline_mode/todo/inline_query_result_document.py b/pyrogram/client/types/inline_mode/todo/inline_query_result_document.py new file mode 100644 index 00000000..93b8fcae --- /dev/null +++ b/pyrogram/client/types/inline_mode/todo/inline_query_result_document.py @@ -0,0 +1,84 @@ +# 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 pyrogram.client.types.pyrogram_type import PyrogramType + + +class InlineQueryResultDocument(PyrogramType): + """Represents a link to a file. By default, this file will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the file. Currently, only .PDF and .ZIP files can be sent using this method. + + Attributes: + ID: ``0xb0700006`` + + Args: + type (``str``): + Type of the result, must be document. + + id (``str``): + Unique identifier for this result, 1-64 bytes. + + title (``str``): + Title for the result. + + document_url (``str``, optional): + Caption of the document to be sent, 0-200 characters. + + mime_type (``str``, optional): + Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. + + caption (``str``): + A valid URL for the file. + + parse_mode (``str``): + Mime type of the content of the file, either "application/pdf" or "application/zip". + + description (``str``, optional): + Short description of the result. + + reply_markup (:obj:`InlineKeyboardMarkup `, optional): + Inline keyboard attached to the message. + + input_message_content (:obj:`InputMessageContent `, optional): + Content of the message to be sent instead of the file. + + thumb_url (``str``, optional): + URL of the thumbnail (jpeg only) for the file. + + thumb_width (``int`` ``32-bit``, optional): + Thumbnail width. + + thumb_height (``int`` ``32-bit``, optional): + Thumbnail height. + + """ + ID = 0xb0700006 + + def __init__(self, type: str, id: str, title: str, document_url: str, mime_type: str, caption: str = None, parse_mode: str = None, description: str = None, reply_markup=None, input_message_content=None, thumb_url: str = None, thumb_width: int = None, thumb_height: int = None): + self.type = type # string + self.id = id # string + self.title = title # string + self.caption = caption # flags.0?string + self.parse_mode = parse_mode # flags.1?string + self.document_url = document_url # string + self.mime_type = mime_type # string + self.description = description # flags.2?string + self.reply_markup = reply_markup # flags.3?InlineKeyboardMarkup + self.input_message_content = input_message_content # flags.4?InputMessageContent + self.thumb_url = thumb_url # flags.5?string + self.thumb_width = thumb_width # flags.6?int + self.thumb_height = thumb_height # flags.7?int diff --git a/pyrogram/client/types/inline_mode/todo/inline_query_result_game.py b/pyrogram/client/types/inline_mode/todo/inline_query_result_game.py new file mode 100644 index 00000000..3e7cfb73 --- /dev/null +++ b/pyrogram/client/types/inline_mode/todo/inline_query_result_game.py @@ -0,0 +1,48 @@ +# 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 pyrogram.client.types.pyrogram_type import PyrogramType + + +class InlineQueryResultGame(PyrogramType): + """Represents a Game. + + Attributes: + ID: ``0xb0700010`` + + Args: + type (``str``): + Type of the result, must be game. + + id (``str``): + Unique identifier for this result, 1-64 bytes. + + game_short_name (``str``): + Short name of the game. + + reply_markup (:obj:`InlineKeyboardMarkup `, optional): + Inline keyboard attached to the message. + + """ + ID = 0xb0700010 + + def __init__(self, type: str, id: str, game_short_name: str, reply_markup=None): + self.type = type # string + self.id = id # string + self.game_short_name = game_short_name # string + self.reply_markup = reply_markup # flags.0?InlineKeyboardMarkup diff --git a/pyrogram/client/types/inline_mode/todo/inline_query_result_gif.py b/pyrogram/client/types/inline_mode/todo/inline_query_result_gif.py new file mode 100644 index 00000000..13f4fc18 --- /dev/null +++ b/pyrogram/client/types/inline_mode/todo/inline_query_result_gif.py @@ -0,0 +1,80 @@ +# 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 pyrogram.client.types.pyrogram_type import PyrogramType + + +class InlineQueryResultGif(PyrogramType): + """Represents a link to an animated GIF file. By default, this animated GIF file will be sent by the user with optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the animation. + + Attributes: + ID: ``0xb0700001`` + + Args: + type (``str``): + Type of the result, must be gif. + + id (``str``): + Unique identifier for this result, 1-64 bytes. + + gif_url (``str``): + A valid URL for the GIF file. File size must not exceed 1MB. + + thumb_url (``str``, optional): + Width of the GIF. + + gif_width (``int`` ``32-bit``, optional): + Height of the GIF. + + gif_height (``int`` ``32-bit``, optional): + Duration of the GIF. + + gif_duration (``int`` ``32-bit``): + URL of the static thumbnail for the result (jpeg or gif). + + title (``str``, optional): + Title for the result. + + caption (``str``, optional): + Caption of the GIF file to be sent, 0-200 characters. + + parse_mode (``str``, optional): + Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. + + reply_markup (:obj:`InlineKeyboardMarkup `, optional): + Inline keyboard attached to the message. + + input_message_content (:obj:`InputMessageContent `, optional): + Content of the message to be sent instead of the GIF animation. + + """ + ID = 0xb0700001 + + def __init__(self, type: str, id: str, gif_url: str, thumb_url: str, gif_width: int = None, gif_height: int = None, gif_duration: int = None, title: str = None, caption: str = None, parse_mode: str = None, reply_markup=None, input_message_content=None): + self.type = type # string + self.id = id # string + self.gif_url = gif_url # string + self.gif_width = gif_width # flags.0?int + self.gif_height = gif_height # flags.1?int + self.gif_duration = gif_duration # flags.2?int + self.thumb_url = thumb_url # string + self.title = title # flags.3?string + self.caption = caption # flags.4?string + self.parse_mode = parse_mode # flags.5?string + self.reply_markup = reply_markup # flags.6?InlineKeyboardMarkup + self.input_message_content = input_message_content # flags.7?InputMessageContent diff --git a/pyrogram/client/types/inline_mode/todo/inline_query_result_location.py b/pyrogram/client/types/inline_mode/todo/inline_query_result_location.py new file mode 100644 index 00000000..176591d2 --- /dev/null +++ b/pyrogram/client/types/inline_mode/todo/inline_query_result_location.py @@ -0,0 +1,76 @@ +# 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 pyrogram.client.types.pyrogram_type import PyrogramType + + +class InlineQueryResultLocation(PyrogramType): + """Represents a location on a map. By default, the location will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the location. + + Attributes: + ID: ``0xb0700007`` + + Args: + type (``str``): + Type of the result, must be location. + + id (``str``): + Unique identifier for this result, 1-64 Bytes. + + latitude (``float`` ``64-bit``): + Location latitude in degrees. + + longitude (``float`` ``64-bit``): + Location longitude in degrees. + + title (``str``): + Location title. + + live_period (``int`` ``32-bit``, optional): + Period in seconds for which the location can be updated, should be between 60 and 86400. + + reply_markup (:obj:`InlineKeyboardMarkup `, optional): + Inline keyboard attached to the message. + + input_message_content (:obj:`InputMessageContent `, optional): + Content of the message to be sent instead of the location. + + thumb_url (``str``, optional): + Url of the thumbnail for the result. + + thumb_width (``int`` ``32-bit``, optional): + Thumbnail width. + + thumb_height (``int`` ``32-bit``, optional): + Thumbnail height. + + """ + ID = 0xb0700007 + + def __init__(self, type: str, id: str, latitude: float, longitude: float, title: str, live_period: int = None, reply_markup=None, input_message_content=None, thumb_url: str = None, thumb_width: int = None, thumb_height: int = None): + self.type = type # string + self.id = id # string + self.latitude = latitude # double + self.longitude = longitude # double + self.title = title # string + self.live_period = live_period # flags.0?int + self.reply_markup = reply_markup # flags.1?InlineKeyboardMarkup + self.input_message_content = input_message_content # flags.2?InputMessageContent + self.thumb_url = thumb_url # flags.3?string + self.thumb_width = thumb_width # flags.4?int + self.thumb_height = thumb_height # flags.5?int diff --git a/pyrogram/client/types/inline_mode/todo/inline_query_result_mpeg4_gif.py b/pyrogram/client/types/inline_mode/todo/inline_query_result_mpeg4_gif.py new file mode 100644 index 00000000..37aa8986 --- /dev/null +++ b/pyrogram/client/types/inline_mode/todo/inline_query_result_mpeg4_gif.py @@ -0,0 +1,80 @@ +# 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 pyrogram.client.types.pyrogram_type import PyrogramType + + +class InlineQueryResultMpeg4Gif(PyrogramType): + """Represents a link to a video animation (H.264/MPEG-4 AVC video without sound). By default, this animated MPEG-4 file will be sent by the user with optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the animation. + + Attributes: + ID: ``0xb0700002`` + + Args: + type (``str``): + Type of the result, must be mpeg4_gif. + + id (``str``): + Unique identifier for this result, 1-64 bytes. + + mpeg4_url (``str``): + A valid URL for the MP4 file. File size must not exceed 1MB. + + thumb_url (``str``, optional): + Video width. + + mpeg4_width (``int`` ``32-bit``, optional): + Video height. + + mpeg4_height (``int`` ``32-bit``, optional): + Video duration. + + mpeg4_duration (``int`` ``32-bit``): + URL of the static thumbnail (jpeg or gif) for the result. + + title (``str``, optional): + Title for the result. + + caption (``str``, optional): + Caption of the MPEG-4 file to be sent, 0-200 characters. + + parse_mode (``str``, optional): + Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. + + reply_markup (:obj:`InlineKeyboardMarkup `, optional): + Inline keyboard attached to the message. + + input_message_content (:obj:`InputMessageContent `, optional): + Content of the message to be sent instead of the video animation. + + """ + ID = 0xb0700002 + + def __init__(self, type: str, id: str, mpeg4_url: str, thumb_url: str, mpeg4_width: int = None, mpeg4_height: int = None, mpeg4_duration: int = None, title: str = None, caption: str = None, parse_mode: str = None, reply_markup=None, input_message_content=None): + self.type = type # string + self.id = id # string + self.mpeg4_url = mpeg4_url # string + self.mpeg4_width = mpeg4_width # flags.0?int + self.mpeg4_height = mpeg4_height # flags.1?int + self.mpeg4_duration = mpeg4_duration # flags.2?int + self.thumb_url = thumb_url # string + self.title = title # flags.3?string + self.caption = caption # flags.4?string + self.parse_mode = parse_mode # flags.5?string + self.reply_markup = reply_markup # flags.6?InlineKeyboardMarkup + self.input_message_content = input_message_content # flags.7?InputMessageContent diff --git a/pyrogram/client/types/inline_mode/todo/inline_query_result_photo.py b/pyrogram/client/types/inline_mode/todo/inline_query_result_photo.py new file mode 100644 index 00000000..2ba7c312 --- /dev/null +++ b/pyrogram/client/types/inline_mode/todo/inline_query_result_photo.py @@ -0,0 +1,126 @@ +# 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 pyrogram.api import types +from pyrogram.client.style import HTML, Markdown +from pyrogram.client.types.pyrogram_type import PyrogramType + + +class InlineQueryResultPhoto(PyrogramType): + """Represents a link to a photo. By default, this photo will be sent by the user with optional caption. + Alternatively, you can use input_message_content to send a message with the specified content instead of the photo. + + Args: + id (``str``): + Unique identifier for this result, 1-64 bytes. + + photo_url (``str``): + A valid URL of the photo. Photo must be in jpeg format. Photo size must not exceed 5MB. + + thumb_url (``str``): + URL of the thumbnail for the photo. + + photo_width (``int``, *optional*): + Width of the photo. + + photo_height (``int``, *optional*): + Height of the photo. + + title (``str``, *optional*): + Title for the result. + + description (``str``, *optional*): + Short description of the result. + + caption (``str``, *optional*): + Caption of the photo to be sent, 0-200 characters. + + parse_mode (``str``, *optional*): + Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in + the media caption. + + reply_markup (:obj:`InlineKeyboardMarkup `, *optional*): + Inline keyboard attached to the message. + + input_message_content (:obj:`InputMessageContent `, *optional*): + Content of the message to be sent instead of the photo. + + """ + + def __init__( + self, + id: str, + photo_url: str, + thumb_url: str, + photo_width: int = 0, + photo_height: int = 0, + title: str = None, + description: str = None, + caption: str = "", + parse_mode: str = "", + reply_markup=None, + input_message_content=None + ): + self.id = id # string + self.photo_url = photo_url # string + self.thumb_url = thumb_url # string + self.photo_width = photo_width # flags.0?int + self.photo_height = photo_height # flags.1?int + self.title = title # flags.2?string + self.description = description # flags.3?string + self.caption = caption # flags.4?string + self.parse_mode = parse_mode # flags.5?string + self.reply_markup = reply_markup # flags.6?InlineKeyboardMarkup + self.input_message_content = input_message_content # flags.7?InputMessageContent + + self.style = HTML() if parse_mode.lower() == "html" else Markdown() + + def write(self): + return types.InputBotInlineResult( + id=self.id, + type="photo", + send_message=types.InputBotInlineMessageMediaAuto( + reply_markup=self.reply_markup.write() if self.reply_markup else None, + **self.style.parse(self.caption) + ), + title=self.title, + description=self.description, + url=self.photo_url, + thumb=types.InputWebDocument( + url=self.thumb_url, + size=0, + mime_type="image/jpeg", + attributes=[ + types.DocumentAttributeImageSize( + w=0, + h=0 + ) + ] + ), + content=types.InputWebDocument( + url=self.thumb_url, + size=0, + mime_type="image/jpeg", + attributes=[ + types.DocumentAttributeImageSize( + w=self.photo_width, + h=self.photo_height + ) + ] + ) + ) diff --git a/pyrogram/client/types/inline_mode/todo/inline_query_result_venue.py b/pyrogram/client/types/inline_mode/todo/inline_query_result_venue.py new file mode 100644 index 00000000..23ddfc35 --- /dev/null +++ b/pyrogram/client/types/inline_mode/todo/inline_query_result_venue.py @@ -0,0 +1,84 @@ +# 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 pyrogram.client.types.pyrogram_type import PyrogramType + + +class InlineQueryResultVenue(PyrogramType): + """Represents a venue. By default, the venue will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the venue. + + Attributes: + ID: ``0xb0700008`` + + Args: + type (``str``): + Type of the result, must be venue. + + id (``str``): + Unique identifier for this result, 1-64 Bytes. + + latitude (``float`` ``64-bit``): + Latitude of the venue location in degrees. + + longitude (``float`` ``64-bit``): + Longitude of the venue location in degrees. + + title (``str``): + Title of the venue. + + address (``str``): + Address of the venue. + + foursquare_id (``str``, optional): + Foursquare identifier of the venue if known. + + foursquare_type (``str``, optional): + Foursquare type of the venue, if known. (For example, "arts_entertainment/default", "arts_entertainment/aquarium" or "food/icecream".). + + reply_markup (:obj:`InlineKeyboardMarkup `, optional): + Inline keyboard attached to the message. + + input_message_content (:obj:`InputMessageContent `, optional): + Content of the message to be sent instead of the venue. + + thumb_url (``str``, optional): + Url of the thumbnail for the result. + + thumb_width (``int`` ``32-bit``, optional): + Thumbnail width. + + thumb_height (``int`` ``32-bit``, optional): + Thumbnail height. + + """ + ID = 0xb0700008 + + def __init__(self, type: str, id: str, latitude: float, longitude: float, title: str, address: str, foursquare_id: str = None, foursquare_type: str = None, reply_markup=None, input_message_content=None, thumb_url: str = None, thumb_width: int = None, thumb_height: int = None): + self.type = type # string + self.id = id # string + self.latitude = latitude # double + self.longitude = longitude # double + self.title = title # string + self.address = address # string + self.foursquare_id = foursquare_id # flags.0?string + self.foursquare_type = foursquare_type # flags.1?string + self.reply_markup = reply_markup # flags.2?InlineKeyboardMarkup + self.input_message_content = input_message_content # flags.3?InputMessageContent + self.thumb_url = thumb_url # flags.4?string + self.thumb_width = thumb_width # flags.5?int + self.thumb_height = thumb_height # flags.6?int diff --git a/pyrogram/client/types/inline_mode/todo/inline_query_result_video.py b/pyrogram/client/types/inline_mode/todo/inline_query_result_video.py new file mode 100644 index 00000000..9b1723e1 --- /dev/null +++ b/pyrogram/client/types/inline_mode/todo/inline_query_result_video.py @@ -0,0 +1,88 @@ +# 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 pyrogram.client.types.pyrogram_type import PyrogramType + + +class InlineQueryResultVideo(PyrogramType): + """Represents a link to a page containing an embedded video player or a video file. By default, this video file will be sent by the user with an optional caption. Alternatively, you can use input_message_content to send a message with the specified content instead of the video. + + Attributes: + ID: ``0xb0700003`` + + Args: + type (``str``): + Type of the result, must be video. + + id (``str``): + Unique identifier for this result, 1-64 bytes. + + video_url (``str``): + A valid URL for the embedded video player or video file. + + mime_type (``str``): + Mime type of the content of video url, "text/html" or "video/mp4". + + thumb_url (``str``): + URL of the thumbnail (jpeg only) for the video. + + title (``str``): + Title for the result. + + caption (``str``, optional): + Caption of the video to be sent, 0-200 characters. + + parse_mode (``str``, optional): + Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. + + video_width (``int`` ``32-bit``, optional): + Video width. + + video_height (``int`` ``32-bit``, optional): + Video height. + + video_duration (``int`` ``32-bit``, optional): + Video duration in seconds. + + description (``str``, optional): + Short description of the result. + + reply_markup (:obj:`InlineKeyboardMarkup `, optional): + Inline keyboard attached to the message. + + input_message_content (:obj:`InputMessageContent `, optional): + Content of the message to be sent instead of the video. This field is required if InlineQueryResultVideo is used to send an HTML-page as a result (e.g., a YouTube video). + + """ + ID = 0xb0700003 + + def __init__(self, type: str, id: str, video_url: str, mime_type: str, thumb_url: str, title: str, caption: str = None, parse_mode: str = None, video_width: int = None, video_height: int = None, video_duration: int = None, description: str = None, reply_markup=None, input_message_content=None): + self.type = type # string + self.id = id # string + self.video_url = video_url # string + self.mime_type = mime_type # string + self.thumb_url = thumb_url # string + self.title = title # string + self.caption = caption # flags.0?string + self.parse_mode = parse_mode # flags.1?string + self.video_width = video_width # flags.2?int + self.video_height = video_height # flags.3?int + self.video_duration = video_duration # flags.4?int + self.description = description # flags.5?string + self.reply_markup = reply_markup # flags.6?InlineKeyboardMarkup + self.input_message_content = input_message_content # flags.7?InputMessageContent diff --git a/pyrogram/client/types/inline_mode/todo/inline_query_result_voice.py b/pyrogram/client/types/inline_mode/todo/inline_query_result_voice.py new file mode 100644 index 00000000..188063ec --- /dev/null +++ b/pyrogram/client/types/inline_mode/todo/inline_query_result_voice.py @@ -0,0 +1,68 @@ +# 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 pyrogram.client.types.pyrogram_type import PyrogramType + + +class InlineQueryResultVoice(PyrogramType): + """Represents a link to a voice recording in an .ogg container encoded with OPUS. By default, this voice recording will be sent by the user. Alternatively, you can use input_message_content to send a message with the specified content instead of the the voice message. + + Attributes: + ID: ``0xb0700005`` + + Args: + type (``str``): + Type of the result, must be voice. + + id (``str``): + Unique identifier for this result, 1-64 bytes. + + voice_url (``str``): + A valid URL for the voice recording. + + title (``str``): + Recording title. + + caption (``str``, optional): + Caption, 0-200 characters. + + parse_mode (``str``, optional): + Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in the media caption. + + voice_duration (``int`` ``32-bit``, optional): + Recording duration in seconds. + + reply_markup (:obj:`InlineKeyboardMarkup `, optional): + Inline keyboard attached to the message. + + input_message_content (:obj:`InputMessageContent `, optional): + Content of the message to be sent instead of the voice recording. + + """ + ID = 0xb0700005 + + def __init__(self, type: str, id: str, voice_url: str, title: str, caption: str = None, parse_mode: str = None, voice_duration: int = None, reply_markup=None, input_message_content=None): + self.type = type # string + self.id = id # string + self.voice_url = voice_url # string + self.title = title # string + self.caption = caption # flags.0?string + self.parse_mode = parse_mode # flags.1?string + self.voice_duration = voice_duration # flags.2?int + self.reply_markup = reply_markup # flags.3?InlineKeyboardMarkup + self.input_message_content = input_message_content # flags.4?InputMessageContent diff --git a/pyrogram/client/types/input_media/__init__.py b/pyrogram/client/types/input_media/__init__.py index e2e0b0f6..c97b9539 100644 --- a/pyrogram/client/types/input_media/__init__.py +++ b/pyrogram/client/types/input_media/__init__.py @@ -23,3 +23,8 @@ from .input_media_document import InputMediaDocument from .input_media_photo import InputMediaPhoto from .input_media_video import InputMediaVideo from .input_phone_contact import InputPhoneContact + +__all__ = [ + "InputMedia", "InputMediaAnimation", "InputMediaAudio", "InputMediaDocument", "InputMediaPhoto", "InputMediaVideo", + "InputPhoneContact" +] diff --git a/pyrogram/client/types/input_media/input_media.py b/pyrogram/client/types/input_media/input_media.py index f55d8aa0..3062f136 100644 --- a/pyrogram/client/types/input_media/input_media.py +++ b/pyrogram/client/types/input_media/input_media.py @@ -16,12 +16,23 @@ # You should have received a copy of the GNU Lesser General Public License # along with Pyrogram. If not, see . +from ..pyrogram_type import PyrogramType + + +class InputMedia(PyrogramType): + """This object represents the content of a media message to be sent. It should be one of: + + - :obj:`InputMediaAnimation ` + - :obj:`InputMediaDocument ` + - :obj:`InputMediaAudio ` + - :obj:`InputMediaPhoto ` + - :obj:`InputMediaVideo ` + """ + __slots__ = ["media", "caption", "parse_mode"] + + def __init__(self, media: str, caption: str, parse_mode: str): + super().__init__(None) -class InputMedia: - def __init__(self, - media: str, - caption: str, - parse_mode: str): self.media = media self.caption = caption self.parse_mode = parse_mode diff --git a/pyrogram/client/types/input_media/input_media_animation.py b/pyrogram/client/types/input_media/input_media_animation.py index af0c2b2a..e77499b5 100644 --- a/pyrogram/client/types/input_media/input_media_animation.py +++ b/pyrogram/client/types/input_media/input_media_animation.py @@ -52,14 +52,18 @@ class InputMediaAnimation(InputMedia): Animation duration. """ - def __init__(self, - media: str, - thumb: str = None, - caption: str = "", - parse_mode: str = "", - width: int = 0, - height: int = 0, - duration: int = 0): + __slots__ = ["thumb", "width", "height", "duration"] + + def __init__( + self, + media: str, + thumb: str = None, + caption: str = "", + parse_mode: str = "", + width: int = 0, + height: int = 0, + duration: int = 0 + ): super().__init__(media, caption, parse_mode) self.thumb = thumb diff --git a/pyrogram/client/types/input_media/input_media_audio.py b/pyrogram/client/types/input_media/input_media_audio.py index a2dc18db..e8f1c257 100644 --- a/pyrogram/client/types/input_media/input_media_audio.py +++ b/pyrogram/client/types/input_media/input_media_audio.py @@ -53,14 +53,18 @@ class InputMediaAudio(InputMedia): Title of the audio """ - def __init__(self, - media: str, - thumb: str = None, - caption: str = "", - parse_mode: str = "", - duration: int = 0, - performer: int = "", - title: str = ""): + __slots__ = ["thumb", "duration", "performer", "title"] + + def __init__( + self, + media: str, + thumb: str = None, + caption: str = "", + parse_mode: str = "", + duration: int = 0, + performer: int = "", + title: str = "" + ): super().__init__(media, caption, parse_mode) self.thumb = thumb diff --git a/pyrogram/client/types/input_media/input_media_document.py b/pyrogram/client/types/input_media/input_media_document.py index 25e17d0f..9391e7d8 100644 --- a/pyrogram/client/types/input_media/input_media_document.py +++ b/pyrogram/client/types/input_media/input_media_document.py @@ -43,11 +43,15 @@ class InputMediaDocument(InputMedia): Defaults to Markdown. """ - def __init__(self, - media: str, - thumb: str = None, - caption: str = "", - parse_mode: str = ""): + __slots__ = ["thumb"] + + def __init__( + self, + media: str, + thumb: str = None, + caption: str = "", + parse_mode: str = "" + ): super().__init__(media, caption, parse_mode) self.thumb = thumb diff --git a/pyrogram/client/types/input_media/input_media_photo.py b/pyrogram/client/types/input_media/input_media_photo.py index 5917fbf0..e6bba03b 100644 --- a/pyrogram/client/types/input_media/input_media_photo.py +++ b/pyrogram/client/types/input_media/input_media_photo.py @@ -39,8 +39,12 @@ class InputMediaPhoto(InputMedia): Defaults to Markdown. """ - def __init__(self, - media: str, - caption: str = "", - parse_mode: str = ""): + __slots__ = [] + + def __init__( + self, + media: str, + caption: str = "", + parse_mode: str = "" + ): super().__init__(media, caption, parse_mode) diff --git a/pyrogram/client/types/input_media/input_media_video.py b/pyrogram/client/types/input_media/input_media_video.py index 6fa7936e..5c918f13 100644 --- a/pyrogram/client/types/input_media/input_media_video.py +++ b/pyrogram/client/types/input_media/input_media_video.py @@ -57,15 +57,19 @@ class InputMediaVideo(InputMedia): Pass True, if the uploaded video is suitable for streaming. """ - def __init__(self, - media: str, - thumb: str = None, - caption: str = "", - parse_mode: str = "", - width: int = 0, - height: int = 0, - duration: int = 0, - supports_streaming: bool = True): + __slots__ = ["thumb", "width", "height", "duration", "supports_streaming"] + + def __init__( + self, + media: str, + thumb: str = None, + caption: str = "", + parse_mode: str = "", + width: int = 0, + height: int = 0, + duration: int = 0, + supports_streaming: bool = True + ): super().__init__(media, caption, parse_mode) self.thumb = thumb diff --git a/pyrogram/client/types/input_media/input_phone_contact.py b/pyrogram/client/types/input_media/input_phone_contact.py index 1a89759a..d2ac8012 100644 --- a/pyrogram/client/types/input_media/input_phone_contact.py +++ b/pyrogram/client/types/input_media/input_phone_contact.py @@ -18,9 +18,10 @@ from pyrogram.api.types import InputPhoneContact as RawInputPhoneContact from pyrogram.session.internals import MsgId +from ..pyrogram_type import PyrogramType -class InputPhoneContact: +class InputPhoneContact(PyrogramType): """This object represents a Phone Contact to be added in your Telegram address book. It is intended to be used with :meth:`add_contacts() ` @@ -35,11 +36,10 @@ class InputPhoneContact: Contact's last name """ - def __init__(self, - phone: str, - first_name: str, - last_name: str = ""): - pass + __slots__ = [] + + def __init__(self, phone: str, first_name: str, last_name: str = ""): + super().__init__(None) def __new__(cls, phone: str, diff --git a/pyrogram/client/dispatcher/__init__.py b/pyrogram/client/types/input_message_content/__init__.py similarity index 81% rename from pyrogram/client/dispatcher/__init__.py rename to pyrogram/client/types/input_message_content/__init__.py index e2e67b70..5c53fd2e 100644 --- a/pyrogram/client/dispatcher/__init__.py +++ b/pyrogram/client/types/input_message_content/__init__.py @@ -16,4 +16,9 @@ # You should have received a copy of the GNU Lesser General Public License # along with Pyrogram. If not, see . -from .dispatcher import Dispatcher +from .input_message_content import InputMessageContent +from .input_text_message_content import InputTextMessageContent + +__all__ = [ + "InputMessageContent", "InputTextMessageContent" +] diff --git a/pyrogram/client/types/input_message_content/input_message_content.py b/pyrogram/client/types/input_message_content/input_message_content.py new file mode 100644 index 00000000..f3e238b8 --- /dev/null +++ b/pyrogram/client/types/input_message_content/input_message_content.py @@ -0,0 +1,37 @@ +# 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 ..pyrogram_type import PyrogramType + +"""- :obj:`InputLocationMessageContent` + - :obj:`InputVenueMessageContent` + - :obj:`InputContactMessageContent`""" + + +class InputMessageContent(PyrogramType): + """This object represents the content of a message to be sent as a result of an inline query. + + Pyrogram currently supports the following 4 types: + + - :obj:`InputTextMessageContent` + """ + + __slots__ = [] + + def __init__(self): + super().__init__(None) diff --git a/pyrogram/client/types/input_message_content/input_text_message_content.py b/pyrogram/client/types/input_message_content/input_text_message_content.py new file mode 100644 index 00000000..0e6ffa8b --- /dev/null +++ b/pyrogram/client/types/input_message_content/input_text_message_content.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 pyrogram.api import types +from .input_message_content import InputMessageContent +from ...style import HTML, Markdown + + +class InputTextMessageContent(InputMessageContent): + """This object represents the content of a text message to be sent as the result of an inline query. + + Args: + message_text (``str``): + Text of the message to be sent, 1-4096 characters. + + parse_mode (``str``, *optional*): + Use :obj:`MARKDOWN ` or :obj:`HTML ` + if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your message. + Defaults to Markdown. + + disable_web_page_preview (``bool``, *optional*): + Disables link previews for links in this message. + """ + + __slots__ = ["message_text", "parse_mode", "disable_web_page_preview"] + + def __init__(self, message_text: str, parse_mode: str = "", disable_web_page_preview: bool = None): + super().__init__() + + self.message_text = message_text + self.parse_mode = parse_mode + self.disable_web_page_preview = disable_web_page_preview + + def write(self, reply_markup): + return types.InputBotInlineMessageText( + no_webpage=self.disable_web_page_preview or None, + reply_markup=reply_markup.write() if reply_markup else None, + **(HTML() if self.parse_mode.lower() == "html" else Markdown()).parse(self.message_text) + ) diff --git a/pyrogram/client/types/messages_and_media/__init__.py b/pyrogram/client/types/messages_and_media/__init__.py index 604b68b9..ae4386d0 100644 --- a/pyrogram/client/types/messages_and_media/__init__.py +++ b/pyrogram/client/types/messages_and_media/__init__.py @@ -35,3 +35,8 @@ from .venue import Venue from .video import Video from .video_note import VideoNote from .voice import Voice + +__all__ = [ + "Animation", "Audio", "Contact", "Document", "Game", "Location", "Message", "MessageEntity", "Messages", "Photo", + "PhotoSize", "Poll", "PollOption", "Sticker", "UserProfilePhotos", "Venue", "Video", "VideoNote", "Voice" +] diff --git a/pyrogram/client/types/messages_and_media/animation.py b/pyrogram/client/types/messages_and_media/animation.py index d5661ea8..21a01e0f 100644 --- a/pyrogram/client/types/messages_and_media/animation.py +++ b/pyrogram/client/types/messages_and_media/animation.py @@ -57,18 +57,22 @@ class Animation(PyrogramType): Date the animation was sent in Unix time. """ - def __init__(self, - *, - client: "pyrogram.client.ext.BaseClient", - file_id: str, - width: int, - height: int, - duration: int, - thumb: PhotoSize = None, - file_name: str = None, - mime_type: str = None, - file_size: int = None, - date: int = None): + __slots__ = ["file_id", "thumb", "file_name", "mime_type", "file_size", "date", "width", "height", "duration"] + + def __init__( + self, + *, + client: "pyrogram.client.ext.BaseClient", + file_id: str, + width: int, + height: int, + duration: int, + thumb: PhotoSize = None, + file_name: str = None, + mime_type: str = None, + file_size: int = None, + date: int = None + ): super().__init__(client) self.file_id = file_id @@ -97,7 +101,7 @@ class Animation(PyrogramType): width=getattr(video_attributes, "w", 0), height=getattr(video_attributes, "h", 0), duration=getattr(video_attributes, "duration", 0), - thumb=PhotoSize._parse(client, animation.thumb), + thumb=PhotoSize._parse(client, animation.thumbs), mime_type=animation.mime_type, file_size=animation.size, file_name=file_name, diff --git a/pyrogram/client/types/messages_and_media/audio.py b/pyrogram/client/types/messages_and_media/audio.py index cfecceae..db49f2eb 100644 --- a/pyrogram/client/types/messages_and_media/audio.py +++ b/pyrogram/client/types/messages_and_media/audio.py @@ -57,18 +57,22 @@ class Audio(PyrogramType): Title of the audio as defined by sender or by audio tags. """ - def __init__(self, - *, - client: "pyrogram.client.ext.BaseClient", - file_id: str, - duration: int, - thumb: PhotoSize = None, - file_name: str = None, - mime_type: str = None, - file_size: int = None, - date: int = None, - performer: str = None, - title: str = None): + __slots__ = ["file_id", "thumb", "file_name", "mime_type", "file_size", "date", "duration", "performer", "title"] + + def __init__( + self, + *, + client: "pyrogram.client.ext.BaseClient", + file_id: str, + duration: int, + thumb: PhotoSize = None, + file_name: str = None, + mime_type: str = None, + file_size: int = None, + date: int = None, + performer: str = None, + title: str = None + ): super().__init__(client) self.file_id = file_id @@ -99,7 +103,7 @@ class Audio(PyrogramType): title=audio_attributes.title, mime_type=audio.mime_type, file_size=audio.size, - thumb=PhotoSize._parse(client, audio.thumb), + thumb=PhotoSize._parse(client, audio.thumbs), file_name=file_name, date=audio.date, client=client diff --git a/pyrogram/client/types/messages_and_media/contact.py b/pyrogram/client/types/messages_and_media/contact.py index 51acb59f..5abe5319 100644 --- a/pyrogram/client/types/messages_and_media/contact.py +++ b/pyrogram/client/types/messages_and_media/contact.py @@ -42,14 +42,18 @@ class Contact(PyrogramType): Additional data about the contact in the form of a vCard. """ - def __init__(self, - *, - client: "pyrogram.client.ext.BaseClient", - phone_number: str, - first_name: str, - last_name: str = None, - user_id: int = None, - vcard: str = None): + __slots__ = ["phone_number", "first_name", "last_name", "user_id", "vcard"] + + def __init__( + self, + *, + client: "pyrogram.client.ext.BaseClient", + phone_number: str, + first_name: str, + last_name: str = None, + user_id: int = None, + vcard: str = None + ): super().__init__(client) self.phone_number = phone_number diff --git a/pyrogram/client/types/messages_and_media/document.py b/pyrogram/client/types/messages_and_media/document.py index e84b5149..f3ccc4f8 100644 --- a/pyrogram/client/types/messages_and_media/document.py +++ b/pyrogram/client/types/messages_and_media/document.py @@ -48,15 +48,19 @@ class Document(PyrogramType): Date the document was sent in Unix time. """ - def __init__(self, - *, - client: "pyrogram.client.ext.BaseClient", - file_id: str, - thumb: PhotoSize = None, - file_name: str = None, - mime_type: str = None, - file_size: int = None, - date: int = None): + __slots__ = ["file_id", "thumb", "file_name", "mime_type", "file_size", "date"] + + def __init__( + self, + *, + client: "pyrogram.client.ext.BaseClient", + file_id: str, + thumb: PhotoSize = None, + file_name: str = None, + mime_type: str = None, + file_size: int = None, + date: int = None + ): super().__init__(client) self.file_id = file_id @@ -78,7 +82,7 @@ class Document(PyrogramType): document.access_hash ) ), - thumb=PhotoSize._parse(client, document.thumb), + thumb=PhotoSize._parse(client, document.thumbs), file_name=file_name, mime_type=document.mime_type, file_size=document.size, diff --git a/pyrogram/client/types/messages_and_media/game.py b/pyrogram/client/types/messages_and_media/game.py index 01af7226..cf0b4fa6 100644 --- a/pyrogram/client/types/messages_and_media/game.py +++ b/pyrogram/client/types/messages_and_media/game.py @@ -48,15 +48,19 @@ class Game(PyrogramType): Upload via BotFather. """ - def __init__(self, - *, - client: "pyrogram.client.ext.BaseClient", - id: int, - title: str, - short_name: str, - description: str, - photo: Photo, - animation: Animation = None): + __slots__ = ["id", "title", "short_name", "description", "photo", "animation"] + + def __init__( + self, + *, + client: "pyrogram.client.ext.BaseClient", + id: int, + title: str, + short_name: str, + description: str, + photo: Photo, + animation: Animation = None + ): super().__init__(client) self.id = id diff --git a/pyrogram/client/types/messages_and_media/location.py b/pyrogram/client/types/messages_and_media/location.py index 64acdbf5..3a7f6d38 100644 --- a/pyrogram/client/types/messages_and_media/location.py +++ b/pyrogram/client/types/messages_and_media/location.py @@ -33,11 +33,15 @@ class Location(PyrogramType): Latitude as defined by sender. """ - def __init__(self, - *, - client: "pyrogram.client.ext.BaseClient", - longitude: float, - latitude: float): + __slots__ = ["longitude", "latitude"] + + def __init__( + self, + *, + client: "pyrogram.client.ext.BaseClient", + longitude: float, + latitude: float + ): super().__init__(client) self.longitude = longitude diff --git a/pyrogram/client/types/messages_and_media/message.py b/pyrogram/client/types/messages_and_media/message.py index badd3689..fc2cb8fb 100644 --- a/pyrogram/client/types/messages_and_media/message.py +++ b/pyrogram/client/types/messages_and_media/message.py @@ -16,11 +16,14 @@ # You should have received a copy of the GNU Lesser General Public License # along with Pyrogram. If not, see . +from functools import partial from typing import List, Match, Union import pyrogram from pyrogram.api import types -from pyrogram.api.errors import MessageIdsEmpty +from pyrogram.errors import MessageIdsEmpty +from pyrogram.client.ext import ChatAction, ParseMode +from pyrogram.client.types.input_media import InputMedia from .contact import Contact from .location import Location from .message_entity import MessageEntity @@ -31,6 +34,32 @@ from ..user_and_chats.chat import Chat from ..user_and_chats.user import User +class Str(str): + def __init__(self, *args): + super().__init__() + + self._client = None + self._entities = None + + def init(self, client, entities): + self._client = client + self._entities = entities + + return self + + @property + def text(self): + return self + + @property + def markdown(self): + return self._client.markdown.unparse(self, self._entities) + + @property + def html(self): + return self._client.html.unparse(self, self._entities) + + class Message(PyrogramType, Update): """This object represents a message. @@ -50,6 +79,9 @@ class Message(PyrogramType, Update): forward_from (:obj:`User `, *optional*): For forwarded messages, sender of the original message. + forward_from_name (``str``, *optional*): + For messages forwarded from users who have hidden their accounts, name of the user. + forward_from_chat (:obj:`Chat `, *optional*): For messages forwarded from channels, information about the original channel. @@ -234,65 +266,81 @@ class Message(PyrogramType, Update): # TODO: Add game missing field. Also invoice, successful_payment, connected_website - def __init__(self, - *, - client: "pyrogram.client.ext.BaseClient", - message_id: int, - date: int = None, - chat: Chat = None, - from_user: User = None, - forward_from: User = None, - forward_from_chat: Chat = None, - forward_from_message_id: int = None, - forward_signature: str = None, - forward_date: int = None, - reply_to_message: "Message" = None, - mentioned: bool = None, - empty: bool = None, - service: bool = None, - media: bool = None, - edit_date: int = None, - media_group_id: str = None, - author_signature: str = None, - text: str = None, - entities: List["pyrogram.MessageEntity"] = None, - caption_entities: List["pyrogram.MessageEntity"] = None, - audio: "pyrogram.Audio" = None, - document: "pyrogram.Document" = None, - photo: "pyrogram.Photo" = None, - sticker: "pyrogram.Sticker" = None, - animation: "pyrogram.Animation" = None, - game: "pyrogram.Game" = None, - video: "pyrogram.Video" = None, - voice: "pyrogram.Voice" = None, - video_note: "pyrogram.VideoNote" = None, - caption: str = None, - contact: "pyrogram.Contact" = None, - 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, - new_chat_photo: "pyrogram.Photo" = None, - delete_chat_photo: bool = None, - group_chat_created: bool = None, - supergroup_chat_created: bool = None, - channel_chat_created: bool = None, - migrate_to_chat_id: int = None, - migrate_from_chat_id: int = None, - pinned_message: "Message" = None, - game_high_score: int = None, - views: int = None, - via_bot: User = None, - outgoing: bool = None, - matches: List[Match] = None, - command: List[str] = None, - reply_markup: Union["pyrogram.InlineKeyboardMarkup", - "pyrogram.ReplyKeyboardMarkup", - "pyrogram.ReplyKeyboardRemove", - "pyrogram.ForceReply"] = None): + __slots__ = [ + "message_id", "date", "chat", "from_user", "forward_from", "forward_from_name", "forward_from_chat", + "forward_from_message_id", "forward_signature", "forward_date", "reply_to_message", "mentioned", "empty", + "service", "media", "edit_date", "media_group_id", "author_signature", "text", "entities", "caption_entities", + "audio", "document", "photo", "sticker", "animation", "game", "video", "voice", "video_note", "caption", + "contact", "location", "venue", "web_page", "poll", "new_chat_members", "left_chat_member", "new_chat_title", + "new_chat_photo", "delete_chat_photo", "group_chat_created", "supergroup_chat_created", "channel_chat_created", + "migrate_to_chat_id", "migrate_from_chat_id", "pinned_message", "game_high_score", "views", "via_bot", + "outgoing", "matches", "command", "reply_markup" + ] + + def __init__( + self, + *, + client: "pyrogram.client.ext.BaseClient", + message_id: int, + date: int = None, + chat: Chat = None, + from_user: User = None, + forward_from: User = None, + forward_from_name: str = None, + forward_from_chat: Chat = None, + forward_from_message_id: int = None, + forward_signature: str = None, + forward_date: int = None, + reply_to_message: "Message" = None, + mentioned: bool = None, + empty: bool = None, + service: bool = None, + media: bool = None, + edit_date: int = None, + media_group_id: str = None, + author_signature: str = None, + text: Str = None, + entities: List["pyrogram.MessageEntity"] = None, + caption_entities: List["pyrogram.MessageEntity"] = None, + audio: "pyrogram.Audio" = None, + document: "pyrogram.Document" = None, + photo: "pyrogram.Photo" = None, + sticker: "pyrogram.Sticker" = None, + animation: "pyrogram.Animation" = None, + game: "pyrogram.Game" = None, + video: "pyrogram.Video" = None, + voice: "pyrogram.Voice" = None, + video_note: "pyrogram.VideoNote" = None, + caption: Str = None, + contact: "pyrogram.Contact" = None, + 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, + new_chat_photo: "pyrogram.Photo" = None, + delete_chat_photo: bool = None, + group_chat_created: bool = None, + supergroup_chat_created: bool = None, + channel_chat_created: bool = None, + migrate_to_chat_id: int = None, + migrate_from_chat_id: int = None, + pinned_message: "Message" = None, + game_high_score: int = None, + views: int = None, + via_bot: User = None, + outgoing: bool = None, + matches: List[Match] = None, + command: List[str] = None, + reply_markup: Union[ + "pyrogram.InlineKeyboardMarkup", + "pyrogram.ReplyKeyboardMarkup", + "pyrogram.ReplyKeyboardRemove", + "pyrogram.ForceReply" + ] = None + ): super().__init__(client) self.message_id = message_id @@ -300,6 +348,7 @@ class Message(PyrogramType, Update): self.chat = chat self.from_user = from_user self.forward_from = forward_from + self.forward_from_name = forward_from_name self.forward_from_chat = forward_from_chat self.forward_from_message_id = forward_from_message_id self.forward_signature = forward_signature @@ -438,18 +487,21 @@ class Message(PyrogramType, Update): entities = list(filter(lambda x: x is not None, entities)) forward_from = None + forward_from_name = None forward_from_chat = None forward_from_message_id = None forward_signature = None forward_date = None - forward_header = message.fwd_from + forward_header = message.fwd_from # type: types.MessageFwdHeader if forward_header: forward_date = forward_header.date if forward_header.from_id: forward_from = User._parse(client, users[forward_header.from_id]) + elif forward_header.from_name: + forward_from_name = forward_header.from_name else: forward_from_chat = Chat._parse_channel_chat(client, chats[forward_header.channel_id]) forward_from_message_id = forward_header.channel_post @@ -555,6 +607,7 @@ class Message(PyrogramType, Update): caption_entities=entities or None if media is not None else None, author_signature=message.post_author, forward_from=forward_from, + forward_from_name=forward_from_name, forward_from_chat=forward_from_chat, forward_from_message_id=forward_from_message_id, forward_signature=forward_signature, @@ -596,14 +649,16 @@ class Message(PyrogramType, Update): return parsed_message - def reply(self, - text: str, - quote: bool = None, - parse_mode: str = "", - disable_web_page_preview: bool = None, - disable_notification: bool = None, - reply_to_message_id: int = None, - reply_markup=None): + def reply( + self, + text: str, + quote: bool = None, + parse_mode: str = "", + disable_web_page_preview: bool = None, + disable_notification: bool = None, + reply_to_message_id: int = None, + reply_markup=None + ) -> "Message": """Bound method *reply* of :obj:`Message `. Use as a shortcut for: @@ -653,7 +708,7 @@ class Message(PyrogramType, Update): On success, the sent Message is returned. Raises: - :class:`Error ` + :class:`RPCError ` """ if quote is None: quote = self.chat.type != "private" @@ -671,7 +726,1661 @@ class Message(PyrogramType, Update): reply_markup=reply_markup ) - def edit(self, text: str, parse_mode: str = "", disable_web_page_preview: bool = None, reply_markup=None): + def reply_animation( + self, + animation: str, + quote: bool = None, + caption: str = "", + parse_mode: str = "", + duration: int = 0, + width: int = 0, + height: int = 0, + thumb: str = None, + disable_notification: bool = None, + reply_markup: Union[ + "pyrogram.InlineKeyboardMarkup", + "pyrogram.ReplyKeyboardMarkup", + "pyrogram.ReplyKeyboardRemove", + "pyrogram.ForceReply" + ] = None, + reply_to_message_id: int = None, + progress: callable = None, + progress_args: tuple = () + ) -> "Message": + """Bound method *reply_animation* of :obj:`Message `. + + Use as a shortcut for: + + .. code-block:: python + + client.send_animation( + chat_id=message.chat.id, + animation=animation + ) + + Example: + .. code-block:: python + + message.reply_animation(animation) + + Args: + animation (``str``): + Animation to send. + Pass a file_id as string to send an animation that exists on the Telegram servers, + pass an HTTP URL as a string for Telegram to get an animation from the Internet, or + pass a file path as string to upload a new animation that exists on your local machine. + + quote (``bool``, *optional*): + If ``True``, the message will be sent as a reply to this message. + If *reply_to_message_id* is passed, this parameter will be ignored. + Defaults to ``True`` in group chats and ``False`` in private chats. + + caption (``str``, *optional*): + Animation caption, 0-1024 characters. + + parse_mode (``str``, *optional*): + Use :obj:`MARKDOWN ` or :obj:`HTML ` + if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your caption. + Defaults to Markdown. + + duration (``int``, *optional*): + Duration of sent animation in seconds. + + width (``int``, *optional*): + Animation width. + + height (``int``, *optional*): + Animation height. + + thumb (``str``, *optional*): + Thumbnail of the animation file sent. + The thumbnail should be in JPEG format and less than 200 KB in size. + A thumbnail's width and height should not exceed 90 pixels. + Thumbnails can't be reused and can be only uploaded as a new file. + + 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. + + progress (``callable``, *optional*): + Pass a callback function to view the upload progress. + The function must take *(client, current, total, \*args)* as positional arguments (look at the section + below for a detailed description). + + progress_args (``tuple``, *optional*): + Extra custom arguments for the progress callback function. Useful, for example, if you want to pass + a chat_id and a message_id in order to edit a message with the updated progress. + + Other Parameters: + client (:obj:`Client `): + The Client itself, useful when you want to call other API methods inside the callback function. + + current (``int``): + The amount of bytes uploaded so far. + + total (``int``): + The size of the file. + + *args (``tuple``, *optional*): + Extra custom arguments as defined in the *progress_args* parameter. + You can either keep *\*args* or add every single extra argument in your function signature. + + Returns: + On success, the sent :obj:`Message ` is returned. + In case the upload is deliberately stopped with :meth:`stop_transmission`, None is returned instead. + + Raises: + :class:`RPCError ` + """ + if quote is None: + quote = self.chat.type != "private" + + if reply_to_message_id is None and quote: + reply_to_message_id = self.message_id + + return self._client.send_animation( + chat_id=self.chat.id, + animation=animation, + caption=caption, + parse_mode=parse_mode, + duration=duration, + width=width, + height=height, + thumb=thumb, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_markup=reply_markup, + progress=progress, + progress_args=progress_args + ) + + def reply_audio( + self, + audio: str, + quote: bool = None, + caption: str = "", + parse_mode: str = "", + duration: int = 0, + performer: str = None, + title: str = None, + thumb: str = None, + disable_notification: bool = None, + reply_to_message_id: int = None, + reply_markup: Union[ + "pyrogram.InlineKeyboardMarkup", + "pyrogram.ReplyKeyboardMarkup", + "pyrogram.ReplyKeyboardRemove", + "pyrogram.ForceReply" + ] = None, + progress: callable = None, + progress_args: tuple = () + ) -> "Message": + """Bound method *reply_audio* of :obj:`Message `. + + Use as a shortcut for: + + .. code-block:: python + + client.send_audio( + chat_id=message.chat.id, + audio=audio + ) + + Example: + .. code-block:: python + + message.reply_audio(audio) + + Args: + audio (``str``): + Audio file to send. + Pass a file_id as string to send an audio file that exists on the Telegram servers, + pass an HTTP URL as a string for Telegram to get an audio file from the Internet, or + pass a file path as string to upload a new audio file that exists on your local machine. + + quote (``bool``, *optional*): + If ``True``, the message will be sent as a reply to this message. + If *reply_to_message_id* is passed, this parameter will be ignored. + Defaults to ``True`` in group chats and ``False`` in private chats. + + caption (``str``, *optional*): + Audio caption, 0-1024 characters. + + parse_mode (``str``, *optional*): + Use :obj:`MARKDOWN ` or :obj:`HTML ` + if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your caption. + Defaults to Markdown. + + duration (``int``, *optional*): + Duration of the audio in seconds. + + performer (``str``, *optional*): + Performer. + + title (``str``, *optional*): + Track name. + + thumb (``str``, *optional*): + Thumbnail of the music file album cover. + The thumbnail should be in JPEG format and less than 200 KB in size. + A thumbnail's width and height should not exceed 90 pixels. + Thumbnails can't be reused and can be only uploaded as a new file. + + 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. + + progress (``callable``, *optional*): + Pass a callback function to view the upload progress. + The function must take *(client, current, total, \*args)* as positional arguments (look at the section + below for a detailed description). + + progress_args (``tuple``, *optional*): + Extra custom arguments for the progress callback function. Useful, for example, if you want to pass + a chat_id and a message_id in order to edit a message with the updated progress. + + Other Parameters: + client (:obj:`Client `): + The Client itself, useful when you want to call other API methods inside the callback function. + + current (``int``): + The amount of bytes uploaded so far. + + total (``int``): + The size of the file. + + *args (``tuple``, *optional*): + Extra custom arguments as defined in the *progress_args* parameter. + You can either keep *\*args* or add every single extra argument in your function signature. + + Returns: + On success, the sent :obj:`Message ` is returned. + In case the upload is deliberately stopped with :meth:`stop_transmission`, None is returned instead. + + Raises: + :class:`RPCError ` + """ + if quote is None: + quote = self.chat.type != "private" + + if reply_to_message_id is None and quote: + reply_to_message_id = self.message_id + + return self._client.send_audio( + chat_id=self.chat.id, + audio=audio, + caption=caption, + parse_mode=parse_mode, + duration=duration, + performer=performer, + title=title, + thumb=thumb, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_markup=reply_markup, + progress=progress, + progress_args=progress_args + ) + + def reply_cached_media( + self, + file_id: str, + quote: bool = None, + caption: str = "", + parse_mode: str = "", + disable_notification: bool = None, + reply_to_message_id: int = None, + reply_markup: Union[ + "pyrogram.InlineKeyboardMarkup", + "pyrogram.ReplyKeyboardMarkup", + "pyrogram.ReplyKeyboardRemove", + "pyrogram.ForceReply" + ] = None + ) -> "Message": + """Bound method *reply_cached_media* of :obj:`Message `. + + Use as a shortcut for: + + .. code-block:: python + + client.send_cached_media( + chat_id=message.chat.id, + file_id=file_id + ) + + Example: + .. code-block:: python + + message.reply_cached_media(file_id) + + Args: + file_id (``str``): + Media to send. + Pass a file_id as string to send a media that exists on the Telegram servers. + + quote (``bool``, *optional*): + If ``True``, the message will be sent as a reply to this message. + If *reply_to_message_id* is passed, this parameter will be ignored. + Defaults to ``True`` in group chats and ``False`` in private chats. + + caption (``bool``, *optional*): + Media caption, 0-1024 characters. + + parse_mode (``str``, *optional*): + Use :obj:`MARKDOWN ` or :obj:`HTML ` + if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your caption. + Defaults to Markdown. + + 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:`RPCError ` + """ + if quote is None: + quote = self.chat.type != "private" + + if reply_to_message_id is None and quote: + reply_to_message_id = self.message_id + + return self._client.send_cached_media( + chat_id=self.chat.id, + file_id=file_id, + caption=caption, + parse_mode=parse_mode, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_markup=reply_markup + ) + + def reply_chat_action( + self, + action: Union[ChatAction, str], + progress: int = 0 + ) -> "Message": + """Bound method *reply_chat_action* of :obj:`Message `. + + Use as a shortcut for: + + .. code-block:: python + + client.send_chat_action( + chat_id=message.chat.id, + action="typing" + ) + + Example: + .. code-block:: python + + message.reply_chat_action("typing") + + Args: + action (:obj:`ChatAction ` | ``str``): + Type of action to broadcast. + Choose one from the :class:`ChatAction ` enumeration, + depending on what the user is about to receive. + You can also provide a string (e.g. "typing", "upload_photo", "record_audio", ...). + + progress (``int``, *optional*): + Progress of the upload process. + Currently useless because official clients don't seem to be handling this. + + Returns: + On success, True is returned. + + Raises: + :class:`RPCError ` in case of a Telegram RPC error. + ``ValueError`` if the provided string is not a valid ChatAction. + """ + return self._client.send_chat_action( + chat_id=self.chat.id, + action=action, + progress=progress + ) + + def reply_contact( + self, + phone_number: str, + first_name: str, + quote: bool = None, + last_name: str = "", + vcard: str = "", + disable_notification: bool = None, + reply_to_message_id: int = None, + reply_markup: Union[ + "pyrogram.InlineKeyboardMarkup", + "pyrogram.ReplyKeyboardMarkup", + "pyrogram.ReplyKeyboardRemove", + "pyrogram.ForceReply" + ] = None + ) -> "Message": + """Bound method *reply_contact* of :obj:`Message `. + + Use as a shortcut for: + + .. code-block:: python + + client.send_contact( + chat_id=message.chat.id, + phone_number=phone_number, + first_name=first_name + ) + + Example: + .. code-block:: python + + message.reply_contact(phone_number, "Dan") + + Args: + phone_number (``str``): + Contact's phone number. + + first_name (``str``): + Contact's first name. + + quote (``bool``, *optional*): + If ``True``, the message will be sent as a reply to this message. + If *reply_to_message_id* is passed, this parameter will be ignored. + Defaults to ``True`` in group chats and ``False`` in private chats. + + last_name (``str``, *optional*): + Contact's last name. + + vcard (``str``, *optional*): + Additional data about the contact in the form of a vCard, 0-2048 bytes + + 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:`RPCError ` in case of a Telegram RPC error. + """ + if quote is None: + quote = self.chat.type != "private" + + if reply_to_message_id is None and quote: + reply_to_message_id = self.message_id + + return self._client.send_contact( + chat_id=self.chat.id, + phone_number=phone_number, + first_name=first_name, + last_name=last_name, + vcard=vcard, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_markup=reply_markup + ) + + def reply_document( + self, + document: str, + quote: bool = None, + thumb: str = None, + caption: str = "", + parse_mode: str = "", + disable_notification: bool = None, + reply_to_message_id: int = None, + reply_markup: Union[ + "pyrogram.InlineKeyboardMarkup", + "pyrogram.ReplyKeyboardMarkup", + "pyrogram.ReplyKeyboardRemove", + "pyrogram.ForceReply" + ] = None, + progress: callable = None, + progress_args: tuple = () + ) -> "Message": + """Bound method *reply_document* of :obj:`Message `. + + Use as a shortcut for: + + .. code-block:: python + + client.send_document( + chat_id=message.chat.id, + document=document + ) + + Example: + .. code-block:: python + + message.reply_document(document) + + Args: + document (``str``): + File to send. + Pass a file_id as string to send a file that exists on the Telegram servers, + pass an HTTP URL as a string for Telegram to get a file from the Internet, or + pass a file path as string to upload a new file that exists on your local machine. + + quote (``bool``, *optional*): + If ``True``, the message will be sent as a reply to this message. + If *reply_to_message_id* is passed, this parameter will be ignored. + Defaults to ``True`` in group chats and ``False`` in private chats. + + thumb (``str``, *optional*): + Thumbnail of the file sent. + The thumbnail should be in JPEG format and less than 200 KB in size. + A thumbnail's width and height should not exceed 90 pixels. + Thumbnails can't be reused and can be only uploaded as a new file. + + caption (``str``, *optional*): + Document caption, 0-1024 characters. + + parse_mode (``str``, *optional*): + Use :obj:`MARKDOWN ` or :obj:`HTML ` + if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your caption. + Defaults to Markdown. + + 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. + + progress (``callable``, *optional*): + Pass a callback function to view the upload progress. + The function must take *(client, current, total, \*args)* as positional arguments (look at the section + below for a detailed description). + + progress_args (``tuple``, *optional*): + Extra custom arguments for the progress callback function. Useful, for example, if you want to pass + a chat_id and a message_id in order to edit a message with the updated progress. + + Other Parameters: + client (:obj:`Client `): + The Client itself, useful when you want to call other API methods inside the callback function. + + current (``int``): + The amount of bytes uploaded so far. + + total (``int``): + The size of the file. + + *args (``tuple``, *optional*): + Extra custom arguments as defined in the *progress_args* parameter. + You can either keep *\*args* or add every single extra argument in your function signature. + + Returns: + On success, the sent :obj:`Message ` is returned. + In case the upload is deliberately stopped with :meth:`stop_transmission`, None is returned instead. + + Raises: + :class:`RPCError ` in case of a Telegram RPC error. + """ + if quote is None: + quote = self.chat.type != "private" + + if reply_to_message_id is None and quote: + reply_to_message_id = self.message_id + + return self._client.send_document( + chat_id=self.chat.id, + document=document, + thumb=thumb, + caption=caption, + parse_mode=parse_mode, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_markup=reply_markup, + progress=progress, + progress_args=progress_args + ) + + def reply_game( + self, + game_short_name: str, + quote: bool = None, + disable_notification: bool = None, + reply_to_message_id: int = None, + reply_markup: Union[ + "pyrogram.InlineKeyboardMarkup", + "pyrogram.ReplyKeyboardMarkup", + "pyrogram.ReplyKeyboardRemove", + "pyrogram.ForceReply" + ] = None + ) -> "Message": + """Bound method *reply_game* of :obj:`Message `. + + Use as a shortcut for: + + .. code-block:: python + + client.send_game( + chat_id=message.chat.id, + game_short_name="lumberjack" + ) + + Example: + .. code-block:: python + + message.reply_game("lumberjack") + + Args: + game_short_name (``str``): + Short name of the game, serves as the unique identifier for the game. Set up your games via Botfather. + + quote (``bool``, *optional*): + If ``True``, the message will be sent as a reply to this message. + If *reply_to_message_id* is passed, this parameter will be ignored. + Defaults to ``True`` in group chats and ``False`` in private chats. + + 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`, *optional*): + An object for an inline keyboard. If empty, one ‘Play game_title’ button will be shown automatically. + If not empty, the first button must launch the game. + + Returns: + On success, the sent :obj:`Message` is returned. + + Raises: + :class:`RPCError ` in case of a Telegram RPC error. + """ + if quote is None: + quote = self.chat.type != "private" + + if reply_to_message_id is None and quote: + reply_to_message_id = self.message_id + + return self._client.send_game( + chat_id=self.chat.id, + game_short_name=game_short_name, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_markup=reply_markup + ) + + def reply_inline_bot_result( + self, + query_id: int, + result_id: str, + quote: bool = None, + disable_notification: bool = None, + reply_to_message_id: int = None, + hide_via: bool = None + ) -> "Message": + """Bound method *reply_inline_bot_result* of :obj:`Message `. + + Use as a shortcut for: + + .. code-block:: python + + client.send_inline_bot_result( + chat_id=message.chat.id, + query_id=query_id, + result_id=result_id + ) + + Example: + .. code-block:: python + + message.reply_inline_bot_result(query_id, result_id) + + Args: + query_id (``int``): + Unique identifier for the answered query. + + result_id (``str``): + Unique identifier for the result that was chosen. + + quote (``bool``, *optional*): + If ``True``, the message will be sent as a reply to this message. + If *reply_to_message_id* is passed, this parameter will be ignored. + Defaults to ``True`` in group chats and ``False`` in private chats. + + disable_notification (``bool``, *optional*): + Sends the message silently. + Users will receive a notification with no sound. + + 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. + + Raises: + :class:`RPCError ` in case of a Telegram RPC error. + """ + if quote is None: + quote = self.chat.type != "private" + + if reply_to_message_id is None and quote: + reply_to_message_id = self.message_id + + return self._client.send_inline_bot_result( + chat_id=self.chat.id, + query_id=query_id, + result_id=result_id, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + hide_via=hide_via + ) + + def reply_location( + self, + latitude: float, + longitude: float, + quote: bool = None, + disable_notification: bool = None, + reply_to_message_id: int = None, + reply_markup: Union[ + "pyrogram.InlineKeyboardMarkup", + "pyrogram.ReplyKeyboardMarkup", + "pyrogram.ReplyKeyboardRemove", + "pyrogram.ForceReply" + ] = None + ) -> "Message": + """Bound method *reply_location* of :obj:`Message `. + + Use as a shortcut for: + + .. code-block:: python + + client.send_location( + chat_id=message.chat.id, + latitude=41.890251, + longitude=12.492373 + ) + + Example: + .. code-block:: python + + message.reply_location(41.890251, 12.492373) + + Args: + latitude (``float``): + Latitude of the location. + + longitude (``float``): + Longitude of the location. + + quote (``bool``, *optional*): + If ``True``, the message will be sent as a reply to this message. + If *reply_to_message_id* is passed, this parameter will be ignored. + Defaults to ``True`` in group chats and ``False`` in private chats. + + 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:`RPCError ` in case of a Telegram RPC error. + """ + if quote is None: + quote = self.chat.type != "private" + + if reply_to_message_id is None and quote: + reply_to_message_id = self.message_id + + return self._client.send_location( + chat_id=self.chat.id, + latitude=latitude, + longitude=longitude, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_markup=reply_markup + ) + + def reply_media_group( + self, + media: List[Union["pyrogram.InputMediaPhoto", "pyrogram.InputMediaVideo"]], + quote: bool = None, + disable_notification: bool = None, + reply_to_message_id: int = None + ) -> "Message": + """Bound method *reply_media_group* of :obj:`Message `. + + Use as a shortcut for: + + .. code-block:: python + + client.send_media_group( + chat_id=message.chat.id, + media=list_of_media + ) + + Example: + .. code-block:: python + + message.reply_media_group(list_of_media) + + Args: + media (``list``): + A list containing either :obj:`InputMediaPhoto ` or + :obj:`InputMediaVideo ` objects + describing photos and videos to be sent, must include 2–10 items. + + quote (``bool``, *optional*): + If ``True``, the message will be sent as a reply to this message. + If *reply_to_message_id* is passed, this parameter will be ignored. + Defaults to ``True`` in group chats and ``False`` in private chats. + + 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. + + Returns: + On success, a :obj:`Messages ` object is returned containing all the + single messages sent. + + Raises: + :class:`RPCError ` in case of a Telegram RPC error. + """ + if quote is None: + quote = self.chat.type != "private" + + if reply_to_message_id is None and quote: + reply_to_message_id = self.message_id + + return self._client.send_media_group( + chat_id=self.chat.id, + media=media, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id + ) + + def reply_photo( + self, + photo: str, + quote: bool = None, + caption: str = "", + parse_mode: str = "", + ttl_seconds: int = None, + disable_notification: bool = None, + reply_to_message_id: int = None, + reply_markup: Union[ + "pyrogram.InlineKeyboardMarkup", + "pyrogram.ReplyKeyboardMarkup", + "pyrogram.ReplyKeyboardRemove", + "pyrogram.ForceReply" + ] = None, + progress: callable = None, + progress_args: tuple = () + ) -> "Message": + """Bound method *reply_photo* of :obj:`Message `. + + Use as a shortcut for: + + .. code-block:: python + + client.send_photo( + chat_id=message.chat.id, + photo=photo + ) + + Example: + .. code-block:: python + + message.reply_photo(photo) + + Args: + photo (``str``): + Photo to send. + Pass a file_id as string to send a photo that exists on the Telegram servers, + pass an HTTP URL as a string for Telegram to get a photo from the Internet, or + pass a file path as string to upload a new photo that exists on your local machine. + + quote (``bool``, *optional*): + If ``True``, the message will be sent as a reply to this message. + If *reply_to_message_id* is passed, this parameter will be ignored. + Defaults to ``True`` in group chats and ``False`` in private chats. + + caption (``bool``, *optional*): + Photo caption, 0-1024 characters. + + parse_mode (``str``, *optional*): + Use :obj:`MARKDOWN ` or :obj:`HTML ` + if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your caption. + Defaults to Markdown. + + ttl_seconds (``int``, *optional*): + Self-Destruct Timer. + If you set a timer, the photo will self-destruct in *ttl_seconds* + seconds after it was viewed. + + 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. + + progress (``callable``, *optional*): + Pass a callback function to view the upload progress. + The function must take *(client, current, total, \*args)* as positional arguments (look at the section + below for a detailed description). + + progress_args (``tuple``, *optional*): + Extra custom arguments for the progress callback function. Useful, for example, if you want to pass + a chat_id and a message_id in order to edit a message with the updated progress. + + Other Parameters: + client (:obj:`Client `): + The Client itself, useful when you want to call other API methods inside the callback function. + + current (``int``): + The amount of bytes uploaded so far. + + total (``int``): + The size of the file. + + *args (``tuple``, *optional*): + Extra custom arguments as defined in the *progress_args* parameter. + You can either keep *\*args* or add every single extra argument in your function signature. + + Returns: + On success, the sent :obj:`Message ` is returned. + In case the upload is deliberately stopped with :meth:`stop_transmission`, None is returned instead. + + Raises: + :class:`RPCError ` in case of a Telegram RPC error. + """ + if quote is None: + quote = self.chat.type != "private" + + if reply_to_message_id is None and quote: + reply_to_message_id = self.message_id + + return self._client.send_photo( + chat_id=self.chat.id, + photo=photo, + caption=caption, + parse_mode=parse_mode, + ttl_seconds=ttl_seconds, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_markup=reply_markup, + progress=progress, + progress_args=progress_args + ) + + def reply_poll( + self, + question: str, + options: List[str], + quote: bool = None, + disable_notification: bool = None, + reply_to_message_id: int = None, + reply_markup: Union[ + "pyrogram.InlineKeyboardMarkup", + "pyrogram.ReplyKeyboardMarkup", + "pyrogram.ReplyKeyboardRemove", + "pyrogram.ForceReply" + ] = None + ) -> "Message": + """Bound method *reply_poll* of :obj:`Message `. + + Use as a shortcut for: + + .. code-block:: python + + client.send_poll( + chat_id=message.chat.id, + question="Is Pyrogram the best?", + options=["Yes", "Yes"] + ) + + Example: + .. code-block:: python + + message.reply_poll("Is Pyrogram the best?", ["Yes", "Yes"]) + + Args: + question (``str``): + The poll question, as string. + + options (List of ``str``): + The poll options, as list of strings (2 to 10 options are allowed). + + quote (``bool``, *optional*): + If ``True``, the message will be sent as a reply to this message. + If *reply_to_message_id* is passed, this parameter will be ignored. + Defaults to ``True`` in group chats and ``False`` in private chats. + + 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:`RPCError ` in case of a Telegram RPC error. + """ + if quote is None: + quote = self.chat.type != "private" + + if reply_to_message_id is None and quote: + reply_to_message_id = self.message_id + + return self._client.send_poll( + chat_id=self.chat.id, + question=question, + options=options, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_markup=reply_markup + ) + + def reply_sticker( + self, + sticker: str, + quote: bool = None, + disable_notification: bool = None, + reply_to_message_id: int = None, + reply_markup: Union[ + "pyrogram.InlineKeyboardMarkup", + "pyrogram.ReplyKeyboardMarkup", + "pyrogram.ReplyKeyboardRemove", + "pyrogram.ForceReply" + ] = None, + progress: callable = None, + progress_args: tuple = () + ) -> "Message": + """Bound method *reply_sticker* of :obj:`Message `. + + Use as a shortcut for: + + .. code-block:: python + + client.send_sticker( + chat_id=message.chat.id, + sticker=sticker + ) + + Example: + .. code-block:: python + + message.reply_sticker(sticker) + + Args: + sticker (``str``): + Sticker to send. + Pass a file_id as string to send a sticker that exists on the Telegram servers, + pass an HTTP URL as a string for Telegram to get a .webp sticker file from the Internet, or + pass a file path as string to upload a new sticker that exists on your local machine. + + quote (``bool``, *optional*): + If ``True``, the message will be sent as a reply to this message. + If *reply_to_message_id* is passed, this parameter will be ignored. + Defaults to ``True`` in group chats and ``False`` in private chats. + + 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. + + progress (``callable``, *optional*): + Pass a callback function to view the upload progress. + The function must take *(client, current, total, \*args)* as positional arguments (look at the section + below for a detailed description). + + progress_args (``tuple``, *optional*): + Extra custom arguments for the progress callback function. Useful, for example, if you want to pass + a chat_id and a message_id in order to edit a message with the updated progress. + + Other Parameters: + client (:obj:`Client `): + The Client itself, useful when you want to call other API methods inside the callback function. + + current (``int``): + The amount of bytes uploaded so far. + + total (``int``): + The size of the file. + + *args (``tuple``, *optional*): + Extra custom arguments as defined in the *progress_args* parameter. + You can either keep *\*args* or add every single extra argument in your function signature. + + Returns: + On success, the sent :obj:`Message ` is returned. + In case the upload is deliberately stopped with :meth:`stop_transmission`, None is returned instead. + + Raises: + :class:`RPCError ` in case of a Telegram RPC error. + """ + if quote is None: + quote = self.chat.type != "private" + + if reply_to_message_id is None and quote: + reply_to_message_id = self.message_id + + return self._client.send_sticker( + chat_id=self.chat.id, + sticker=sticker, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_markup=reply_markup, + progress=progress, + progress_args=progress_args + ) + + def reply_venue( + self, + latitude: float, + longitude: float, + title: str, + address: str, + quote: bool = None, + foursquare_id: str = "", + foursquare_type: str = "", + disable_notification: bool = None, + reply_to_message_id: int = None, + reply_markup: Union[ + "pyrogram.InlineKeyboardMarkup", + "pyrogram.ReplyKeyboardMarkup", + "pyrogram.ReplyKeyboardRemove", + "pyrogram.ForceReply" + ] = None + ) -> "Message": + """Bound method *reply_venue* of :obj:`Message `. + + Use as a shortcut for: + + .. code-block:: python + + client.send_venue( + chat_id=message.chat.id, + latitude=41.890251, + longitude=12.492373, + title="Coliseum", + address="Piazza del Colosseo, 1, 00184 Roma RM" + ) + + Example: + .. code-block:: python + + message.reply_venue(41.890251, 12.492373, "Coliseum", "Piazza del Colosseo, 1, 00184 Roma RM") + + Args: + latitude (``float``): + Latitude of the venue. + + longitude (``float``): + Longitude of the venue. + + title (``str``): + Name of the venue. + + address (``str``): + Address of the venue. + + quote (``bool``, *optional*): + If ``True``, the message will be sent as a reply to this message. + If *reply_to_message_id* is passed, this parameter will be ignored. + Defaults to ``True`` in group chats and ``False`` in private chats. + + foursquare_id (``str``, *optional*): + Foursquare identifier of the venue. + + foursquare_type (``str``, *optional*): + Foursquare type of the venue, if known. + (For example, "arts_entertainment/default", "arts_entertainment/aquarium" or "food/icecream".) + + 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:`RPCError ` in case of a Telegram RPC error. + """ + if quote is None: + quote = self.chat.type != "private" + + if reply_to_message_id is None and quote: + reply_to_message_id = self.message_id + + return self._client.send_venue( + chat_id=self.chat.id, + latitude=latitude, + longitude=longitude, + title=title, + address=address, + foursquare_id=foursquare_id, + foursquare_type=foursquare_type, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_markup=reply_markup + ) + + def reply_video( + self, + video: str, + quote: bool = None, + caption: str = "", + parse_mode: str = "", + duration: int = 0, + width: int = 0, + height: int = 0, + thumb: str = None, + supports_streaming: bool = True, + disable_notification: bool = None, + reply_to_message_id: int = None, + reply_markup: Union[ + "pyrogram.InlineKeyboardMarkup", + "pyrogram.ReplyKeyboardMarkup", + "pyrogram.ReplyKeyboardRemove", + "pyrogram.ForceReply" + ] = None, + progress: callable = None, + progress_args: tuple = () + ) -> "Message": + """Bound method *reply_video* of :obj:`Message `. + + Use as a shortcut for: + + .. code-block:: python + + client.send_video( + chat_id=message.chat.id, + video=video + ) + + Example: + .. code-block:: python + + message.reply_video(video) + + Args: + video (``str``): + Video to send. + Pass a file_id as string to send a video that exists on the Telegram servers, + pass an HTTP URL as a string for Telegram to get a video from the Internet, or + pass a file path as string to upload a new video that exists on your local machine. + + quote (``bool``, *optional*): + If ``True``, the message will be sent as a reply to this message. + If *reply_to_message_id* is passed, this parameter will be ignored. + Defaults to ``True`` in group chats and ``False`` in private chats. + + caption (``str``, *optional*): + Video caption, 0-1024 characters. + + parse_mode (``str``, *optional*): + Use :obj:`MARKDOWN ` or :obj:`HTML ` + if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your caption. + Defaults to Markdown. + + duration (``int``, *optional*): + Duration of sent video in seconds. + + width (``int``, *optional*): + Video width. + + height (``int``, *optional*): + Video height. + + thumb (``str``, *optional*): + Thumbnail of the video sent. + The thumbnail should be in JPEG format and less than 200 KB in size. + A thumbnail's width and height should not exceed 90 pixels. + Thumbnails can't be reused and can be only uploaded as a new file. + + supports_streaming (``bool``, *optional*): + Pass True, if the uploaded video is suitable for streaming. + + 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. + + progress (``callable``, *optional*): + Pass a callback function to view the upload progress. + The function must take *(client, current, total, \*args)* as positional arguments (look at the section + below for a detailed description). + + progress_args (``tuple``, *optional*): + Extra custom arguments for the progress callback function. Useful, for example, if you want to pass + a chat_id and a message_id in order to edit a message with the updated progress. + + Other Parameters: + client (:obj:`Client `): + The Client itself, useful when you want to call other API methods inside the callback function. + + current (``int``): + The amount of bytes uploaded so far. + + total (``int``): + The size of the file. + + *args (``tuple``, *optional*): + Extra custom arguments as defined in the *progress_args* parameter. + You can either keep *\*args* or add every single extra argument in your function signature. + + Returns: + On success, the sent :obj:`Message ` is returned. + In case the upload is deliberately stopped with :meth:`stop_transmission`, None is returned instead. + + Raises: + :class:`RPCError ` in case of a Telegram RPC error. + """ + if quote is None: + quote = self.chat.type != "private" + + if reply_to_message_id is None and quote: + reply_to_message_id = self.message_id + + return self._client.send_video( + chat_id=self.chat.id, + video=video, + caption=caption, + parse_mode=parse_mode, + duration=duration, + width=width, + height=height, + thumb=thumb, + supports_streaming=supports_streaming, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_markup=reply_markup, + progress=progress, + progress_args=progress_args + ) + + def reply_video_note( + self, + video_note: str, + quote: bool = None, + duration: int = 0, + length: int = 1, + thumb: str = None, + disable_notification: bool = None, + reply_to_message_id: int = None, + reply_markup: Union[ + "pyrogram.InlineKeyboardMarkup", + "pyrogram.ReplyKeyboardMarkup", + "pyrogram.ReplyKeyboardRemove", + "pyrogram.ForceReply" + ] = None, + progress: callable = None, + progress_args: tuple = () + ) -> "Message": + """Bound method *reply_video_note* of :obj:`Message `. + + Use as a shortcut for: + + .. code-block:: python + + client.send_video_note( + chat_id=message.chat.id, + video_note=video_note + ) + + Example: + .. code-block:: python + + message.reply_video_note(video_note) + + Args: + video_note (``str``): + Video note to send. + Pass a file_id as string to send a video note that exists on the Telegram servers, or + pass a file path as string to upload a new video note that exists on your local machine. + Sending video notes by a URL is currently unsupported. + + quote (``bool``, *optional*): + If ``True``, the message will be sent as a reply to this message. + If *reply_to_message_id* is passed, this parameter will be ignored. + Defaults to ``True`` in group chats and ``False`` in private chats. + + duration (``int``, *optional*): + Duration of sent video in seconds. + + length (``int``, *optional*): + Video width and height. + + thumb (``str``, *optional*): + Thumbnail of the video sent. + The thumbnail should be in JPEG format and less than 200 KB in size. + A thumbnail's width and height should not exceed 90 pixels. + Thumbnails can't be reused and can be only uploaded as a new file. + + 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. + + progress (``callable``, *optional*): + Pass a callback function to view the upload progress. + The function must take *(client, current, total, \*args)* as positional arguments (look at the section + below for a detailed description). + + progress_args (``tuple``, *optional*): + Extra custom arguments for the progress callback function. Useful, for example, if you want to pass + a chat_id and a message_id in order to edit a message with the updated progress. + + Other Parameters: + client (:obj:`Client `): + The Client itself, useful when you want to call other API methods inside the callback function. + + current (``int``): + The amount of bytes uploaded so far. + + total (``int``): + The size of the file. + + *args (``tuple``, *optional*): + Extra custom arguments as defined in the *progress_args* parameter. + You can either keep *\*args* or add every single extra argument in your function signature. + + Returns: + On success, the sent :obj:`Message ` is returned. + In case the upload is deliberately stopped with :meth:`stop_transmission`, None is returned instead. + + Raises: + :class:`RPCError ` in case of a Telegram RPC error. + """ + if quote is None: + quote = self.chat.type != "private" + + if reply_to_message_id is None and quote: + reply_to_message_id = self.message_id + + return self._client.send_video_note( + chat_id=self.chat.id, + video_note=video_note, + duration=duration, + length=length, + thumb=thumb, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_markup=reply_markup, + progress=progress, + progress_args=progress_args + ) + + def reply_voice( + self, + voice: str, + quote: bool = None, + caption: str = "", + parse_mode: str = "", + duration: int = 0, + disable_notification: bool = None, + reply_to_message_id: int = None, + reply_markup: Union[ + "pyrogram.InlineKeyboardMarkup", + "pyrogram.ReplyKeyboardMarkup", + "pyrogram.ReplyKeyboardRemove", + "pyrogram.ForceReply" + ] = None, + progress: callable = None, + progress_args: tuple = () + ) -> "Message": + """Bound method *reply_voice* of :obj:`Message `. + + Use as a shortcut for: + + .. code-block:: python + + client.send_voice( + chat_id=message.chat.id, + voice=voice + ) + + Example: + .. code-block:: python + + message.reply_voice(voice) + + Args: + voice (``str``): + Audio file to send. + Pass a file_id as string to send an audio that exists on the Telegram servers, + pass an HTTP URL as a string for Telegram to get an audio from the Internet, or + pass a file path as string to upload a new audio that exists on your local machine. + + quote (``bool``, *optional*): + If ``True``, the message will be sent as a reply to this message. + If *reply_to_message_id* is passed, this parameter will be ignored. + Defaults to ``True`` in group chats and ``False`` in private chats. + + caption (``str``, *optional*): + Voice message caption, 0-1024 characters. + + parse_mode (``str``, *optional*): + Use :obj:`MARKDOWN ` or :obj:`HTML ` + if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your caption. + Defaults to Markdown. + + duration (``int``, *optional*): + Duration of the voice message in seconds. + + 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. + + progress (``callable``, *optional*): + Pass a callback function to view the upload progress. + The function must take *(client, current, total, \*args)* as positional arguments (look at the section + below for a detailed description). + + progress_args (``tuple``, *optional*): + Extra custom arguments for the progress callback function. Useful, for example, if you want to pass + a chat_id and a message_id in order to edit a message with the updated progress. + + Other Parameters: + client (:obj:`Client `): + The Client itself, useful when you want to call other API methods inside the callback function. + + current (``int``): + The amount of bytes uploaded so far. + + total (``int``): + The size of the file. + + *args (``tuple``, *optional*): + Extra custom arguments as defined in the *progress_args* parameter. + You can either keep *\*args* or add every single extra argument in your function signature. + + Returns: + On success, the sent :obj:`Message ` is returned. + In case the upload is deliberately stopped with :meth:`stop_transmission`, None is returned instead. + + Raises: + :class:`RPCError ` in case of a Telegram RPC error. + """ + if quote is None: + quote = self.chat.type != "private" + + if reply_to_message_id is None and quote: + reply_to_message_id = self.message_id + + return self._client.send_voice( + chat_id=self.chat.id, + voice=voice, + caption=caption, + parse_mode=parse_mode, + duration=duration, + disable_notification=disable_notification, + reply_to_message_id=reply_to_message_id, + reply_markup=reply_markup, + progress=progress, + progress_args=progress_args + ) + + def edit( + self, + text: str, + parse_mode: str = "", + disable_web_page_preview: bool = None, + reply_markup: Union[ + "pyrogram.InlineKeyboardMarkup", + "pyrogram.ReplyKeyboardMarkup", + "pyrogram.ReplyKeyboardRemove", + "pyrogram.ForceReply" + ] = None + ) -> "Message": """Bound method *edit* of :obj:`Message ` Use as a shortcut for: @@ -681,7 +2390,7 @@ class Message(PyrogramType, Update): client.edit_message_text( chat_id=message.chat.id, message_id=message.message_id, - text="hello", + text="hello" ) Example: @@ -708,7 +2417,7 @@ class Message(PyrogramType, Update): On success, the edited :obj:`Message ` is returned. Raises: - :class:`Error ` in case of a Telegram RPC error. + :class:`RPCError ` in case of a Telegram RPC error. """ return self._client.edit_message_text( chat_id=self.chat.id, @@ -719,9 +2428,140 @@ class Message(PyrogramType, Update): reply_markup=reply_markup ) - def forward(self, - chat_id: int or str, - disable_notification: bool = None): + def edit_caption( + self, + caption: str, + parse_mode: str = "", + reply_markup: Union[ + "pyrogram.InlineKeyboardMarkup", + "pyrogram.ReplyKeyboardMarkup", + "pyrogram.ReplyKeyboardRemove", + "pyrogram.ForceReply" + ] = None + ) -> "Message": + """Bound method *edit_caption* of :obj:`Message ` + + Use as a shortcut for: + + .. code-block:: python + + client.edit_message_caption( + chat_id=message.chat.id, + message_id=message.message_id, + caption="hello" + ) + + Example: + .. code-block:: python + + message.edit_caption("hello") + + Args: + caption (``str``): + New caption of the message. + + parse_mode (``str``, *optional*): + Use :obj:`MARKDOWN ` or :obj:`HTML ` + if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your message. + Defaults to Markdown. + + reply_markup (:obj:`InlineKeyboardMarkup`, *optional*): + An InlineKeyboardMarkup object. + + Returns: + On success, the edited :obj:`Message ` is returned. + + Raises: + :class:`RPCError ` in case of a Telegram RPC error. + """ + return self._client.edit_message_caption( + chat_id=self.chat.id, + message_id=self.message_id, + caption=caption, + parse_mode=parse_mode, + reply_markup=reply_markup + ) + + def edit_media(self, media: InputMedia, reply_markup: "pyrogram.InlineKeyboardMarkup" = None) -> "Message": + """Bound method *edit_media* of :obj:`Message ` + + Use as a shortcut for: + + .. code-block:: python + + client.edit_message_media( + chat_id=message.chat.id, + message_id=message.message_id, + media=media + ) + + Example: + .. code-block:: python + + message.edit_media(media) + + Args: + media (:obj:`InputMediaAnimation` | :obj:`InputMediaAudio` | :obj:`InputMediaDocument` | :obj:`InputMediaPhoto` | :obj:`InputMediaVideo`) + One of the InputMedia objects describing an animation, audio, document, photo or video. + + reply_markup (:obj:`InlineKeyboardMarkup`, *optional*): + An InlineKeyboardMarkup object. + + Returns: + On success, the edited :obj:`Message ` is returned. + + Raises: + :class:`RPCError ` in case of a Telegram RPC error. + """ + return self._client.edit_message_media( + chat_id=self.chat.id, + message_id=self.message_id, + media=media, + reply_markup=reply_markup + ) + + def edit_reply_markup(self, reply_markup: "pyrogram.InlineKeyboardMarkup" = None) -> "Message": + """Bound method *edit_reply_markup* of :obj:`Message ` + + Use as a shortcut for: + + .. code-block:: python + + client.edit_message_reply_markup( + chat_id=message.chat.id, + message_id=message.message_id, + reply_markup=inline_reply_markup + ) + + Example: + .. code-block:: python + + message.edit_reply_markup(inline_reply_markup) + + Args: + reply_markup (:obj:`InlineKeyboardMarkup`): + An InlineKeyboardMarkup object. + + Returns: + On success, if edited message is sent by the bot, the edited + :obj:`Message ` is returned, otherwise True is returned. + + Raises: + :class:`RPCError ` in case of a Telegram RPC error. + """ + return self._client.edit_message_reply_markup( + chat_id=self.chat.id, + message_id=self.message_id, + reply_markup=reply_markup + ) + + def forward( + self, + chat_id: int or str, + disable_notification: bool = None, + as_copy: bool = False, + remove_caption: bool = False + ) -> "Message": """Bound method *forward* of :obj:`Message `. Use as a shortcut for: @@ -731,7 +2571,7 @@ class Message(PyrogramType, Update): client.forward_messages( chat_id=chat_id, from_chat_id=message.chat.id, - message_ids=message.message_id, + message_ids=message.message_id ) Example: @@ -749,18 +2589,120 @@ class Message(PyrogramType, Update): Sends the message silently. Users will receive a notification with no sound. + as_copy (``bool``, *optional*): + Pass True to forward messages without the forward header (i.e.: send a copy of the message content). + Defaults to False. + + remove_caption (``bool``, *optional*): + If set to True and *as_copy* is enabled as well, media captions are not preserved when copying the + message. Has no effect if *as_copy* is not enabled. + Defaults to False. + Returns: On success, the forwarded Message is returned. Raises: - :class:`Error ` + :class:`RPCError ` """ - return self._client.forward_messages( - chat_id=chat_id, - from_chat_id=self.chat.id, - message_ids=self.message_id, - disable_notification=disable_notification - ) + if as_copy: + if self.service: + raise ValueError("Unable to copy service messages") + + if self.game and not self._client.is_bot: + raise ValueError("Users cannot send messages with Game media type") + + # TODO: Improve markdown parser. Currently html appears to be more stable, thus we use it here because users + # can"t choose. + + if self.text: + return self._client.send_message( + chat_id, + text=self.text.html, + parse_mode="html", + disable_web_page_preview=not self.web_page, + disable_notification=disable_notification + ) + elif self.media: + caption = self.caption.html if self.caption and not remove_caption else None + + send_media = partial( + self._client.send_cached_media, + chat_id=chat_id, + disable_notification=disable_notification + ) + + if self.photo: + file_id = self.photo.sizes[-1].file_id + elif self.audio: + file_id = self.audio.file_id + elif self.document: + file_id = self.document.file_id + elif self.video: + file_id = self.video.file_id + elif self.animation: + file_id = self.animation.file_id + elif self.voice: + file_id = self.voice.file_id + elif self.sticker: + file_id = self.sticker.file_id + elif self.video_note: + file_id = self.video_note.file_id + elif self.contact: + return self._client.send_contact( + chat_id, + phone_number=self.contact.phone_number, + first_name=self.contact.first_name, + last_name=self.contact.last_name, + vcard=self.contact.vcard, + disable_notification=disable_notification + ) + elif self.location: + return self._client.send_location( + chat_id, + latitude=self.location.latitude, + longitude=self.location.longitude, + disable_notification=disable_notification + ) + elif self.venue: + return self._client.send_venue( + chat_id, + latitude=self.venue.location.latitude, + longitude=self.venue.location.longitude, + title=self.venue.title, + address=self.venue.address, + foursquare_id=self.venue.foursquare_id, + foursquare_type=self.venue.foursquare_type, + disable_notification=disable_notification + ) + elif self.poll: + return self._client.send_poll( + chat_id, + question=self.poll.question, + options=[opt.text for opt in self.poll.options], + disable_notification=disable_notification + ) + elif self.game: + return self._client.send_game( + chat_id, + game_short_name=self.game.short_name, + disable_notification=disable_notification + ) + else: + raise ValueError("Unknown media type") + + if self.sticker or self.video_note: # Sticker and VideoNote should have no caption + return send_media(file_id=file_id) + else: + return send_media(file_id=file_id, caption=caption, parse_mode=ParseMode.HTML) + else: + raise ValueError("Can't copy this message") + else: + return self._client.forward_messages( + chat_id=chat_id, + from_chat_id=self.chat.id, + message_ids=self.message_id, + disable_notification=disable_notification + ) def delete(self, revoke: bool = True): """Bound method *delete* of :obj:`Message `. @@ -790,7 +2732,7 @@ class Message(PyrogramType, Update): True on success. Raises: - :class:`Error ` + :class:`RPCError ` """ self._client.delete_messages( chat_id=self.chat.id, @@ -855,12 +2797,12 @@ class Message(PyrogramType, Update): button. Raises: - :class:`Error ` + :class:`RPCError ` ``ValueError``: If the provided index or position is out of range or the button label was not found ``TimeoutError``: If, after clicking an inline button, the bot fails to answer within 10 seconds """ if isinstance(self.reply_markup, pyrogram.ReplyKeyboardMarkup): - return self.reply(x) + return self.reply(x, quote=quote) elif isinstance(self.reply_markup, pyrogram.InlineKeyboardMarkup): if isinstance(x, int) and y is None: try: @@ -912,7 +2854,13 @@ class Message(PyrogramType, Update): else: raise ValueError("The message doesn't contain any keyboard") - def download(self, file_name: str = "", block: bool = True, progress: callable = None, progress_args: tuple = ()): + def download( + self, + file_name: str = "", + block: bool = True, + progress: callable = None, + progress_args: tuple = () + ) -> "Message": """Bound method *download* of :obj:`Message `. Use as a shortcut for: @@ -950,7 +2898,7 @@ class Message(PyrogramType, Update): On success, the absolute path of the downloaded file as string is returned, None otherwise. Raises: - :class:`Error ` + :class:`RPCError ` ``ValueError``: If the message doesn't contain any downloadable media """ return self._client.download_media( @@ -961,28 +2909,36 @@ class Message(PyrogramType, Update): progress_args=progress_args, ) + def pin(self, disable_notification: bool = None) -> "Message": + """Bound method *pin* of :obj:`Message `. -class Str(str): - def __init__(self, *args): - super().__init__() + Use as a shortcut for: - self.client = None - self.entities = None + .. code-block:: python - def init(self, client, entities): - self.client = client - self.entities = entities + client.pin_chat_message( + chat_id=message.chat.id, + message_id=message_id + ) - return self + Example: + .. code-block:: python - @property - def text(self): - return self + message.pin() - @property - def markdown(self): - return self.client.markdown.unparse(self, self.entities) + Args: + disable_notification (``bool``): + Pass True, if it is not necessary to send a notification to all chat members about the new pinned + message. Notifications are always disabled in channels. - @property - def html(self): - return self.client.html.unparse(self, self.entities) + Returns: + True on success. + + Raises: + :class:`RPCError ` + """ + return self._client.pin_chat_message( + chat_id=self.chat.id, + message_id=self.message_id, + disable_notification=disable_notification + ) diff --git a/pyrogram/client/types/messages_and_media/message_entity.py b/pyrogram/client/types/messages_and_media/message_entity.py index 88beeb2f..160d0d1e 100644 --- a/pyrogram/client/types/messages_and_media/message_entity.py +++ b/pyrogram/client/types/messages_and_media/message_entity.py @@ -47,6 +47,8 @@ class MessageEntity(PyrogramType): For "text_mention" only, the mentioned user. """ + __slots__ = ["type", "offset", "length", "url", "user"] + ENTITIES = { types.MessageEntityMention.ID: "mention", types.MessageEntityHashtag.ID: "hashtag", @@ -63,14 +65,16 @@ class MessageEntity(PyrogramType): types.MessageEntityPhone.ID: "phone_number" } - def __init__(self, - *, - client: "pyrogram.client.ext.BaseClient", - type: str, - offset: int, - length: int, - url: str = None, - user: User = None): + def __init__( + self, + *, + client: "pyrogram.client.ext.BaseClient", + type: str, + offset: int, + length: int, + url: str = None, + user: User = None + ): super().__init__(client) self.type = type diff --git a/pyrogram/client/types/messages_and_media/messages.py b/pyrogram/client/types/messages_and_media/messages.py index d89f0bad..da1a2676 100644 --- a/pyrogram/client/types/messages_and_media/messages.py +++ b/pyrogram/client/types/messages_and_media/messages.py @@ -16,7 +16,7 @@ # You should have received a copy of the GNU Lesser General Public License # along with Pyrogram. If not, see . -from typing import List +from typing import List, Union import pyrogram from pyrogram.api import types @@ -37,11 +37,15 @@ class Messages(PyrogramType, Update): Requested messages. """ - def __init__(self, - *, - client: "pyrogram.client.ext.BaseClient", - total_count: int, - messages: List[Message]): + __slots__ = ["total_count", "messages"] + + def __init__( + self, + *, + client: "pyrogram.client.ext.BaseClient", + total_count: int, + messages: List[Message] + ): super().__init__(client) self.total_count = total_count @@ -112,3 +116,55 @@ class Messages(PyrogramType, Update): messages=parsed_messages, client=client ) + + def forward( + self, + chat_id: Union[int, str], + disable_notification: bool = None, + as_copy: bool = False, + remove_caption: bool = False + ): + """Bound method *forward* of :obj:`Message `. + + 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). + + disable_notification (``bool``, *optional*): + Sends messages silently. + Users will receive a notification with no sound. + + as_copy (``bool``, *optional*): + Pass True to forward messages without the forward header (i.e.: send a copy of the message content). + Defaults to False. + + remove_caption (``bool``, *optional*): + If set to True and *as_copy* is enabled as well, media captions are not preserved when copying the + message. Has no effect if *as_copy* is not enabled. + Defaults to False. + + Returns: + On success, a :class:`Messages ` containing forwarded messages is returned. + + Raises: + :class:`RPCError ` + """ + forwarded_messages = [] + + for message in self.messages: + forwarded_messages.append( + message.forward( + chat_id=chat_id, + as_copy=as_copy, + disable_notification=disable_notification, + remove_caption=remove_caption + ) + ) + + return Messages( + total_count=len(forwarded_messages), + messages=forwarded_messages, + client=self._client + ) diff --git a/pyrogram/client/types/messages_and_media/photo.py b/pyrogram/client/types/messages_and_media/photo.py index 72187761..6f1852fb 100644 --- a/pyrogram/client/types/messages_and_media/photo.py +++ b/pyrogram/client/types/messages_and_media/photo.py @@ -41,12 +41,16 @@ class Photo(PyrogramType): Available sizes of this photo. """ - def __init__(self, - *, - client: "pyrogram.client.ext.BaseClient", - id: str, - date: int, - sizes: List[PhotoSize]): + __slots__ = ["id", "date", "sizes"] + + def __init__( + self, + *, + client: "pyrogram.client.ext.BaseClient", + id: str, + date: int, + sizes: List[PhotoSize] + ): super().__init__(client) self.id = id @@ -61,7 +65,6 @@ class Photo(PyrogramType): for raw_size in raw_sizes: if isinstance(raw_size, (types.PhotoSize, types.PhotoCachedSize)): - if isinstance(raw_size, types.PhotoSize): file_size = raw_size.size elif isinstance(raw_size, types.PhotoCachedSize): diff --git a/pyrogram/client/types/messages_and_media/photo_size.py b/pyrogram/client/types/messages_and_media/photo_size.py index 05f00455..10d00a86 100644 --- a/pyrogram/client/types/messages_and_media/photo_size.py +++ b/pyrogram/client/types/messages_and_media/photo_size.py @@ -17,6 +17,7 @@ # along with Pyrogram. If not, see . from struct import pack +from typing import List, Union import pyrogram from pyrogram.api import types @@ -41,13 +42,17 @@ class PhotoSize(PyrogramType): File size. """ - def __init__(self, - *, - client: "pyrogram.client.ext.BaseClient", - file_id: str, - width: int, - height: int, - file_size: int): + __slots__ = ["file_id", "width", "height", "file_size"] + + def __init__( + self, + *, + client: "pyrogram.client.ext.BaseClient", + file_id: str, + width: int, + height: int, + file_size: int + ): super().__init__(client) self.file_id = file_id @@ -56,27 +61,30 @@ class PhotoSize(PyrogramType): self.file_size = file_size @staticmethod - def _parse(client, photo_size: types.PhotoSize or types.PhotoCachedSize): - if isinstance(photo_size, (types.PhotoSize, types.PhotoCachedSize)): + def _parse(client, thumbs: List) -> Union["PhotoSize", None]: + if not thumbs: + return None - if isinstance(photo_size, types.PhotoSize): - file_size = photo_size.size - elif isinstance(photo_size, types.PhotoCachedSize): - file_size = len(photo_size.bytes) - else: - file_size = 0 + photo_size = thumbs[-1] - loc = photo_size.location + if not isinstance(photo_size, (types.PhotoSize, types.PhotoCachedSize, types.PhotoStrippedSize)): + return None - if isinstance(loc, types.FileLocation): - return PhotoSize( - file_id=encode( - pack( - ". from collections import OrderedDict -from json import dumps, JSONEncoder +from json import dumps + +import pyrogram class PyrogramType: - def __init__(self, client): + __slots__ = ["_client"] + + def __init__(self, client: "pyrogram.client.ext.BaseClient"): self._client = client def __str__(self): - return dumps(self, cls=Encoder, indent=4) + return dumps(self, indent=4, default=default, ensure_ascii=False) def __getitem__(self, item): return getattr(self, item) @@ -40,15 +44,9 @@ def remove_none(obj): return obj -class Encoder(JSONEncoder): - def default(self, o: PyrogramType): - try: - content = { - i: getattr(o, i) - for i in filter(lambda x: not x.startswith("_"), o.__dict__) - } - except AttributeError: - return repr(o) +def default(o: PyrogramType): + try: + content = {i: getattr(o, i) for i in o.__slots__} return remove_none( OrderedDict( @@ -56,3 +54,5 @@ class Encoder(JSONEncoder): + [i for i in content.items()] ) ) + except AttributeError: + return repr(o) diff --git a/pyrogram/client/types/update.py b/pyrogram/client/types/update.py index 2ec22f5a..48179ac0 100644 --- a/pyrogram/client/types/update.py +++ b/pyrogram/client/types/update.py @@ -26,6 +26,8 @@ class ContinuePropagation(StopIteration): class Update: + __slots__ = [] + def stop_propagation(self): raise StopPropagation diff --git a/pyrogram/client/types/user_and_chats/__init__.py b/pyrogram/client/types/user_and_chats/__init__.py index 24a9a51b..2059589a 100644 --- a/pyrogram/client/types/user_and_chats/__init__.py +++ b/pyrogram/client/types/user_and_chats/__init__.py @@ -19,9 +19,15 @@ from .chat import Chat from .chat_member import ChatMember from .chat_members import ChatMembers +from .chat_permissions import ChatPermissions from .chat_photo import ChatPhoto from .chat_preview import ChatPreview from .dialog import Dialog from .dialogs import Dialogs from .user import User from .user_status import UserStatus + +__all__ = [ + "Chat", "ChatMember", "ChatMembers", "ChatPermissions", "ChatPhoto", "ChatPreview", "Dialog", "Dialogs", "User", + "UserStatus" +] diff --git a/pyrogram/client/types/user_and_chats/chat.py b/pyrogram/client/types/user_and_chats/chat.py index ec30b866..a13f8a2b 100644 --- a/pyrogram/client/types/user_and_chats/chat.py +++ b/pyrogram/client/types/user_and_chats/chat.py @@ -16,8 +16,11 @@ # You should have received a copy of the GNU Lesser General Public License # along with Pyrogram. If not, see . +from typing import Union + import pyrogram from pyrogram.api import types +from .chat_permissions import ChatPermissions from .chat_photo import ChatPhoto from ..pyrogram_type import PyrogramType @@ -44,9 +47,6 @@ class Chat(PyrogramType): last_name (``str``, *optional*): Last name of the other party in a private chat. - all_members_are_administrators (``bool``, *optional*): - True if a basic group has "All Members Are Admins" enabled. - photo (:obj:`ChatPhoto `, *optional*): Chat photo. Suitable for downloads only. @@ -75,26 +75,37 @@ class Chat(PyrogramType): restriction_reason (``str``, *optional*): The reason why this chat might be unavailable to some users. + + permissions (:obj:`ChatPermissions ` *optional*): + Information about the chat default permissions. """ - def __init__(self, - *, - client: "pyrogram.client.ext.BaseClient", - id: int, - type: str, - title: str = None, - username: str = None, - first_name: str = None, - last_name: str = None, - all_members_are_administrators: bool = None, - photo: ChatPhoto = None, - description: str = None, - invite_link: str = None, - pinned_message=None, - sticker_set_name: str = None, - can_set_sticker_set: bool = None, - members_count: int = None, - restriction_reason: str = None): + __slots__ = [ + "id", "type", "title", "username", "first_name", "last_name", "photo", "description", "invite_link", + "pinned_message", "sticker_set_name", "can_set_sticker_set", "members_count", "restriction_reason", + "permissions" + ] + + def __init__( + self, + *, + client: "pyrogram.client.ext.BaseClient", + id: int, + type: str, + title: str = None, + username: str = None, + first_name: str = None, + last_name: str = None, + photo: ChatPhoto = None, + description: str = None, + invite_link: str = None, + pinned_message=None, + sticker_set_name: str = None, + can_set_sticker_set: bool = None, + members_count: int = None, + restriction_reason: str = None, + permissions: "pyrogram.ChatPermissions" = None + ): super().__init__(client) self.id = id @@ -103,7 +114,6 @@ class Chat(PyrogramType): self.username = username self.first_name = first_name self.last_name = last_name - self.all_members_are_administrators = all_members_are_administrators self.photo = photo self.description = description self.invite_link = invite_link @@ -112,6 +122,7 @@ class Chat(PyrogramType): self.can_set_sticker_set = can_set_sticker_set self.members_count = members_count self.restriction_reason = restriction_reason + self.permissions = permissions @staticmethod def _parse_user_chat(client, user: types.User) -> "Chat": @@ -128,17 +139,12 @@ class Chat(PyrogramType): @staticmethod def _parse_chat_chat(client, chat: types.Chat) -> "Chat": - admins_enabled = getattr(chat, "admins_enabled", None) - - if admins_enabled is not None: - admins_enabled = not admins_enabled - return Chat( id=-chat.id, type="group", title=chat.title, - all_members_are_administrators=admins_enabled, photo=ChatPhoto._parse(client, getattr(chat, "photo", None)), + permissions=ChatPermissions._parse(getattr(chat, "default_banned_rights", None)), client=client ) @@ -151,6 +157,7 @@ class Chat(PyrogramType): username=getattr(channel, "username", None), photo=ChatPhoto._parse(client, getattr(channel, "photo", None)), restriction_reason=getattr(channel, "restriction_reason", None), + permissions=ChatPermissions._parse(getattr(channel, "default_banned_rights", None)), client=client ) @@ -197,7 +204,7 @@ class Chat(PyrogramType): parsed_chat.description = full_chat.about or None # TODO: Add StickerSet type parsed_chat.can_set_sticker_set = full_chat.can_set_stickers - parsed_chat.sticker_set_name = full_chat.stickerset + parsed_chat.sticker_set_name = getattr(full_chat.stickerset, "short_name", None) if full_chat.pinned_msg_id: parsed_chat.pinned_message = client.get_messages( @@ -209,3 +216,12 @@ class Chat(PyrogramType): parsed_chat.invite_link = full_chat.exported_invite.link return parsed_chat + + @staticmethod + def _parse_chat(client, chat: Union[types.Chat, types.User, types.Channel]) -> "Chat": + if isinstance(chat, types.Chat): + return Chat._parse_chat_chat(client, chat) + elif isinstance(chat, types.User): + return Chat._parse_user_chat(client, chat) + else: + return Chat._parse_channel_chat(client, chat) diff --git a/pyrogram/client/types/user_and_chats/chat_member.py b/pyrogram/client/types/user_and_chats/chat_member.py index 70f32540..35911210 100644 --- a/pyrogram/client/types/user_and_chats/chat_member.py +++ b/pyrogram/client/types/user_and_chats/chat_member.py @@ -30,8 +30,8 @@ class ChatMember(PyrogramType): Information about the user. status (``str``): - The member's status in the chat. Can be "creator", "administrator", "member", "restricted", - "left" or "kicked". + The member's status in the chat. + Can be "creator", "administrator", "member", "restricted", "left" or "kicked". date (``int``, *optional*): Date when the user joined, unix time. Not available for creator. @@ -46,77 +46,25 @@ class ChatMember(PyrogramType): restricted_by (:obj:`User `, *optional*): Restricted and kicked only. Information about the user who restricted or kicked this member. - until_date (``int``, *optional*): - Restricted and kicked only. Date when restrictions will be lifted for this user, unix time. - - can_be_edited (``bool``, *optional*): - Administrators only. True, if the bot is allowed to edit administrator privileges of that user. - - can_change_info (``bool``, *optional*): - Administrators only. True, if the administrator can change the chat title, photo and other settings. - - can_post_messages (``bool``, *optional*): - Administrators only. True, if the administrator can post in the channel, channels only. - - can_edit_messages (``bool``, *optional*): - Administrators only. True, if the administrator can edit messages of other users and can pin messages, - channels only. - - can_delete_messages (``bool``, *optional*): - Administrators only. True, if the administrator can delete messages of other users. - - can_invite_users (``bool``, *optional*): - Administrators only. True, if the administrator can invite new users to the chat. - - can_restrict_members (``bool``, *optional*): - Administrators only. True, if the administrator can restrict, ban or unban chat members. - - can_pin_messages (``bool``, *optional*): - Administrators only. True, if the administrator can pin messages, supergroups only. - - can_promote_members (``bool``, *optional*): - Administrators only. True, if the administrator can add new administrators with a subset of his - own privileges or demote administrators that he has promoted, directly or indirectly (promoted by - administrators that were appointed by the user). - - can_send_messages (``bool``, *optional*): - Restricted only. True, if the user can send text messages, contacts, locations and venues. - - can_send_media_messages (``bool``, *optional*): - Restricted only. True, if the user can send audios, documents, photos, videos, video notes and voice notes, - implies can_send_messages. - - can_send_other_messages (``bool``, *optional*): - Restricted only. True, if the user can send animations, games, stickers and use inline bots, implies - can_send_media_messages. - - can_add_web_page_previews (``bool``, *optional*): - Restricted only. True, if user may add web page previews to his messages, implies can_send_media_messages. + permissions (:obj:`ChatPermissions ` *optional*): + Administrators, restricted and kicked members only. + Information about the member permissions. """ - def __init__(self, - *, - client: "pyrogram.client.ext.BaseClient", - user: "pyrogram.User", - status: str, - date: int = None, - invited_by: "pyrogram.User" = None, - promoted_by: "pyrogram.User" = None, - restricted_by: "pyrogram.User" = None, - until_date: int = None, - can_be_edited: bool = None, - can_change_info: bool = None, - can_post_messages: bool = None, - can_edit_messages: bool = None, - can_delete_messages: bool = None, - can_invite_users: bool = None, - can_restrict_members: bool = None, - can_pin_messages: bool = None, - can_promote_members: bool = None, - can_send_messages: bool = None, - can_send_media_messages: bool = None, - can_send_other_messages: bool = None, - can_add_web_page_previews: bool = None): + __slots__ = ["user", "status", "date", "invited_by", "promoted_by", "restricted_by", "permissions"] + + def __init__( + self, + *, + client: "pyrogram.client.ext.BaseClient", + user: "pyrogram.User", + status: str, + date: int = None, + invited_by: "pyrogram.User" = None, + promoted_by: "pyrogram.User" = None, + restricted_by: "pyrogram.User" = None, + permissions: "pyrogram.ChatPermissions" = None + ): super().__init__(client) self.user = user @@ -125,79 +73,63 @@ class ChatMember(PyrogramType): self.invited_by = invited_by self.promoted_by = promoted_by self.restricted_by = restricted_by - self.until_date = until_date - self.can_be_edited = can_be_edited - self.can_change_info = can_change_info - self.can_post_messages = can_post_messages - self.can_edit_messages = can_edit_messages - self.can_delete_messages = can_delete_messages - self.can_invite_users = can_invite_users - self.can_restrict_members = can_restrict_members - self.can_pin_messages = can_pin_messages - self.can_promote_members = can_promote_members - self.can_send_messages = can_send_messages - self.can_send_media_messages = can_send_media_messages - self.can_send_other_messages = can_send_other_messages - self.can_add_web_page_previews = can_add_web_page_previews + self.permissions = permissions @staticmethod def _parse(client, member, users) -> "ChatMember": user = pyrogram.User._parse(client, users[member.user_id]) - invited_by = pyrogram.User._parse(client, users[member.inviter_id]) if hasattr(member, "inviter_id") else None + + invited_by = ( + pyrogram.User._parse(client, users[member.inviter_id]) + if getattr(member, "inviter_id", None) else None + ) if isinstance(member, (types.ChannelParticipant, types.ChannelParticipantSelf, types.ChatParticipant)): - return ChatMember(user=user, status="member", date=member.date, invited_by=invited_by, client=client) + return ChatMember( + user=user, + status="member", + date=member.date, + invited_by=invited_by, + client=client + ) if isinstance(member, (types.ChannelParticipantCreator, types.ChatParticipantCreator)): - return ChatMember(user=user, status="creator", client=client) + return ChatMember( + user=user, + status="creator", + client=client + ) if isinstance(member, types.ChatParticipantAdmin): - return ChatMember(user=user, status="administrator", date=member.date, invited_by=invited_by, client=client) + return ChatMember( + user=user, + status="administrator", + date=member.date, + invited_by=invited_by, + client=client + ) if isinstance(member, types.ChannelParticipantAdmin): - rights = member.admin_rights - return ChatMember( user=user, status="administrator", date=member.date, invited_by=invited_by, promoted_by=pyrogram.User._parse(client, users[member.promoted_by]), - can_be_edited=member.can_edit, - can_change_info=rights.change_info, - can_post_messages=rights.post_messages, - can_edit_messages=rights.edit_messages, - can_delete_messages=rights.delete_messages, - can_invite_users=rights.invite_users or rights.invite_link, - can_restrict_members=rights.ban_users, - can_pin_messages=rights.pin_messages, - can_promote_members=rights.add_admins, + permissions=pyrogram.ChatPermissions._parse(member), client=client ) if isinstance(member, types.ChannelParticipantBanned): - rights = member.banned_rights - - chat_member = ChatMember( + return ChatMember( user=user, status=( - "kicked" if rights.view_messages + "kicked" if member.banned_rights.view_messages else "left" if member.left else "restricted" ), date=member.date, restricted_by=pyrogram.User._parse(client, users[member.kicked_by]), - until_date=0 if rights.until_date == (1 << 31) - 1 else rights.until_date, + permissions=pyrogram.ChatPermissions._parse(member), client=client ) - - if chat_member.status == "restricted": - chat_member.can_send_messages = not rights.send_messages - chat_member.can_send_media_messages = not rights.send_media - chat_member.can_send_other_messages = ( - not rights.send_stickers or not rights.send_gifs or - not rights.send_games or not rights.send_inline - ) - chat_member.can_add_web_page_previews = not rights.embed_links - - return chat_member diff --git a/pyrogram/client/types/user_and_chats/chat_members.py b/pyrogram/client/types/user_and_chats/chat_members.py index 39d69089..3c89b124 100644 --- a/pyrogram/client/types/user_and_chats/chat_members.py +++ b/pyrogram/client/types/user_and_chats/chat_members.py @@ -21,7 +21,6 @@ from typing import List import pyrogram from pyrogram.api import types from .chat_member import ChatMember -from .user import User from ..pyrogram_type import PyrogramType @@ -36,11 +35,15 @@ class ChatMembers(PyrogramType): Requested chat members. """ - def __init__(self, - *, - client: "pyrogram.client.ext.BaseClient", - total_count: int, - chat_members: List[ChatMember]): + __slots__ = ["total_count", "chat_members"] + + def __init__( + self, + *, + client: "pyrogram.client.ext.BaseClient", + total_count: int, + chat_members: List[ChatMember] + ): super().__init__(client) self.total_count = total_count diff --git a/pyrogram/client/types/user_and_chats/chat_permissions.py b/pyrogram/client/types/user_and_chats/chat_permissions.py new file mode 100644 index 00000000..7b35b1d0 --- /dev/null +++ b/pyrogram/client/types/user_and_chats/chat_permissions.py @@ -0,0 +1,189 @@ +# 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 Union + +from pyrogram.api import types +from ..pyrogram_type import PyrogramType + + +class ChatPermissions(PyrogramType): + """This object represents both a chat default permissions and a single member permissions within a chat. + + Some permissions make sense depending on the context: default chat permissions, restricted/kicked member or + administrators in groups or channels. + + Args: + until_date (``int``, *optional*): + Applicable to restricted and kicked members only. + Date when user restrictions will be lifted, unix time. + 0 means the restrictions will never be lifted (user restricted forever). + + can_be_edited (``bool``, *optional*): + Applicable to administrators only. + True, if you are allowed to edit administrator privileges of the user. + + can_change_info (``bool``, *optional*): + Applicable to default chat permissions in private groups and administrators in public groups only. + True, if the chat title, photo and other settings can be changed. + + can_post_messages (``bool``, *optional*): + Applicable to channel administrators only. + True, if the administrator can post messages in the channel, channels only. + + can_edit_messages (``bool``, *optional*): + Applicable to channel administrators only. + True, if the administrator can edit messages of other users and can pin messages, channels only. + + can_delete_messages (``bool``, *optional*): + Applicable to administrators only. + True, if the administrator can delete messages of other users. + + can_restrict_members (``bool``, *optional*): + Applicable to administrators only. + True, if the administrator can restrict, ban or unban chat members. + + can_invite_users (``bool``, *optional*): + Applicable to default chat permissions and administrators only. + True, if new users can be invited to the chat. + + can_pin_messages (``bool``, *optional*): + Applicable to default chat permissions in private groups and administrators in public groups only. + True, if messages can be pinned, supergroups only. + + can_promote_members (``bool``, *optional*): + Applicable to administrators only. + True, if the administrator can add new administrators with a subset of his own privileges or demote + administrators that he has promoted, directly or indirectly (promoted by administrators that were appointed + by the user). + + can_send_messages (``bool``, *optional*): + Applicable to default chat permissions and restricted members only. + True, if text messages, contacts, locations and venues can be sent. + + can_send_media_messages (``bool``, *optional*): + Applicable to default chat permissions and restricted members only. + True, if audios, documents, photos, videos, video notes and voice notes can be sent, implies + can_send_messages. + + can_send_other_messages (``bool``, *optional*): + Applicable to default chat permissions and restricted members only. + True, if animations, games, stickers and inline bot results can be sent, implies can_send_media_messages. + + can_add_web_page_previews (``bool``, *optional*): + Applicable to default chat permissions and restricted members only. + True, if web page previews can be attached to text messages, implies can_send_media_messages. + + can_send_polls (``bool``, *optional*): + Applicable to default chat permissions and restricted members only. + True, if polls can be sent, implies can_send_media_messages. + """ + + __slots__ = [ + "until_date", "can_be_edited", "can_change_info", "can_post_messages", "can_edit_messages", + "can_delete_messages", "can_restrict_members", "can_invite_users", "can_pin_messages", "can_promote_members", + "can_send_messages", "can_send_media_messages", "can_send_other_messages", "can_add_web_page_previews", + "can_send_polls" + ] + + def __init__( + self, + *, + until_date: int = None, + + # Admin permissions + can_be_edited: bool = None, + can_change_info: bool = None, + can_post_messages: bool = None, # Channels only + can_edit_messages: bool = None, # Channels only + can_delete_messages: bool = None, + can_restrict_members: bool = None, + can_invite_users: bool = None, + can_pin_messages: bool = None, # Supergroups only + can_promote_members: bool = None, + + # Restricted user permissions + can_send_messages: bool = None, # Text, contacts, locations and venues + can_send_media_messages: bool = None, # Audios, documents, photos, videos, video notes and voice notes + can_send_other_messages: bool = None, # Animations (GIFs), games, stickers, inline bot results + can_add_web_page_previews: bool = None, + can_send_polls: bool = None + ): + super().__init__(None) + + self.until_date = until_date + self.can_be_edited = can_be_edited + + self.can_change_info = can_change_info + self.can_post_messages = can_post_messages + self.can_edit_messages = can_edit_messages + self.can_delete_messages = can_delete_messages + self.can_restrict_members = can_restrict_members + self.can_invite_users = can_invite_users + self.can_pin_messages = can_pin_messages + self.can_promote_members = can_promote_members + + self.can_send_messages = can_send_messages + self.can_send_media_messages = can_send_media_messages + self.can_send_other_messages = can_send_other_messages + self.can_add_web_page_previews = can_add_web_page_previews + self.can_send_polls = can_send_polls + + @staticmethod + def _parse( + entity: Union[ + types.ChannelParticipantAdmin, + types.ChannelParticipantBanned, + types.ChatBannedRights + ] + ) -> "ChatPermissions": + if isinstance(entity, types.ChannelParticipantAdmin): + permissions = entity.admin_rights + + return ChatPermissions( + can_be_edited=entity.can_edit, + can_change_info=permissions.change_info, + can_post_messages=permissions.post_messages, + can_edit_messages=permissions.edit_messages, + can_delete_messages=permissions.delete_messages, + can_restrict_members=permissions.ban_users, + can_invite_users=permissions.invite_users, + can_pin_messages=permissions.pin_messages, + can_promote_members=permissions.add_admins + ) + + if isinstance(entity, (types.ChannelParticipantBanned, types.ChatBannedRights)): + if isinstance(entity, types.ChannelParticipantBanned): + denied_permissions = entity.banned_rights # type: types.ChatBannedRights + else: + denied_permissions = entity + + return ChatPermissions( + until_date=0 if denied_permissions.until_date == (1 << 31) - 1 else denied_permissions.until_date, + can_send_messages=not denied_permissions.send_messages, + can_send_media_messages=not denied_permissions.send_media, + can_send_other_messages=( + not denied_permissions.send_stickers or not denied_permissions.send_gifs or + not denied_permissions.send_games or not denied_permissions.send_inline + ), + can_add_web_page_previews=not denied_permissions.embed_links, + can_send_polls=not denied_permissions.send_polls, + can_change_info=not denied_permissions.change_info, + can_invite_users=not denied_permissions.invite_users, + can_pin_messages=not denied_permissions.pin_messages + ) diff --git a/pyrogram/client/types/user_and_chats/chat_photo.py b/pyrogram/client/types/user_and_chats/chat_photo.py index 848f7250..6fbc779d 100644 --- a/pyrogram/client/types/user_and_chats/chat_photo.py +++ b/pyrogram/client/types/user_and_chats/chat_photo.py @@ -35,11 +35,15 @@ class ChatPhoto(PyrogramType): Unique file identifier of big (640x640) chat photo. This file_id can be used only for photo download. """ - def __init__(self, - *, - client: "pyrogram.client.ext.BaseClient", - small_file_id: str, - big_file_id: str): + __slots__ = ["small_file_id", "big_file_id"] + + def __init__( + self, + *, + client: "pyrogram.client.ext.BaseClient", + small_file_id: str, + big_file_id: str + ): super().__init__(client) self.small_file_id = small_file_id diff --git a/pyrogram/client/types/user_and_chats/chat_preview.py b/pyrogram/client/types/user_and_chats/chat_preview.py index 45048637..ddd84b09 100644 --- a/pyrogram/client/types/user_and_chats/chat_preview.py +++ b/pyrogram/client/types/user_and_chats/chat_preview.py @@ -45,14 +45,18 @@ class ChatPreview(PyrogramType): Preview of some of the chat members. """ - def __init__(self, - *, - client: "pyrogram.client.ext.BaseClient", - title: str, - photo: ChatPhoto, - type: str, - members_count: int, - members: List[User] = None): + __slots__ = ["title", "photo", "type", "members_count", "members"] + + def __init__( + self, + *, + client: "pyrogram.client.ext.BaseClient", + title: str, + photo: ChatPhoto, + type: str, + members_count: int, + members: List[User] = None + ): super().__init__(client) self.title = title diff --git a/pyrogram/client/types/user_and_chats/dialog.py b/pyrogram/client/types/user_and_chats/dialog.py index b71caba7..1bbd3b4b 100644 --- a/pyrogram/client/types/user_and_chats/dialog.py +++ b/pyrogram/client/types/user_and_chats/dialog.py @@ -46,15 +46,19 @@ class Dialog(PyrogramType): True, if the dialog is pinned. """ - def __init__(self, - *, - client: "pyrogram.client.ext.BaseClient", - chat: Chat, - top_message: "pyrogram.Message", - unread_messages_count: int, - unread_mentions_count: int, - unread_mark: bool, - is_pinned: bool): + __slots__ = ["chat", "top_message", "unread_messages_count", "unread_mentions_count", "unread_mark", "is_pinned"] + + def __init__( + self, + *, + client: "pyrogram.client.ext.BaseClient", + chat: Chat, + top_message: "pyrogram.Message", + unread_messages_count: int, + unread_mentions_count: int, + unread_mark: bool, + is_pinned: bool + ): super().__init__(client) self.chat = chat diff --git a/pyrogram/client/types/user_and_chats/dialogs.py b/pyrogram/client/types/user_and_chats/dialogs.py index 394ddd28..431cca8d 100644 --- a/pyrogram/client/types/user_and_chats/dialogs.py +++ b/pyrogram/client/types/user_and_chats/dialogs.py @@ -26,7 +26,7 @@ from ..pyrogram_type import PyrogramType class Dialogs(PyrogramType): - """This object represents a user's dialogs chunk + """This object represents a user's dialogs chunk. Args: total_count (``int``): @@ -36,11 +36,15 @@ class Dialogs(PyrogramType): Requested dialogs. """ - def __init__(self, - *, - client: "pyrogram.client.ext.BaseClient", - total_count: int, - dialogs: List[Dialog]): + __slots__ = ["total_count", "dialogs"] + + def __init__( + self, + *, + client: "pyrogram.client.ext.BaseClient", + total_count: int, + dialogs: List[Dialog] + ): super().__init__(client) self.total_count = total_count diff --git a/pyrogram/client/types/user_and_chats/user.py b/pyrogram/client/types/user_and_chats/user.py index c6ee03e8..5718b917 100644 --- a/pyrogram/client/types/user_and_chats/user.py +++ b/pyrogram/client/types/user_and_chats/user.py @@ -70,23 +70,30 @@ class User(PyrogramType): The reason why this bot might be unavailable to some users. """ - def __init__(self, - *, - client: "pyrogram.client.ext.BaseClient", - id: int, - is_self: bool, - is_contact: bool, - is_mutual_contact: bool, - is_deleted: bool, - is_bot: bool, - first_name: str, - last_name: str = None, - status: UserStatus = None, - username: str = None, - language_code: str = None, - phone_number: str = None, - photo: ChatPhoto = None, - restriction_reason: str = None): + __slots__ = [ + "id", "is_self", "is_contact", "is_mutual_contact", "is_deleted", "is_bot", "first_name", "last_name", "status", + "username", "language_code", "phone_number", "photo", "restriction_reason" + ] + + def __init__( + self, + *, + client: "pyrogram.client.ext.BaseClient", + id: int, + is_self: bool, + is_contact: bool, + is_mutual_contact: bool, + is_deleted: bool, + is_bot: bool, + first_name: str, + last_name: str = None, + status: UserStatus = None, + username: str = None, + language_code: str = None, + phone_number: str = None, + photo: ChatPhoto = None, + restriction_reason: str = None + ): super().__init__(client) self.id = id diff --git a/pyrogram/client/types/user_and_chats/user_status.py b/pyrogram/client/types/user_and_chats/user_status.py index 8c936f8e..170ce373 100644 --- a/pyrogram/client/types/user_and_chats/user_status.py +++ b/pyrogram/client/types/user_and_chats/user_status.py @@ -65,17 +65,21 @@ class UserStatus(PyrogramType, Update): always shown to blocked users), None otherwise. """ - def __init__(self, - *, - client: "pyrogram.client.ext.BaseClient", - user_id: int, - online: bool = None, - offline: bool = None, - date: int = None, - recently: bool = None, - within_week: bool = None, - within_month: bool = None, - long_time_ago: bool = None): + __slots__ = ["user_id", "online", "offline", "date", "recently", "within_week", "within_month", "long_time_ago"] + + def __init__( + self, + *, + client: "pyrogram.client.ext.BaseClient", + user_id: int, + online: bool = None, + offline: bool = None, + date: int = None, + recently: bool = None, + within_week: bool = None, + within_month: bool = None, + long_time_ago: bool = None + ): super().__init__(client) self.user_id = user_id diff --git a/pyrogram/connection/transport/tcp/tcp_abridged_o.py b/pyrogram/connection/transport/tcp/tcp_abridged_o.py index d15d0389..136d22ef 100644 --- a/pyrogram/connection/transport/tcp/tcp_abridged_o.py +++ b/pyrogram/connection/transport/tcp/tcp_abridged_o.py @@ -40,9 +40,7 @@ class TCPAbridgedO(TCP): while True: nonce = bytearray(os.urandom(64)) - if (nonce[0] != b"\xef" - and nonce[:4] not in self.RESERVED - and nonce[4:4] != b"\x00" * 4): + if nonce[0] != b"\xef" and nonce[:4] not in self.RESERVED and nonce[4:4] != b"\x00" * 4: nonce[56] = nonce[57] = nonce[58] = nonce[59] = 0xef break diff --git a/pyrogram/connection/transport/tcp/tcp_intermediate_o.py b/pyrogram/connection/transport/tcp/tcp_intermediate_o.py index c59deed7..a92acb7f 100644 --- a/pyrogram/connection/transport/tcp/tcp_intermediate_o.py +++ b/pyrogram/connection/transport/tcp/tcp_intermediate_o.py @@ -41,9 +41,7 @@ class TCPIntermediateO(TCP): while True: nonce = bytearray(os.urandom(64)) - if (nonce[0] != b"\xef" - and nonce[:4] not in self.RESERVED - and nonce[4:4] != b"\x00" * 4): + if nonce[0] != b"\xef" and nonce[:4] not in self.RESERVED and nonce[4:4] != b"\x00" * 4: nonce[56] = nonce[57] = nonce[58] = nonce[59] = 0xee break diff --git a/pyrogram/api/errors/__init__.py b/pyrogram/errors/__init__.py similarity index 95% rename from pyrogram/api/errors/__init__.py rename to pyrogram/errors/__init__.py index ca65619c..a34d7078 100644 --- a/pyrogram/api/errors/__init__.py +++ b/pyrogram/errors/__init__.py @@ -17,4 +17,4 @@ # along with Pyrogram. If not, see . from .exceptions import * -from .error import UnknownError +from .rpc_error import UnknownError diff --git a/pyrogram/api/errors/error.py b/pyrogram/errors/rpc_error.py similarity index 88% rename from pyrogram/api/errors/error.py rename to pyrogram/errors/rpc_error.py index 5f92a369..c1799f50 100644 --- a/pyrogram/api/errors/error.py +++ b/pyrogram/errors/rpc_error.py @@ -19,11 +19,11 @@ import re from importlib import import_module -from pyrogram.api.types import RpcError +from pyrogram.api.types import RpcError as RawRPCError from .exceptions.all import exceptions -class Error(Exception): +class RPCError(Exception): """This is the base exception class for all Telegram API related errors. For a finer grained control, see the specific errors below. """ @@ -32,7 +32,7 @@ class Error(Exception): NAME = None MESSAGE = None - def __init__(self, x: int or RpcError = None, query_type: type = None): + def __init__(self, x: int or RawRPCError = None, query_type: type = None): super().__init__("[{} {}]: {}".format( self.CODE, self.ID or self.NAME, @@ -50,7 +50,7 @@ class Error(Exception): f.write("{}\t{}\t{}\n".format(x.error_code, x.error_message, query_type)) @staticmethod - def raise_it(rpc_error: RpcError, query_type: type): + def raise_it(rpc_error: RawRPCError, query_type: type): code = rpc_error.error_code if code not in exceptions: @@ -66,12 +66,12 @@ class Error(Exception): x = x.group(1) if x is not None else x raise getattr( - import_module("pyrogram.api.errors"), + import_module("pyrogram.errors"), exceptions[code][id] )(x=x) -class UnknownError(Error): +class UnknownError(RPCError): """This object represents an Unknown Error, that is, an error which Pyrogram does not know anything about, yet. """ diff --git a/pyrogram/session/auth.py b/pyrogram/session/auth.py index 05b20eec..89e5b61f 100644 --- a/pyrogram/session/auth.py +++ b/pyrogram/session/auth.py @@ -83,7 +83,7 @@ class Auth: # Step 1; Step 2 nonce = int.from_bytes(urandom(16), "little", signed=True) log.debug("Send req_pq: {}".format(nonce)) - res_pq = self.send(functions.ReqPqMulti(nonce)) + res_pq = self.send(functions.ReqPqMulti(nonce=nonce)) log.debug("Got ResPq: {}".format(res_pq.server_nonce)) log.debug("Server public key fingerprints: {}".format(res_pq.server_public_key_fingerprints)) @@ -110,12 +110,12 @@ class Auth: new_nonce = int.from_bytes(urandom(32), "little", signed=True) data = types.PQInnerData( - res_pq.pq, - p.to_bytes(4, "big"), - q.to_bytes(4, "big"), - nonce, - server_nonce, - new_nonce, + pq=res_pq.pq, + p=p.to_bytes(4, "big"), + q=q.to_bytes(4, "big"), + nonce=nonce, + server_nonce=server_nonce, + new_nonce=new_nonce, ).write() sha = sha1(data).digest() @@ -129,12 +129,12 @@ class Auth: log.debug("Send req_DH_params") server_dh_params = self.send( functions.ReqDHParams( - nonce, - server_nonce, - p.to_bytes(4, "big"), - q.to_bytes(4, "big"), - public_key_fingerprint, - encrypted_data + nonce=nonce, + server_nonce=server_nonce, + p=p.to_bytes(4, "big"), + q=q.to_bytes(4, "big"), + public_key_fingerprint=public_key_fingerprint, + encrypted_data=encrypted_data ) ) @@ -175,10 +175,10 @@ class Auth: retry_id = 0 data = types.ClientDHInnerData( - nonce, - server_nonce, - retry_id, - g_b + nonce=nonce, + server_nonce=server_nonce, + retry_id=retry_id, + g_b=g_b ).write() sha = sha1(data).digest() @@ -189,9 +189,9 @@ class Auth: log.debug("Send set_client_DH_params") set_client_dh_params_answer = self.send( functions.SetClientDHParams( - nonce, - server_nonce, - encrypted_data + nonce=nonce, + server_nonce=server_nonce, + encrypted_data=encrypted_data ) ) diff --git a/pyrogram/session/session.py b/pyrogram/session/session.py index ffdb1893..8afaf7b6 100644 --- a/pyrogram/session/session.py +++ b/pyrogram/session/session.py @@ -31,9 +31,9 @@ from pyrogram import __copyright__, __license__, __version__ from pyrogram.api import functions, types, core from pyrogram.api.all import layer from pyrogram.api.core import Message, Object, MsgContainer, Long, FutureSalt, Int -from pyrogram.api.errors import Error, InternalServerError, AuthKeyDuplicated from pyrogram.connection import Connection from pyrogram.crypto import AES, KDF +from pyrogram.errors import RPCError, InternalServerError, AuthKeyDuplicated from .internals import MsgId, MsgFactory log = logging.getLogger(__name__) @@ -48,6 +48,7 @@ class Result: class Session: INITIAL_SALT = 0x616e67656c696361 NET_WORKERS = 1 + START_TIMEOUT = 1 WAIT_TIMEOUT = 15 MAX_RETRIES = 5 ACKS_THRESHOLD = 8 @@ -131,8 +132,14 @@ class Session: Thread(target=self.recv, name="RecvThread").start() self.current_salt = FutureSalt(0, 0, self.INITIAL_SALT) - self.current_salt = FutureSalt(0, 0, self._send(functions.Ping(0)).new_server_salt) - self.current_salt = self._send(functions.GetFutureSalts(1)).salts[0] + self.current_salt = FutureSalt( + 0, 0, + self._send( + functions.Ping(ping_id=0), + timeout=self.START_TIMEOUT + ).new_server_salt + ) + self.current_salt = self._send(functions.GetFutureSalts(num=1), timeout=self.START_TIMEOUT).salts[0] self.next_salt_thread = Thread(target=self.next_salt, name="NextSaltThread") self.next_salt_thread.start() @@ -140,8 +147,8 @@ class Session: if not self.is_cdn: self._send( functions.InvokeWithLayer( - layer, - functions.InitConnection( + layer=layer, + query=functions.InitConnection( api_id=self.client.api_id, app_version=self.client.app_version, device_model=self.client.device_model, @@ -151,7 +158,8 @@ class Session: lang_pack="", query=functions.help.GetConfig(), ) - ) + ), + timeout=self.START_TIMEOUT ) self.ping_thread = Thread(target=self.ping, name="PingThread") @@ -164,7 +172,7 @@ class Session: except AuthKeyDuplicated as e: self.stop() raise e - except (OSError, TimeoutError, Error): + except (OSError, TimeoutError, RPCError): self.stop() except Exception as e: self.stop() @@ -307,7 +315,7 @@ class Session: log.info("Send {} acks".format(len(self.pending_acks))) try: - self._send(types.MsgsAck(list(self.pending_acks)), False) + self._send(types.MsgsAck(msg_ids=list(self.pending_acks)), False) except (OSError, TimeoutError): pass else: @@ -328,9 +336,9 @@ class Session: try: self._send(functions.PingDelayDisconnect( - 0, self.WAIT_TIMEOUT + 10 + ping_id=0, disconnect_delay=self.WAIT_TIMEOUT + 10 ), False) - except (OSError, TimeoutError, Error): + except (OSError, TimeoutError, RPCError): pass log.debug("PingThread stopped") @@ -358,8 +366,8 @@ class Session: break try: - self.current_salt = self._send(functions.GetFutureSalts(1)).salts[0] - except (OSError, TimeoutError, Error): + self.current_salt = self._send(functions.GetFutureSalts(num=1)).salts[0] + except (OSError, TimeoutError, RPCError): self.connection.close() break @@ -405,7 +413,7 @@ class Session: if result is None: raise TimeoutError elif isinstance(result, types.RpcError): - Error.raise_it(result, type(data)) + RPCError.raise_it(result, type(data)) elif isinstance(result, types.BadMsgNotification): raise Exception(self.BAD_MSG_DESCRIPTION.get( result.error_code, diff --git a/pyrogram/vendor/typing/typing.py b/pyrogram/vendor/typing/typing.py index 2189cd48..16888d3f 100644 --- a/pyrogram/vendor/typing/typing.py +++ b/pyrogram/vendor/typing/typing.py @@ -1,17 +1,18 @@ import abc -from abc import abstractmethod, abstractproperty import collections import contextlib import functools import re as stdlib_re # Avoid confusion with the re we export. import sys import types +from abc import abstractmethod, abstractproperty + try: import collections.abc as collections_abc except ImportError: import collections as collections_abc # Fallback for PY3.2. if sys.version_info[:2] >= (3, 6): - import _collections_abc # Needed for private function _check_methods # noqa + pass try: from types import WrapperDescriptorType, MethodWrapperType, MethodDescriptorType except ImportError: @@ -19,7 +20,6 @@ except ImportError: MethodWrapperType = type(object().__str__) MethodDescriptorType = type(str.join) - # Please keep __all__ alphabetized within each category. __all__ = [ # Super-special typing primitives. @@ -36,7 +36,7 @@ __all__ = [ # ABCs (from collections.abc). 'AbstractSet', # collections.abc.Set. 'GenericMeta', # subclass of abc.ABCMeta and a metaclass - # for 'Generic' and ABCs below. + # for 'Generic' and ABCs below. 'ByteString', 'Container', 'ContextManager', @@ -96,6 +96,7 @@ __all__ = [ 'TYPE_CHECKING', ] + # The pseudo-submodules 're' and 'io' are part of the public # namespace, but excluded from __all__ because they might stomp on # legitimate imports of those modules. @@ -173,8 +174,8 @@ class _TypingBase(metaclass=TypingMeta, _root=True): someone tries to subclass a special typing object (not a good idea). """ if (len(args) == 3 and - isinstance(args[0], str) and - isinstance(args[1], tuple)): + isinstance(args[0], str) and + isinstance(args[1], tuple)): # Close enough. raise TypeError("Cannot subclass %r" % cls) return super().__new__(cls) @@ -396,7 +397,7 @@ def _type_repr(obj): return _qualname(obj) return '%s.%s' % (obj.__module__, _qualname(obj)) if obj is ...: - return('...') + return ('...') if isinstance(obj, types.FunctionType): return obj.__name__ return repr(obj) @@ -681,6 +682,7 @@ def _tp_cache(func): except TypeError: pass # All real errors (not unhashable args) are raised below. return func(*args, **kwds) + return inner @@ -948,7 +950,7 @@ class GenericMeta(TypingMeta, abc.ABCMeta): if base is Generic: raise TypeError("Cannot inherit from plain Generic") if (isinstance(base, GenericMeta) and - base.__origin__ is Generic): + base.__origin__ is Generic): if gvars is not None: raise TypeError( "Cannot inherit from Generic[...] multiple types.") @@ -1183,14 +1185,14 @@ def _generic_new(base_cls, cls, *args, **kwds): # but attempt to store it in __orig_class__ if cls.__origin__ is None: if (base_cls.__new__ is object.__new__ and - cls.__init__ is not object.__init__): + cls.__init__ is not object.__init__): return base_cls.__new__(cls) else: return base_cls.__new__(cls, *args, **kwds) else: origin = cls._gorg if (base_cls.__new__ is object.__new__ and - cls.__init__ is not object.__init__): + cls.__init__ is not object.__init__): obj = base_cls.__new__(origin) else: obj = base_cls.__new__(origin, *args, **kwds) @@ -1399,7 +1401,7 @@ class _ClassVar(_FinalTypingBase, _root=True): cls = type(self) if self.__type__ is None: return cls(_type_check(item, - '{} accepts only single type.'.format(cls.__name__[1:])), + '{} accepts only single type.'.format(cls.__name__[1:])), _root=True) raise TypeError('{} cannot be further subscripted' .format(cls.__name__[1:])) @@ -1671,26 +1673,26 @@ class _ProtocolMeta(GenericMeta): # Include attributes not defined in any non-protocol bases. for c in self.__mro__: if (c is not base and attr in c.__dict__ and - not getattr(c, '_is_protocol', False)): + not getattr(c, '_is_protocol', False)): break else: if (not attr.startswith('_abc_') and - attr != '__abstractmethods__' and - attr != '__annotations__' and - attr != '__weakref__' and - attr != '_is_protocol' and - attr != '_gorg' and - attr != '__dict__' and - attr != '__args__' and - attr != '__slots__' and - attr != '_get_protocol_attrs' and - attr != '__next_in_mro__' and - attr != '__parameters__' and - attr != '__origin__' and - attr != '__orig_bases__' and - attr != '__extra__' and - attr != '__tree_hash__' and - attr != '__module__'): + attr != '__abstractmethods__' and + attr != '__annotations__' and + attr != '__weakref__' and + attr != '_is_protocol' and + attr != '_gorg' and + attr != '__dict__' and + attr != '__args__' and + attr != '__slots__' and + attr != '_get_protocol_attrs' and + attr != '__next_in_mro__' and + attr != '__parameters__' and + attr != '__origin__' and + attr != '__orig_bases__' and + attr != '__extra__' and + attr != '__tree_hash__' and + attr != '__module__'): attrs.add(attr) return attrs @@ -1714,31 +1716,31 @@ class _Protocol(metaclass=_ProtocolMeta): Hashable = collections_abc.Hashable # Not generic. - if hasattr(collections_abc, 'Awaitable'): class Awaitable(Generic[T_co], extra=collections_abc.Awaitable): __slots__ = () - __all__.append('Awaitable') + __all__.append('Awaitable') if hasattr(collections_abc, 'Coroutine'): class Coroutine(Awaitable[V_co], Generic[T_co, T_contra, V_co], extra=collections_abc.Coroutine): __slots__ = () + __all__.append('Coroutine') - if hasattr(collections_abc, 'AsyncIterable'): - class AsyncIterable(Generic[T_co], extra=collections_abc.AsyncIterable): __slots__ = () + class AsyncIterator(AsyncIterable[T_co], extra=collections_abc.AsyncIterator): __slots__ = () + __all__.append('AsyncIterable') __all__.append('AsyncIterator') @@ -1810,7 +1812,6 @@ else: def __reversed__(self) -> 'Iterator[T_co]': pass - Sized = collections_abc.Sized # Not generic. @@ -1823,8 +1824,8 @@ if hasattr(collections_abc, 'Collection'): extra=collections_abc.Collection): __slots__ = () - __all__.append('Collection') + __all__.append('Collection') # Callable was defined earlier. @@ -1881,7 +1882,6 @@ class ByteString(Sequence[int], extra=collections_abc.ByteString): class List(list, MutableSequence[T], extra=list): - __slots__ = () def __new__(cls, *args, **kwds): @@ -1892,7 +1892,6 @@ class List(list, MutableSequence[T], extra=list): class Deque(collections.deque, MutableSequence[T], extra=collections.deque): - __slots__ = () def __new__(cls, *args, **kwds): @@ -1902,7 +1901,6 @@ class Deque(collections.deque, MutableSequence[T], extra=collections.deque): class Set(set, MutableSet[T], extra=set): - __slots__ = () def __new__(cls, *args, **kwds): @@ -1969,12 +1967,12 @@ else: return True return NotImplemented - if hasattr(contextlib, 'AbstractAsyncContextManager'): class AsyncContextManager(Generic[T_co], extra=contextlib.AbstractAsyncContextManager): __slots__ = () + __all__.append('AsyncContextManager') elif sys.version_info[:2] >= (3, 5): exec(""" @@ -2003,7 +2001,6 @@ __all__.append('AsyncContextManager') class Dict(dict, MutableMapping[KT, VT], extra=dict): - __slots__ = () def __new__(cls, *args, **kwds): @@ -2015,7 +2012,6 @@ class Dict(dict, MutableMapping[KT, VT], extra=dict): class DefaultDict(collections.defaultdict, MutableMapping[KT, VT], extra=collections.defaultdict): - __slots__ = () def __new__(cls, *args, **kwds): @@ -2025,7 +2021,6 @@ class DefaultDict(collections.defaultdict, MutableMapping[KT, VT], class Counter(collections.Counter, Dict[T, int], extra=collections.Counter): - __slots__ = () def __new__(cls, *args, **kwds): @@ -2038,6 +2033,7 @@ if hasattr(collections, 'ChainMap'): # ChainMap only exists in 3.3+ __all__.append('ChainMap') + class ChainMap(collections.ChainMap, MutableMapping[KT, VT], extra=collections.ChainMap): @@ -2048,7 +2044,6 @@ if hasattr(collections, 'ChainMap'): return collections.ChainMap(*args, **kwds) return _generic_new(collections.ChainMap, cls, *args, **kwds) - # Determine what base class to use for Generator. if hasattr(collections_abc, 'Generator'): # Sufficiently recent versions of 3.5 have a Generator ABC. @@ -2074,8 +2069,8 @@ if hasattr(collections_abc, 'AsyncGenerator'): extra=collections_abc.AsyncGenerator): __slots__ = () - __all__.append('AsyncGenerator') + __all__.append('AsyncGenerator') # Internal type variable used for Type[]. CT_co = TypeVar('CT_co', covariant=True, bound=type) @@ -2237,7 +2232,6 @@ def NewType(name, tp): # Python-version-specific alias (Python 2: unicode; Python 3: str) Text = str - # Constant that's True when type checking, but False here. TYPE_CHECKING = False @@ -2394,7 +2388,6 @@ class io: io.__name__ = __name__ + '.io' sys.modules[io.__name__] = io - Pattern = _TypeAlias('Pattern', AnyStr, type(stdlib_re.compile('')), lambda p: p.pattern) Match = _TypeAlias('Match', AnyStr, type(stdlib_re.match('', '')),