Merge branch 'develop'

This commit is contained in:
Dan 2018-09-19 17:55:10 +02:00
commit a093024824
176 changed files with 3356 additions and 1140 deletions

View File

@ -26,7 +26,7 @@ Features
- **Easy to use**: You can easily install Pyrogram using pip and start building your app right away.
- **High-level**: The low-level details of MTProto are abstracted and automatically handled.
- **Fast**: Crypto parts are boosted up by TgCrypto_, a high-performance library written in pure C.
- **Updated** to the latest Telegram API version, currently Layer 81 on top of MTProto 2.0.
- **Updated** to the latest Telegram API version, currently Layer 82 on top of MTProto 2.0.
- **Documented**: The Pyrogram API is well documented and resembles the Telegram Bot API.
- **Full API**, allowing to execute any advanced action an official client is able to do, and more.
@ -78,14 +78,13 @@ Copyright & License
<h1 align="center">
<a href="https://github.com/pyrogram/pyrogram">
<div><img src="https://media.pyrogram.ml/images/icon.png" alt="Pyrogram Icon"></div>
<div><img src="https://media.pyrogram.ml/images/label.png" alt="Pyrogram Label"></div>
<div><img src="https://raw.githubusercontent.com/pyrogram/logos/master/logos/pyrogram_logo2.png" alt="Pyrogram Logo"></div>
</a>
</h1>
<p align="center">
<b>Telegram MTProto API Client Library for Python</b>
<br>
<a href="https://github.com/pyrogram/pyrogram/releases/latest">
Download
@ -100,25 +99,25 @@ Copyright & License
</a>
<br><br>
<a href="compiler/api/source/main_api.tl">
<img src="https://img.shields.io/badge/SCHEME-LAYER%2081-eda738.svg?longCache=true&style=for-the-badge&colorA=262b30"
<img src="https://img.shields.io/badge/SCHEME-LAYER%2082-eda738.svg?longCache=true&style=for-the-badge&colorA=262b30"
alt="Scheme Layer">
</a>
<a href="https://github.com/pyrogram/tgcrypto">
<img src="https://img.shields.io/badge/TGCRYPTO-V1.0.4-eda738.svg?longCache=true&style=for-the-badge&colorA=262b30"
<img src="https://img.shields.io/badge/TGCRYPTO-V1.1.1-eda738.svg?longCache=true&style=for-the-badge&colorA=262b30"
alt="TgCrypto">
</a>
</p>
.. |logo| image:: https://pyrogram.ml/images/logo.png
.. |logo| image:: https://raw.githubusercontent.com/pyrogram/logos/master/logos/pyrogram_logo2.png
:target: https://pyrogram.ml
:alt: Pyrogram
.. |description| replace:: **Telegram MTProto API Client Library for Python**
.. |scheme| image:: "https://img.shields.io/badge/SCHEME-LAYER%2081-eda738.svg?longCache=true&style=for-the-badge&colorA=262b30"
.. |scheme| image:: "https://img.shields.io/badge/SCHEME-LAYER%2082-eda738.svg?longCache=true&style=for-the-badge&colorA=262b30"
:target: compiler/api/source/main_api.tl
:alt: Scheme Layer
.. |tgcrypto| image:: "https://img.shields.io/badge/TGCRYPTO-V1.0.4-eda738.svg?longCache=true&style=for-the-badge&colorA=262b30"
.. |tgcrypto| image:: "https://img.shields.io/badge/TGCRYPTO-V1.1.1-eda738.svg?longCache=true&style=for-the-badge&colorA=262b30"
:target: https://github.com/pyrogram/tgcrypto
:alt: TgCrypto

View File

@ -172,9 +172,8 @@ def start():
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/pyrogram.tl".format(HOME), encoding="utf-8") as pyrogram:
schema = (auth.read() + system.read() + api.read() + pyrogram.read()).splitlines()
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:
mtproto_template = f.read()
@ -494,17 +493,19 @@ def start():
f.write("\n 0xb0700015: \"pyrogram.client.types.ChatPhoto\",")
f.write("\n 0xb0700016: \"pyrogram.client.types.ChatMember\",")
f.write("\n 0xb0700017: \"pyrogram.client.types.Sticker\",")
f.write("\n 0xb0700025: \"pyrogram.client.types.GIF\",")
f.write("\n 0xb0700018: \"pyrogram.client.types.bots.ForceReply\",")
f.write("\n 0xb0700019: \"pyrogram.client.types.bots.InlineKeyboardButton\",")
f.write("\n 0xb0700020: \"pyrogram.client.types.bots.InlineKeyboardMarkup\",")
f.write("\n 0xb0700021: \"pyrogram.client.types.bots.KeyboardButton\",")
f.write("\n 0xb0700022: \"pyrogram.client.types.bots.ReplyKeyboardMarkup\",")
f.write("\n 0xb0700023: \"pyrogram.client.types.bots.ReplyKeyboardRemove\",")
f.write("\n 0xb0700024: \"pyrogram.client.types.CallbackQuery\",")
f.write("\n 0xb0700025: \"pyrogram.client.types.Animation\",")
f.write("\n 0xb0700026: \"pyrogram.client.types.Messages\",")
f.write("\n 0xb0700018: \"pyrogram.client.types.reply_markup.ForceReply\",")
f.write("\n 0xb0700019: \"pyrogram.client.types.reply_markup.InlineKeyboardButton\",")
f.write("\n 0xb0700020: \"pyrogram.client.types.reply_markup.InlineKeyboardMarkup\",")
f.write("\n 0xb0700021: \"pyrogram.client.types.reply_markup.KeyboardButton\",")
f.write("\n 0xb0700022: \"pyrogram.client.types.reply_markup.ReplyKeyboardMarkup\",")
f.write("\n 0xb0700023: \"pyrogram.client.types.reply_markup.ReplyKeyboardRemove\",")
f.write("\n 0xb0700024: \"pyrogram.client.types.CallbackQuery\"")
f.write("\n 0xb0700027: \"pyrogram.client.types.Photo\",")
f.write("\n 0xb0700028: \"pyrogram.client.types.Dialog\",")
f.write("\n 0xb0700029: \"pyrogram.client.types.Dialogs\",")
f.write("\n 0xb0700030: \"pyrogram.client.types.ChatMembers\",")
f.write("\n}\n")

View File

@ -36,7 +36,7 @@ inputMediaEmpty#9664f57f = InputMedia;
inputMediaUploadedPhoto#1e287d04 flags:# file:InputFile stickers:flags.0?Vector<InputDocument> ttl_seconds:flags.1?int = InputMedia;
inputMediaPhoto#b3ba0635 flags:# id:InputPhoto ttl_seconds:flags.0?int = InputMedia;
inputMediaGeoPoint#f9c44144 geo_point:InputGeoPoint = InputMedia;
inputMediaContact#a6e45987 phone_number:string first_name:string last_name:string = InputMedia;
inputMediaContact#f8ab7dfb phone_number:string first_name:string last_name:string vcard:string = InputMedia;
inputMediaUploadedDocument#5b38c6c1 flags:# nosound_video:flags.3?true file:InputFile thumb:flags.2?InputFile mime_type:string attributes:Vector<DocumentAttribute> stickers:flags.0?Vector<InputDocument> ttl_seconds:flags.1?int = InputMedia;
inputMediaDocument#23ab23d2 flags:# id:InputDocument ttl_seconds:flags.0?int = InputMedia;
inputMediaVenue#c13d1c11 geo_point:InputGeoPoint title:string address:string provider:string venue_id:string venue_type:string = InputMedia;
@ -61,6 +61,7 @@ inputFileLocation#14637196 volume_id:long local_id:int secret:long = InputFileLo
inputEncryptedFileLocation#f5235d55 id:long access_hash:long = InputFileLocation;
inputDocumentFileLocation#430f0724 id:long access_hash:long version:int = InputFileLocation;
inputSecureFileLocation#cbc7ee28 id:long access_hash:long = InputFileLocation;
inputTakeoutFileLocation#29be5899 = InputFileLocation;
inputAppEvent#770656a8 time:double type:string peer:long data:string = InputAppEvent;
@ -121,7 +122,7 @@ messageService#9e19a1f6 flags:# out:flags.1?true mentioned:flags.4?true media_un
messageMediaEmpty#3ded6320 = MessageMedia;
messageMediaPhoto#695150d7 flags:# photo:flags.0?Photo ttl_seconds:flags.2?int = MessageMedia;
messageMediaGeo#56e0d474 geo:GeoPoint = MessageMedia;
messageMediaContact#5e7d2f39 phone_number:string first_name:string last_name:string user_id:int = MessageMedia;
messageMediaContact#cbf24940 phone_number:string first_name:string last_name:string vcard:string user_id:int = MessageMedia;
messageMediaUnsupported#9f84f49e = MessageMedia;
messageMediaDocument#9cb070d7 flags:# document:flags.0?Document ttl_seconds:flags.2?int = MessageMedia;
messageMediaWebPage#a32dd600 webpage:WebPage = MessageMedia;
@ -153,7 +154,7 @@ messageActionBotAllowed#abe9affe domain:string = MessageAction;
messageActionSecureValuesSentMe#1b287353 values:Vector<SecureValue> credentials:SecureCredentialsEncrypted = MessageAction;
messageActionSecureValuesSent#d95c6154 types:Vector<SecureValueType> = MessageAction;
dialog#e4def5db flags:# pinned:flags.2?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage = Dialog;
dialog#e4def5db flags:# pinned:flags.2?true unread_mark:flags.3?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage = Dialog;
photoEmpty#2331b22d id:long = Photo;
photo#9288dd29 flags:# has_stickers:flags.0?true id:long access_hash:long date:int sizes:Vector<PhotoSize> = Photo;
@ -163,7 +164,7 @@ photoSize#77bfb61b type:string location:FileLocation w:int h:int size:int = Phot
photoCachedSize#e9a734fa type:string location:FileLocation w:int h:int bytes:bytes = PhotoSize;
geoPointEmpty#1117dd5f = GeoPoint;
geoPoint#2049d70c long:double lat:double = GeoPoint;
geoPoint#296f104 long:double lat:double access_hash:long = GeoPoint;
auth.checkedPhone#811ea28e phone_registered:Bool = auth.CheckedPhone;
@ -213,6 +214,7 @@ contacts.blockedSlice#900802a1 count:int blocked:Vector<ContactBlocked> users:Ve
messages.dialogs#15ba6c40 dialogs:Vector<Dialog> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.Dialogs;
messages.dialogsSlice#71e094f3 count:int dialogs:Vector<Dialog> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.Dialogs;
messages.dialogsNotModified#f0e3e596 count:int = messages.Dialogs;
messages.messages#8c718e87 messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.Messages;
messages.messagesSlice#b446ae3 count:int messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.Messages;
@ -309,6 +311,7 @@ updateFavedStickers#e511996d = Update;
updateChannelReadMessagesContents#89893b45 channel_id:int messages:Vector<int> = Update;
updateContactsReset#7084a7be = Update;
updateChannelAvailableMessages#70db6837 channel_id:int available_min_id:int = Update;
updateDialogUnreadMark#e16459c3 flags:# unread:flags.0?true peer:DialogPeer = Update;
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
@ -335,7 +338,7 @@ upload.fileCdnRedirect#f18cda44 dc_id:int file_token:bytes encryption_key:bytes
dcOption#18b7a10d flags:# ipv6:flags.0?true media_only:flags.1?true tcpo_only:flags.2?true cdn:flags.3?true static:flags.4?true id:int ip_address:string port:int secret:flags.10?bytes = DcOption;
config#eb7bb160 flags:# phonecalls_enabled:flags.1?true default_p2p_contacts:flags.3?true preload_featured_stickers:flags.4?true ignore_phone_entities:flags.5?true revoke_pm_inbox:flags.6?true blocked_mode:flags.8?true date:int expires:int test_mode:Bool this_dc:int dc_options:Vector<DcOption> chat_size_max:int megagroup_size_max:int forwarded_count_max:int online_update_period_ms:int offline_blur_timeout_ms:int offline_idle_timeout_ms:int online_cloud_timeout_ms:int notify_cloud_delay_ms:int notify_default_delay_ms:int push_chat_period_ms:int push_chat_limit:int saved_gifs_limit:int edit_time_limit:int revoke_time_limit:int revoke_pm_time_limit:int rating_e_decay:int stickers_recent_limit:int stickers_faved_limit:int channels_read_media_period:int tmp_sessions:flags.0?int pinned_dialogs_count_max:int call_receive_timeout_ms:int call_ring_timeout_ms:int call_connect_timeout_ms:int call_packet_timeout_ms:int me_url_prefix:string autoupdate_url_prefix:flags.7?string suggested_lang_code:flags.2?string lang_pack_version:flags.2?int = Config;
config#3213dbba flags:# phonecalls_enabled:flags.1?true default_p2p_contacts:flags.3?true preload_featured_stickers:flags.4?true ignore_phone_entities:flags.5?true revoke_pm_inbox:flags.6?true blocked_mode:flags.8?true date:int expires:int test_mode:Bool this_dc:int dc_options:Vector<DcOption> dc_txt_domain_name:string chat_size_max:int megagroup_size_max:int forwarded_count_max:int online_update_period_ms:int offline_blur_timeout_ms:int offline_idle_timeout_ms:int online_cloud_timeout_ms:int notify_cloud_delay_ms:int notify_default_delay_ms:int push_chat_period_ms:int push_chat_limit:int saved_gifs_limit:int edit_time_limit:int revoke_time_limit:int revoke_pm_time_limit:int rating_e_decay:int stickers_recent_limit:int stickers_faved_limit:int channels_read_media_period:int tmp_sessions:flags.0?int pinned_dialogs_count_max:int call_receive_timeout_ms:int call_ring_timeout_ms:int call_connect_timeout_ms:int call_packet_timeout_ms:int me_url_prefix:string autoupdate_url_prefix:flags.7?string gif_search_username:flags.9?string venue_search_username:flags.10?string img_search_username:flags.11?string static_maps_provider:flags.12?string caption_length_max:int message_length_max:int webfile_dc_id:int suggested_lang_code:flags.2?string lang_pack_version:flags.2?int = Config;
nearestDc#8e1a1775 country:string this_dc:int nearest_dc:int = NearestDc;
@ -562,7 +565,7 @@ inputBotInlineMessageMediaAuto#3380c786 flags:# message:string entities:flags.1?
inputBotInlineMessageText#3dcd7a87 flags:# no_webpage:flags.0?true message:string entities:flags.1?Vector<MessageEntity> reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
inputBotInlineMessageMediaGeo#c1b15d65 flags:# geo_point:InputGeoPoint period:int reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
inputBotInlineMessageMediaVenue#417bbf11 flags:# geo_point:InputGeoPoint title:string address:string provider:string venue_id:string venue_type:string reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
inputBotInlineMessageMediaContact#2daf01a7 flags:# phone_number:string first_name:string last_name:string reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
inputBotInlineMessageMediaContact#a6edbffd flags:# phone_number:string first_name:string last_name:string vcard:string reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
inputBotInlineMessageGame#4b425864 flags:# reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
inputBotInlineResult#88bf9319 flags:# id:string type:string title:flags.1?string description:flags.2?string url:flags.3?string thumb:flags.4?InputWebDocument content:flags.5?InputWebDocument send_message:InputBotInlineMessage = InputBotInlineResult;
@ -574,7 +577,7 @@ botInlineMessageMediaAuto#764cf810 flags:# message:string entities:flags.1?Vecto
botInlineMessageText#8c7f65e2 flags:# no_webpage:flags.0?true message:string entities:flags.1?Vector<MessageEntity> reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
botInlineMessageMediaGeo#b722de65 flags:# geo:GeoPoint period:int reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
botInlineMessageMediaVenue#8a86659c flags:# geo:GeoPoint title:string address:string provider:string venue_id:string venue_type:string reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
botInlineMessageMediaContact#35edb4d4 flags:# phone_number:string first_name:string last_name:string reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
botInlineMessageMediaContact#18d1cdc2 flags:# phone_number:string first_name:string last_name:string vcard:string reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
botInlineResult#11965f3a flags:# id:string type:string title:flags.1?string description:flags.2?string url:flags.3?string thumb:flags.4?WebDocument content:flags.5?WebDocument send_message:BotInlineMessage = BotInlineResult;
botInlineMediaResult#17db940b flags:# id:string type:string photo:flags.0?Photo document:flags.1?Document title:flags.2?string description:flags.3?string send_message:BotInlineMessage = BotInlineResult;
@ -617,8 +620,9 @@ topPeerCategoryPeers#fb834291 category:TopPeerCategory count:int peers:Vector<To
contacts.topPeersNotModified#de266ef5 = contacts.TopPeers;
contacts.topPeers#70b772a8 categories:Vector<TopPeerCategoryPeers> chats:Vector<Chat> users:Vector<User> = contacts.TopPeers;
contacts.topPeersDisabled#b52c939d = contacts.TopPeers;
draftMessageEmpty#ba4baec5 = DraftMessage;
draftMessageEmpty#1b0c841a flags:# date:flags.0?int = DraftMessage;
draftMessage#fd8e711f flags:# no_webpage:flags.1?true reply_to_msg_id:flags.0?int message:string entities:flags.3?Vector<MessageEntity> date:int = DraftMessage;
messages.featuredStickersNotModified#4ede3cf = messages.FeaturedStickers;
@ -706,14 +710,13 @@ paymentRequestedInfo#909c3f94 flags:# name:flags.0?string phone:flags.1?string e
paymentSavedCredentialsCard#cdc27a1f id:string title:string = PaymentSavedCredentials;
webDocument#c61acbd8 url:string access_hash:long size:int mime_type:string attributes:Vector<DocumentAttribute> dc_id:int = WebDocument;
webDocument#1c570ed1 url:string access_hash:long size:int mime_type:string attributes:Vector<DocumentAttribute> = WebDocument;
webDocumentNoProxy#f9c8bcc6 url:string size:int mime_type:string attributes:Vector<DocumentAttribute> = WebDocument;
inputWebDocument#9bed434d url:string size:int mime_type:string attributes:Vector<DocumentAttribute> = InputWebDocument;
inputWebFileLocation#c239d686 url:string access_hash:long = InputWebFileLocation;
inputWebFileGeoPointLocation#66275a62 geo_point:InputGeoPoint w:int h:int zoom:int scale:int = InputWebFileLocation;
inputWebFileGeoMessageLocation#553f32eb peer:InputPeer msg_id:int w:int h:int zoom:int scale:int = InputWebFileLocation;
inputWebFileGeoPointLocation#9f2221c9 geo_point:InputGeoPoint access_hash:long w:int h:int zoom:int scale:int = InputWebFileLocation;
upload.webFile#21e753bc size:int mime_type:string file_type:storage.FileType mtime:int bytes:bytes = upload.WebFile;
@ -883,6 +886,10 @@ account.sentEmailCode#811f854f email_pattern:string length:int = account.SentEma
help.deepLinkInfoEmpty#66afa166 = help.DeepLinkInfo;
help.deepLinkInfo#6a4ee832 flags:# update_app:flags.0?true message:string entities:flags.1?Vector<MessageEntity> = help.DeepLinkInfo;
savedPhoneContact#1142bd56 phone:string first_name:string last_name:string date:int = SavedContact;
account.takeout#4dba4501 id:long = account.Takeout;
---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
@ -890,13 +897,14 @@ invokeAfterMsgs#3dc4b4f0 {X:Type} msg_ids:Vector<long> query:!X = X;
initConnection#785188b8 {X:Type} flags:# api_id:int device_model:string system_version:string app_version:string system_lang_code:string lang_pack:string lang_code:string proxy:flags.0?InputClientProxy query:!X = X;
invokeWithLayer#da9b0d0d {X:Type} layer:int query:!X = X;
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.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;
auth.resetAuthorizations#9fab0d1a = Bool;
auth.sendInvites#771c1d97 phone_numbers:Vector<string> message:string = Bool;
auth.exportAuthorization#e5bfffcd dc_id:int = auth.ExportedAuthorization;
auth.importAuthorization#e3ef9613 id:int bytes:bytes = auth.Authorization;
auth.bindTempAuthKey#cdd42a05 perm_auth_key_id:long nonce:long expires_at:int encrypted_message:bytes = Bool;
@ -948,6 +956,8 @@ account.sendVerifyPhoneCode#823380b4 flags:# allow_flashcall:flags.0?true phone_
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;
account.initTakeoutSession#f05b4804 flags:# contacts:flags.0?true message_users:flags.1?true message_chats:flags.2?true message_megagroups:flags.3?true message_channels:flags.4?true files:flags.5?true file_max_size:flags.5?int = account.Takeout;
account.finishTakeoutSession#1d2652ee flags:# success:flags.0?true = Bool;
users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
users.getFullUser#ca30a5b1 id:InputUser = UserFull;
@ -968,9 +978,11 @@ contacts.resolveUsername#f93ccba3 username:string = contacts.ResolvedPeer;
contacts.getTopPeers#d4982db5 flags:# correspondents:flags.0?true bots_pm:flags.1?true bots_inline:flags.2?true phone_calls:flags.3?true groups:flags.10?true channels:flags.15?true offset:int limit:int hash:int = contacts.TopPeers;
contacts.resetTopPeerRating#1ae373ac category:TopPeerCategory peer:InputPeer = Bool;
contacts.resetSaved#879537f1 = Bool;
contacts.getSaved#82f1e39f = Vector<SavedContact>;
contacts.toggleTopPeers#8514bdda enabled:Bool = Bool;
messages.getMessages#63c66506 id:Vector<InputMessage> = messages.Messages;
messages.getDialogs#191ba9c5 flags:# exclude_pinned:flags.0?true offset_date:int offset_id:int offset_peer:InputPeer limit:int = messages.Dialogs;
messages.getDialogs#b098aee6 flags:# exclude_pinned:flags.0?true offset_date:int offset_id:int offset_peer:InputPeer limit:int hash:int = messages.Dialogs;
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;
@ -1065,6 +1077,9 @@ messages.getRecentLocations#bbc45b09 peer:InputPeer limit:int hash:int = message
messages.sendMultiMedia#2095512f flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int multi_media:Vector<InputSingleMedia> = Updates;
messages.uploadEncryptedFile#5057c497 peer:InputEncryptedChat file:InputEncryptedFile = EncryptedFile;
messages.searchStickerSets#c2b7d08b flags:# exclude_featured:flags.0?true q:string hash:int = messages.FoundStickerSets;
messages.getSplitRanges#1cff7e08 = Vector<MessageRange>;
messages.markDialogUnread#c286d98f flags:# unread:flags.0?true peer:InputDialogPeer = Bool;
messages.getDialogUnreadMarks#22e24e22 = Vector<DialogPeer>;
updates.getState#edd4882a = updates.State;
updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference;
@ -1131,6 +1146,7 @@ channels.setStickers#ea8ca4f9 channel:InputChannel stickerset:InputStickerSet =
channels.readMessageContents#eab5dc38 channel:InputChannel id:Vector<int> = Bool;
channels.deleteHistory#af369d42 channel:InputChannel max_id:int = Bool;
channels.togglePreHistoryHidden#eabbb94c channel:InputChannel enabled:Bool = Updates;
channels.getLeftChannels#8341ecc0 offset:int = messages.Chats;
bots.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON;
bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool;
@ -1161,4 +1177,4 @@ langpack.getStrings#2e1ee318 lang_code:string keys:Vector<string> = Vector<LangP
langpack.getDifference#b2e4d7d from_version:int = LangPackDifference;
langpack.getLanguages#800fd57d = Vector<LangPackLanguage>;
// LAYER 81
// LAYER 82

View File

@ -1,22 +0,0 @@
// Pyrogram
---types---
//pyrogram.update#b0700000 flags:# update_id:int message:flags.0?Message edited_message:flags.1?Message channel_post:flags.2?Message edited_channel_post:flags.3?Message inline_query:flags.4?InlineQuery chosen_inline_result:flags.5?ChosenInlineResult callback_query:flags.6?CallbackQuery shipping_query:flags.7?ShippingQuery pre_checkout_query:flags.8?PreCheckoutQuery = pyrogram.Update;
//pyrogram.user#b0700001 flags:# id:int is_bot:Bool first_name:string last_name:flags.0?string username:flags.1?string language_code:flags.2?string phone_number:flags.3?string photo:flags.4?ChatPhoto = pyrogram.User;
//pyrogram.chat#b0700002 flags:# id:int type:string title:flags.0?string username:flags.1?string first_name:flags.2?string last_name:flags.3?string all_members_are_administrators:flags.4?Bool photo:flags.5?ChatPhoto description:flags.6?string invite_link:flags.7?string pinned_message:flags.8?Message sticker_set_name:flags.9?string can_set_sticker_set:flags.10?Bool = pyrogram.Chat;
//pyrogram.message#b0700003 flags:# message_id:int from_user:flags.0?User date:int chat:Chat forward_from:flags.1?User forward_from_chat:flags.2?Chat forward_from_message_id:flags.3?int forward_signature:flags.4?string forward_date:flags.5?int reply_to_message:flags.6?Message edit_date:flags.7?int media_group_id:flags.8?string author_signature:flags.9?string text:flags.10?string entities:flags.11?Vector<MessageEntity> caption_entities:flags.12?Vector<MessageEntity> audio:flags.13?Audio document:flags.14?Document game:flags.15?Game photo:flags.16?Vector<PhotoSize> sticker:flags.17?Sticker video:flags.18?Video voice:flags.19?Voice video_note:flags.20?VideoNote caption:flags.21?string contact:flags.22?Contact location:flags.23?Location venue:flags.24?Venue new_chat_members:flags.25?Vector<User> left_chat_member:flags.26?User new_chat_title:flags.27?string new_chat_photo:flags.28?Vector<PhotoSize> delete_chat_photo:flags.29?true group_chat_created:flags.30?true supergroup_chat_created:flags.31?true channel_chat_created:flags.32?true migrate_to_chat_id:flags.33?int migrate_from_chat_id:flags.34?int pinned_message:flags.35?Message invoice:flags.36?Invoice successful_payment:flags.37?SuccessfulPayment connected_website:flags.38?string views:flags.39?int via_bot:flags.40?User = pyrogram.Message;
//pyrogram.messageEntity#b0700004 flags:# type:string offset:int length:int url:flags.0?string user:flags.1?User = pyrogram.MessageEntity;
//pyrogram.photoSize#b0700005 flags:# file_id:string file_size:flags.0?int date:flags.1?int width:int height:int = pyrogram.PhotoSize;
//pyrogram.audio#b0700006 flags:# file_id:string thumb:flags.0?PhotoSize file_name:flags.1?string mime_type:flags.2?string file_size:flags.3?int date:flags.4?int duration:int performer:flags.5?string title:flags.6?string = pyrogram.Audio;
//pyrogram.document#b0700007 flags:# file_id:string thumb:flags.0?PhotoSize file_name:flags.1?string mime_type:flags.2?string file_size:flags.3?int date:flags.4?int = pyrogram.Document;
//pyrogram.video#b0700008 flags:# file_id:string thumb:flags.0?PhotoSize file_name:flags.1?string mime_type:flags.2?string file_size:flags.3?int date:flags.4?int width:int height:int duration:int = pyrogram.Video;
//pyrogram.voice#b0700009 flags:# file_id:string thumb:flags.0?PhotoSize file_name:flags.1?string mime_type:flags.2?string file_size:flags.3?int date:flags.4?int duration:int = pyrogram.Voice;
//pyrogram.videoNote#b0700010 flags:# file_id:string thumb:flags.0?PhotoSize file_name:flags.1?string mime_type:flags.2?string file_size:flags.3?int date:flags.4?int length:int duration:int = pyrogram.VideoNote;
//pyrogram.contact#b0700011 flags:# phone_number:string first_name:string last_name:flags.0?string user_id:flags.1?int = pyrogram.Contact;
//pyrogram.location#b0700012 longitude:double latitude:double = pyrogram.Location;
//pyrogram.venue#b0700013 flags:# location:Location title:string address:string foursquare_id:flags.0?string = pyrogram.Venue;
//pyrogram.userProfilePhotos#b0700014 total_count:int photos:Vector<Vector<PhotoSize>> = pyrogram.UserProfilePhotos;
//pyrogram.chatPhoto#b0700015 small_file_id:string big_file_id:string = pyrogram.ChatPhoto;
//pyrogram.chatMember#b0700016 flags:# user:User status:string until_date:flags.0?int can_be_edited:flags.1?Bool can_change_info:flags.2?Bool can_post_messages:flags.3?Bool can_edit_messages:flags.4?Bool can_delete_messages:flags.5?Bool can_invite_users:flags.6?Bool can_restrict_members:flags.7?Bool can_pin_messages:flags.8?Bool can_promote_members:flags.9?Bool can_send_messages:flags.10?Bool can_send_media_messages:flags.11?Bool can_send_other_messages:flags.12?Bool can_add_web_page_previews:flags.13?Bool = pyrogram.ChatMember;
//pyrogram.sticker#b0700017 flags:# file_id:string thumb:flags.0?PhotoSize file_name:flags.1?string mime_type:flags.2?string file_size:flags.3?int date:flags.4?int width:int height:int emoji:flags.5?string set_name:flags.6?string mask_position:flags.7?MaskPosition = pyrogram.Sticker;

View File

@ -62,4 +62,8 @@ USER_IS_BOT A bot cannot send messages to other bots or to itself
WEBPAGE_CURL_FAILED Telegram could not fetch the provided URL
STICKERSET_INVALID The requested sticker set is invalid
PEER_FLOOD The method can't be used because your account is limited
MEDIA_CAPTION_TOO_LONG The media caption is longer than 200 characters
MEDIA_CAPTION_TOO_LONG The media caption is longer than 200 characters
USER_NOT_MUTUAL_CONTACT The user is not a mutual contact
USER_CHANNELS_TOO_MUCH The user is already in too many channels or supergroups
API_ID_PUBLISHED_FLOOD You are using an API key that is limited on the server side
USER_NOT_PARTICIPANT The user is not a member of this chat
1 id message
62 WEBPAGE_CURL_FAILED Telegram could not fetch the provided URL
63 STICKERSET_INVALID The requested sticker set is invalid
64 PEER_FLOOD The method can't be used because your account is limited
65 MEDIA_CAPTION_TOO_LONG The media caption is longer than 200 characters
66 USER_NOT_MUTUAL_CONTACT The user is not a mutual contact
67 USER_CHANNELS_TOO_MUCH The user is already in too many channels or supergroups
68 API_ID_PUBLISHED_FLOOD You are using an API key that is limited on the server side
69 USER_NOT_PARTICIPANT The user is not a member of this chat

View File

@ -0,0 +1,2 @@
id message
CHAT_WRITE_FORBIDDEN You don't have rights to send messages in this chat
1 id message
2 CHAT_WRITE_FORBIDDEN You don't have rights to send messages in this chat

View File

@ -0,0 +1,2 @@
id message
AUTH_KEY_DUPLICATED Authorization error. You must log out and log in again with your phone number. We apologize for the inconvenience.
1 id message
2 AUTH_KEY_DUPLICATED Authorization error. You must log out and log in again with your phone number. We apologize for the inconvenience.

View File

@ -3,4 +3,5 @@ AUTH_RESTART User authorization has restarted
RPC_CALL_FAIL Telegram is having internal problems. Please try again later
RPC_MCGET_FAIL Telegram is having internal problems. Please try again later
PERSISTENT_TIMESTAMP_OUTDATED Telegram is having internal problems. Please try again later
HISTORY_GET_FAILED Telegram is having internal problems. Please try again later
HISTORY_GET_FAILED Telegram is having internal problems. Please try again later
REG_ID_GENERATE_FAILED Telegram is having internal problems. Please try again later
1 id message
3 RPC_CALL_FAIL Telegram is having internal problems. Please try again later
4 RPC_MCGET_FAIL Telegram is having internal problems. Please try again later
5 PERSISTENT_TIMESTAMP_OUTDATED Telegram is having internal problems. Please try again later
6 HISTORY_GET_FAILED Telegram is having internal problems. Please try again later
7 REG_ID_GENERATE_FAILED Telegram is having internal problems. Please try again later

View File

@ -3,7 +3,7 @@
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = ~/PycharmProjects/pyrogram/venv3.6/bin/sphinx-build
SPHINXBUILD = sphinx-build
SPHINXPROJ = Pyrogram
SOURCEDIR = source
BUILDDIR = build

View File

@ -1,20 +0,0 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
SPHINXPROJ = Pyrogram
SOURCEDIR = source
BUILDDIR = build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

View File

@ -5,8 +5,7 @@ Welcome to Pyrogram
<div align="center">
<a href="https://docs.pyrogram.ml">
<div><img src="https://media.pyrogram.ml/images/icon.png" alt="Pyrogram Icon"></div>
<div><img src="https://media.pyrogram.ml/images/label.png" alt="Pyrogram Label"></div>
<div><img src="_static/logo.png" alt="Pyrogram Logo"></div>
</a>
</div>
@ -26,11 +25,11 @@ Welcome to Pyrogram
</a>
<br><br>
<a href="https://github.com/pyrogram/pyrogram/blob/master/compiler/api/source/main_api.tl">
<img src="https://img.shields.io/badge/SCHEME-LAYER%2081-eda738.svg?longCache=true&style=for-the-badge&colorA=262b30"
<img src="https://img.shields.io/badge/SCHEME-LAYER%2082-eda738.svg?longCache=true&style=for-the-badge&colorA=262b30"
alt="Scheme Layer">
</a>
<a href="https://github.com/pyrogram/tgcrypto">
<img src="https://img.shields.io/badge/TGCRYPTO-V1.0.4-eda738.svg?longCache=true&style=for-the-badge&colorA=262b30"
<img src="https://img.shields.io/badge/TGCRYPTO-V1.1.1-eda738.svg?longCache=true&style=for-the-badge&colorA=262b30"
alt="TgCrypto">
</a>
</p>
@ -84,6 +83,7 @@ To get started, press the Next button.
:caption: Resources
resources/UpdateHandling
resources/UsingFilters
resources/AutoAuthorization
resources/CustomizeSessions
resources/TgCrypto

View File

@ -1,68 +1,135 @@
Client
======
.. currentmodule:::: pyrogram.Client
.. currentmodule:: pyrogram.Client
.. autoclass:: pyrogram.Client
Utilities
---------
.. autosummary::
:nosignatures:
start
stop
idle
run
add_handler
remove_handler
send
resolve_peer
download_media
Decorators
----------
.. autosummary::
:nosignatures:
on_message
on_callback_query
on_deleted_messages
on_disconnect
on_raw_update
Messages
--------
.. autosummary::
:nosignatures:
send_message
forward_messages
send_photo
send_audio
send_document
send_sticker
send_video
send_animation
send_voice
send_video_note
send_media_group
send_location
send_venue
send_contact
send_chat_action
edit_message_text
edit_message_caption
edit_message_reply_markup
edit_message_media
delete_messages
get_messages
get_history
Chats
-----
.. autosummary::
:nosignatures:
join_chat
leave_chat
kick_chat_member
unban_chat_member
restrict_chat_member
promote_chat_member
export_chat_invite_link
set_chat_photo
delete_chat_photo
set_chat_title
set_chat_description
pin_chat_message
unpin_chat_message
get_chat
get_chat_member
get_chat_members
get_chat_members_count
get_dialogs
Users
-----
.. autosummary::
:nosignatures:
get_me
get_users
get_user_profile_photos
delete_profile_photos
Contacts
--------
.. autosummary::
:nosignatures:
add_contacts
get_contacts
delete_contacts
Password
--------
.. autosummary::
:nosignatures:
enable_cloud_password
change_cloud_password
remove_cloud_password
Bots
----
.. autosummary::
:nosignatures:
get_inline_bot_results
send_inline_bot_result
answer_callback_query
request_callback_answer
.. autoclass:: pyrogram.Client
:inherited-members:
:members:
.. _available-methods:
**Available methods**
.. autosummary::
:nosignatures:
start
stop
idle
run
on_message
on_callback_query
on_raw_update
add_handler
remove_handler
send
resolve_peer
get_me
send_message
forward_messages
send_photo
send_audio
send_document
send_sticker
send_video
send_voice
send_video_note
send_media_group
send_location
send_venue
send_contact
send_chat_action
download_media
get_user_profile_photos
edit_message_text
edit_message_caption
edit_message_reply_markup
delete_messages
join_chat
leave_chat
export_chat_invite_link
enable_cloud_password
change_cloud_password
remove_cloud_password
kick_chat_member
unban_chat_member
restrict_chat_member
promote_chat_member
add_contacts
get_contacts
delete_contacts
get_inline_bot_results
send_inline_bot_result
answer_callback_query
get_users
get_chat
get_messages
get_history

View File

@ -1,5 +1,3 @@
:tocdepth: 1
Error
=====

View File

@ -0,0 +1,29 @@
Handlers
========
.. currentmodule:: pyrogram
.. autosummary::
:nosignatures:
MessageHandler
DeletedMessagesHandler
CallbackQueryHandler
DisconnectHandler
RawUpdateHandler
.. autoclass:: MessageHandler
:members:
.. autoclass:: DeletedMessagesHandler
:members:
.. autoclass:: CallbackQueryHandler
:members:
.. autoclass:: DisconnectHandler
:members:
.. autoclass:: RawUpdateHandler
:members:

View File

@ -0,0 +1,188 @@
Types
=====
.. currentmodule:: pyrogram
Users & Chats
-------------
.. autosummary::
:nosignatures:
User
Chat
ChatPhoto
ChatMember
ChatMembers
Dialog
Dialogs
Messages & Media
----------------
.. autosummary::
:nosignatures:
Message
Messages
MessageEntity
Photo
PhotoSize
UserProfilePhotos
Audio
Document
Animation
Video
Voice
VideoNote
Contact
Location
Venue
Sticker
Bots
----
.. autosummary::
:nosignatures:
ReplyKeyboardMarkup
KeyboardButton
ReplyKeyboardRemove
InlineKeyboardMarkup
InlineKeyboardButton
ForceReply
CallbackQuery
Input Media
-----------
.. autosummary::
:nosignatures:
InputMediaPhoto
InputMediaVideo
InputMediaAudio
InputMediaAnimation
InputMediaDocument
InputPhoneContact
.. User & Chats
------------
.. autoclass:: User
:members:
.. autoclass:: Chat
:members:
.. autoclass:: ChatPhoto
:members:
.. autoclass:: ChatMember
:members:
.. autoclass:: ChatMembers
:members:
.. autoclass:: Dialog
:members:
.. autoclass:: Dialogs
:members:
.. Messages & Media
----------------
.. autoclass:: Message
:members:
.. autoclass:: Messages
:members:
.. autoclass:: MessageEntity
:members:
.. autoclass:: Photo
:members:
.. autoclass:: PhotoSize
:members:
.. autoclass:: UserProfilePhotos
:members:
.. autoclass:: Audio
:members:
.. autoclass:: Document
:members:
.. autoclass:: Animation
:members:
.. autoclass:: Video
:members:
.. autoclass:: Voice
:members:
.. autoclass:: VideoNote
:members:
.. autoclass:: Contact
:members:
.. autoclass:: Location
:members:
.. autoclass:: Venue
:members:
.. autoclass:: Sticker
:members:
.. Bots
----
.. autoclass:: ReplyKeyboardMarkup
:members:
.. autoclass:: KeyboardButton
:members:
.. autoclass:: ReplyKeyboardRemove
:members:
.. autoclass:: InlineKeyboardMarkup
:members:
.. autoclass:: InlineKeyboardButton
:members:
.. autoclass:: ForceReply
:members:
.. autoclass:: CallbackQuery
:members:
.. Input Media
-----------
.. autoclass:: InputMediaPhoto
:members:
.. autoclass:: InputMediaVideo
:members:
.. autoclass:: InputMediaAudio
:members:
.. autoclass:: InputMediaAnimation
:members:
.. autoclass:: InputMediaDocument
:members:
.. autoclass:: InputPhoneContact
:members:

View File

@ -1,6 +0,0 @@
CallbackQueryHandler
====================
.. autoclass:: pyrogram.CallbackQueryHandler
:members:
:undoc-members:

View File

@ -1,6 +0,0 @@
DeletedMessagesHandler
======================
.. autoclass:: pyrogram.DeletedMessagesHandler
:members:
:undoc-members:

View File

@ -1,6 +0,0 @@
DisconnectHandler
=================
.. autoclass:: pyrogram.DisconnectHandler
:members:
:undoc-members:

View File

@ -1,6 +0,0 @@
MessageHandler
==============
.. autoclass:: pyrogram.MessageHandler
:members:
:undoc-members:

View File

@ -1,6 +0,0 @@
RawUpdateHandler
================
.. autoclass:: pyrogram.RawUpdateHandler
:members:
:undoc-members:

View File

@ -1,11 +0,0 @@
:tocdepth: 1
Handlers
========
.. toctree::
MessageHandler
DeletedMessagesHandler
CallbackQueryHandler
DisconnectHandler
RawUpdateHandler

View File

@ -7,9 +7,11 @@ In this section you can find a detailed description of the Pyrogram package and
after the well established `Telegram Bot API`_ methods, thus offering a familiar look to Bot developers.
.. toctree::
:maxdepth: 1
Client
types/index
handlers/index
Types
Handlers
Filters
ChatAction
ParseMode

View File

@ -1,5 +0,0 @@
Audio
=====
.. autoclass:: pyrogram.Audio
:members:

View File

@ -1,5 +0,0 @@
CallbackQuery
=============
.. autoclass:: pyrogram.CallbackQuery
:members:

View File

@ -1,5 +0,0 @@
Chat
====
.. autoclass:: pyrogram.Chat
:members:

View File

@ -1,5 +0,0 @@
ChatMember
==========
.. autoclass:: pyrogram.ChatMember
:members:

View File

@ -1,5 +0,0 @@
ChatPhoto
=========
.. autoclass:: pyrogram.ChatPhoto
:members:

View File

@ -1,5 +0,0 @@
Contact
=======
.. autoclass:: pyrogram.Contact
:members:

View File

@ -1,5 +0,0 @@
Document
========
.. autoclass:: pyrogram.Document
:members:

View File

@ -1,5 +0,0 @@
GIF
===
.. autoclass:: pyrogram.GIF
:members:

View File

@ -1,5 +0,0 @@
InputMediaPhoto
===============
.. autoclass:: pyrogram.InputMediaPhoto
:members:

View File

@ -1,5 +0,0 @@
InputMediaVideo
===============
.. autoclass:: pyrogram.InputMediaVideo
:members:

View File

@ -1,5 +0,0 @@
InputPhoneContact
=================
.. autoclass:: pyrogram.InputPhoneContact
:members:

View File

@ -1,5 +0,0 @@
Location
========
.. autoclass:: pyrogram.Location
:members:

View File

@ -1,5 +0,0 @@
Message
=======
.. autoclass:: pyrogram.Message
:members:

View File

@ -1,5 +0,0 @@
MessageEntity
=============
.. autoclass:: pyrogram.MessageEntity
:members:

View File

@ -1,5 +0,0 @@
Messages
========
.. autoclass:: pyrogram.Messages
:members:

View File

@ -1,5 +0,0 @@
PhotoSize
=========
.. autoclass:: pyrogram.PhotoSize
:members:

View File

@ -1,5 +0,0 @@
Sticker
=======
.. autoclass:: pyrogram.Sticker
:members:

View File

@ -1,5 +0,0 @@
Update
======
.. autoclass:: pyrogram.Update
:members:

View File

@ -1,5 +0,0 @@
User
====
.. autoclass:: pyrogram.User
:members:

View File

@ -1,5 +0,0 @@
UserProfilePhotos
=================
.. autoclass:: pyrogram.UserProfilePhotos
:members:

View File

@ -1,5 +0,0 @@
Venue
=====
.. autoclass:: pyrogram.Venue
:members:

View File

@ -1,5 +0,0 @@
Video
=====
.. autoclass:: pyrogram.Video
:members:

View File

@ -1,5 +0,0 @@
VideoNote
=========
.. autoclass:: pyrogram.VideoNote
:members:

View File

@ -1,5 +0,0 @@
Voice
=====
.. autoclass:: pyrogram.Voice
:members:

View File

@ -1,35 +0,0 @@
:tocdepth: 1
Types
=====
.. toctree::
User
Chat
Message
MessageEntity
Messages
PhotoSize
Audio
Document
GIF
Video
Voice
VideoNote
Contact
Location
Venue
UserProfilePhotos
ChatPhoto
ChatMember
InputMediaPhoto
InputMediaVideo
InputPhoneContact
Sticker
reply_markup/ForceReply
reply_markup/InlineKeyboardButton
reply_markup/InlineKeyboardMarkup
reply_markup/KeyboardButton
reply_markup/ReplyKeyboardMarkup
reply_markup/ReplyKeyboardRemove
CallbackQuery

View File

@ -1,5 +0,0 @@
ForceReply
==========
.. autoclass:: pyrogram.ForceReply
:members:

View File

@ -1,5 +0,0 @@
InlineKeyboardButton
====================
.. autoclass:: pyrogram.InlineKeyboardButton
:members:

View File

@ -1,5 +0,0 @@
InlineKeyboardMarkup
====================
.. autoclass:: pyrogram.InlineKeyboardMarkup
:members:

View File

@ -1,5 +0,0 @@
KeyboardButton
==============
.. autoclass:: pyrogram.KeyboardButton
:members:

View File

@ -1,5 +0,0 @@
ReplyKeyboardMarkup
===================
.. autoclass:: pyrogram.ReplyKeyboardMarkup
:members:

View File

@ -1,5 +0,0 @@
ReplyKeyboardRemove
===================
.. autoclass:: pyrogram.ReplyKeyboardRemove
:members:

View File

@ -1,8 +1,11 @@
Text Formatting
===============
Pyrogram, just like `Telegram Bot API`_, supports basic Markdown and HTML formatting styles for text messages and
media captions; Markdown uses the same syntax as Telegram Desktop's and is enabled by default.
Pyrogram, just like the `Telegram Bot API`_, natively supports basic Markdown and HTML formatting styles for text
messages and media captions.
Markdown style uses the same syntax as Telegram Desktop's and is enabled by default.
Beside bold, italic, and pre-formatted code, **Pyrogram does also support inline URLs and inline mentions of users**.
Markdown Style
@ -11,7 +14,7 @@ Markdown Style
To use this mode, pass :obj:`MARKDOWN <pyrogram.ParseMode.MARKDOWN>` or "markdown" in the *parse_mode* field when using
:obj:`send_message() <pyrogram.Client.send_message>`. Use the following syntax in your message:
.. code-block:: txt
.. code-block:: text
**bold text**
@ -34,7 +37,7 @@ HTML Style
To use this mode, pass :obj:`HTML <pyrogram.ParseMode.HTML>` or "html" in the *parse_mode* field when using
:obj:`send_message() <pyrogram.Client.send_message>`. The following tags are currently supported:
.. code-block:: txt
.. code-block:: text
<b>bold</b>, <strong>bold</strong>
@ -46,9 +49,7 @@ To use this mode, pass :obj:`HTML <pyrogram.ParseMode.HTML>` or "html" in the *p
<code>inline fixed-width code</code>
<pre>pre-formatted fixed-width
code block
</pre>
<pre>pre-formatted fixed-width code block</pre>
.. note:: Mentions are only guaranteed to work if you have already met the user (in groups or private chats).

View File

@ -2,188 +2,58 @@ Update Handling
===============
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 with an Handler. There are multiple Handlers to choose
from, one for each kind of update:
and can be handled by registering one or more callback functions in your app by using an `Handler <../pyrogram/Handlers.html>`_.
- `MessageHandler <../pyrogram/handlers/MessageHandler.html>`_
- `DeletedMessagesHandler <../pyrogram/handlers/DeletedMessagesHandler.html>`_
- `CallbackQueryHandler <../pyrogram/handlers/CallbackQueryHandler.html>`_
- `RawUpdateHandler <../pyrogram/handlers/RawUpdateHandler.html>`_
- `DisconnectHandler <../pyrogram/handlers/DisconnectHandler.html>`_
To put it simply, whenever an update is received from Telegram it will be dispatched and your previously defined callback
function(s) will be called back with the update itself as argument.
Registering an Handler
----------------------
We shall examine the :obj:`MessageHandler <pyrogram.MessageHandler>`, which will be in charge for handling
:obj:`Message <pyrogram.Message>` objects.
- The easiest and nicest way to register a MessageHandler is by decorating your function with the
:meth:`on_message() <pyrogram.Client.on_message>` decorator. 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
app = Client("my_account")
To explain how `Handlers <../pyrogram/Handlers.html>`_ work let's have a look at the most used one, the
:obj:`MessageHandler <pyrogram.MessageHandler>`, which will be in charge for handling :obj:`Message <pyrogram.Message>`
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.
@app.on_message()
def my_handler(client, message):
print(message)
app.run()
- If you prefer not to use decorators, there is an alternative way for registering Handlers.
This is useful, for example, when you want to keep your callback functions in separate files.
.. code-block:: python
from pyrogram import Client, MessageHandler
def my_handler(client, message):
print(message)
app = Client("my_account")
app.add_handler(MessageHandler(my_handler))
app.run()
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 <pyrogram.Filters>`.
- This example will show you how to **only** handle messages containing an
:obj:`Audio <pyrogram.Audio>` object and filter out any other message:
.. code-block:: python
from pyrogram import Filters
@app.on_message(Filters.audio)
def my_handler(client, message):
print(message)
- or, without decorators:
.. code-block:: python
from pyrogram import Filters, MessageHandler
def my_handler(client, message):
print(message)
app.add_handler(MessageHandler(my_handler, Filters.audio))
Combining Filters
-----------------
Filters can also be used in a more advanced way by combining more filters together using bitwise operators:
- Use ``~`` to invert a filter (behaves like the ``not`` operator).
- Use ``&`` and ``|`` to merge two filters (behave like ``and``, ``or`` operators respectively).
Here are some examples:
- Message is a **text** message **and** is **not edited**.
.. code-block:: python
@app.on_message(Filters.text & ~Filters.edited)
def my_handler(client, message):
print(message)
- Message is a **sticker** **and** is coming from a **channel or** a **private** chat.
.. code-block:: python
@app.on_message(Filters.sticker & (Filters.channel | Filters.private))
def my_handler(client, message):
print(message)
Advanced Filters
Using Decorators
----------------
Some filters, like :obj:`command() <pyrogram.Filters.command>` or :obj:`regex() <pyrogram.Filters.regex>`
can also accept arguments:
- Message is either a */start* or */help* **command**.
.. code-block:: python
@app.on_message(Filters.command(["start", "help"]))
def my_handler(client, message):
print(message)
- Message is a **text** message matching the given **regex** pattern.
.. code-block:: python
@app.on_message(Filters.regex("pyrogram"))
def my_handler(client, message):
print(message)
More handlers using different filters can also live together.
The easiest and nicest way to register a MessageHandler is by decorating your function with the
:meth:`on_message() <pyrogram.Client.on_message>` decorator. Here's a full example that prints out the content
of a message as soon as it arrives.
.. code-block:: python
@app.on_message(Filters.command("start"))
def start_command(client, message):
print("This is the /start command")
from pyrogram import Client
app = Client("my_account")
@app.on_message(Filters.command("help"))
def help_command(client, message):
print("This is the /help command")
@app.on_message()
def my_handler(client, message):
print(message)
@app.on_message(Filters.chat("PyrogramChat"))
def from_pyrogramchat(client, message):
print("New message in @PyrogramChat")
app.run()
Handler Groups
--------------
Using add_handler()
-------------------
If you register handlers with overlapping filters, only the first one is executed and any other handler will be ignored.
In order to process the same message 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. This means that a lower group number has
a higher priority.
For example, in:
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.
.. code-block:: python
@app.on_message(Filters.text | Filters.sticker)
def text_or_sticker(client, message):
print("Text or Sticker")
from pyrogram import Client, MessageHandler
@app.on_message(Filters.text)
def just_text(client, message):
print("Just Text")
def my_handler(client, message):
print(message)
``just_text`` is never executed. To enable it, simply register the function using a different group:
.. code-block:: python
app = Client("my_account")
@app.on_message(Filters.text, group=1)
def just_text(client, message):
print("Just Text")
app.add_handler(MessageHandler(my_handler))
or, if you want ``just_text`` to be fired *before* ``text_or_sticker``:
.. code-block:: python
@app.on_message(Filters.text, group=-1)
def just_text(client, message):
print("Just Text")
app.run()

View File

@ -0,0 +1,228 @@
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 <pyrogram.Filters>`.
.. note::
This section makes use of Handlers to handle updates. Learn more at `Update Handling <UpdateHandling.html>`_.
- This example will show you how to **only** handle messages containing an :obj:`Audio <pyrogram.Audio>` object and
ignore any other message:
.. code-block:: python
from pyrogram import Filters
@app.on_message(Filters.audio)
def my_handler(client, message):
print(message)
- or, without decorators:
.. code-block:: python
from pyrogram import Filters, MessageHandler
def my_handler(client, message):
print(message)
app.add_handler(MessageHandler(my_handler, Filters.audio))
Combining Filters
-----------------
Filters can also be used in a more advanced way by inverting and combining more filters together using bitwise
operators:
- Use ``~`` to invert a filter (behaves like the ``not`` operator).
- Use ``&`` and ``|`` to merge two filters (behave like ``and``, ``or`` operators respectively).
Here are some examples:
- Message is a **text** message **and** is **not edited**.
.. code-block:: python
@app.on_message(Filters.text & ~Filters.edited)
def my_handler(client, message):
print(message)
- Message is a **sticker** **and** is coming from a **channel or** a **private** chat.
.. code-block:: python
@app.on_message(Filters.sticker & (Filters.channel | Filters.private))
def my_handler(client, message):
print(message)
Advanced Filters
----------------
Some filters, like :meth:`command() <pyrogram.Filters.command>` or :meth:`regex() <pyrogram.Filters.regex>`
can also accept arguments:
- Message is either a */start* or */help* **command**.
.. code-block:: python
@app.on_message(Filters.command(["start", "help"]))
def my_handler(client, message):
print(message)
- Message is a **text** message matching the given **regex** pattern.
.. code-block:: python
@app.on_message(Filters.regex("pyrogram"))
def my_handler(client, message):
print(message)
More handlers using different filters can also live together.
.. code-block:: python
@app.on_message(Filters.command("start"))
def start_command(client, message):
print("This is the /start command")
@app.on_message(Filters.command("help"))
def help_command(client, message):
print("This is the /help command")
@app.on_message(Filters.chat("PyrogramChat"))
def from_pyrogramchat(client, message):
print("New message in @PyrogramChat")
Handler Groups
--------------
If you register handlers with overlapping filters, only the first one is executed and any other handler will be ignored.
In order to process the same message 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. This means that a lower group number has
a higher priority.
For example, in:
.. code-block:: python
@app.on_message(Filters.text | Filters.sticker)
def text_or_sticker(client, message):
print("Text or Sticker")
@app.on_message(Filters.text)
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:
.. code-block:: python
@app.on_message(Filters.text, group=1)
def just_text(client, message):
print("Just Text")
or, if you want ``just_text`` to be fired *before* ``text_or_sticker`` (note ``-1``, which is less than ``0``):
.. code-block:: python
@app.on_message(Filters.text, group=-1)
def just_text(client, message):
print("Just Text")
Custom Filters
--------------
Pyrogram already provides lots of built-in :class:`Filters <pyrogram.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() <pyrogram.Filters.create>`.
.. note::
At the moment, the built-in filters are intended to be used with the :obj:`MessageHandler <pyrogram.MessageHandler>`
only.
An example to demonstrate how custom filters work is to show how to create and use one for the
:obj:`CallbackQueryHandler <pyrogram.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
from pyrogram import InlineKeyboardMarkup, InlineKeyboardButton
app.send_message(
"username", # Change this to your username or id
"Pyrogram's custom filter test",
reply_markup=InlineKeyboardMarkup(
[[InlineKeyboardButton("Press me", "pyrogram")]]
)
)
Basic Filters
^^^^^^^^^^^^^
For this basic filter we will be using only the first two parameters of :meth:`Filters.create() <pyrogram.Filters.create>`.
The code below creates a simple filter for hardcoded callback data. This filter will only allow callback queries
containing "pyrogram" as data:
.. code-block:: python
hardcoded_data = Filters.create(
name="HardcodedData",
func=lambda filter, callback_query: callback_query.data == "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:
.. code-block:: python
def func(filter, callback_query):
return callback_query.data == "pyrogram"
hardcoded_data = Filters.create(
name="HardcodedData",
func=func
)
The filter usage remains the same:
.. code-block:: python
@app.on_callback_query(hardcoded_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 dynamic filter like this will make use of the third parameter of :meth:`Filters.create() <pyrogram.Filters.create>`.
This is how a dynamic custom filter looks like:
.. code-block:: python
def dynamic_data(data):
return Filters.create(
name="DynamicData",
func=lambda filter, callback_query: filter.data == callback_query.data,
data=data # "data" kwarg is accessed with "filter.data"
)
And its usage:
.. code-block:: python
@app.on_callback_query(dynamic_data("pyrogram"))
def pyrogram_data(client, callback_query):
client.answer_callback_query(callback_query.id, "it works!")

View File

@ -7,7 +7,8 @@ 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/.
Pyrogram supports Python 3 only, starting from version 3.4 and PyPy.
.. note::
Pyrogram supports Python 3 only, starting from version 3.4 and PyPy.
Install Pyrogram
----------------
@ -44,7 +45,7 @@ If no error shows up you are good to go.
>>> import pyrogram
>>> pyrogram.__version__
'0.7.5'
'0.8.0'
.. _TgCrypto: https://docs.pyrogram.ml/resources/TgCrypto
.. _develop: http://github.com/pyrogram/pyrogram

View File

@ -8,22 +8,29 @@ with Pyrogram.
API Keys
--------
The very first step requires you to obtain a valid Telegram API key.
The very first step requires you to obtain a valid Telegram API key (API id/hash pair).
If you already have one you can skip this step, otherwise:
#. Visit https://my.telegram.org/apps and log in with your Telegram Account.
#. Fill out the form to register a new Telegram application.
#. Done. The Telegram API key consists of two parts: the **App api_id** and the **App api_hash**.
#. Done. The API key consists of two parts: **App api_id** and **App api_hash**.
.. important:: This key should be kept secret.
.. important::
This API key is personal and should be kept secret.
Configuration
-------------
There are two ways to configure a Pyrogram application project, and you can choose the one that fits better for you:
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**.
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 <#api-keys>`_. This is the preferred method because allows you
**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
@ -45,7 +52,8 @@ There are two ways to configure a Pyrogram application project, and you can choo
api_hash="0123456789abcdef0123456789abcdef"
)
.. note:: The examples below assume you have created a ``config.ini`` file, thus they won't show the *api_id*
.. 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.
User Authorization
@ -76,16 +84,17 @@ After successfully authorizing yourself, a new file called ``my_account.session`
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`` file(s) must be kept secret.
.. important:: Your ``*.session`` files are personal and must be kept secret.
Bot Authorization
-----------------
Being written entirely from the ground up, Pyrogram is also able to authorize Bots.
Bots are a special kind of users which also make use of MTProto, the underlying Telegram protocol.
This means that you can use Pyrogram to execute API calls with a Bot identity.
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.
Instead of phone numbers, Bots are authorized via their tokens which are created by BotFather_:
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.
.. code-block:: python
@ -94,9 +103,6 @@ Instead of phone numbers, Bots are authorized via their tokens which are created
app = Client("123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11")
app.run()
That's all, no further action is needed. The session file will be named after the Bot user_id, which is
``123456.session`` for the example above.
.. _installed Pyrogram: Installation.html
.. _`Country Code`: https://en.wikipedia.org/wiki/List_of_country_calling_codes
.. _BotFather: https://t.me/botfather

View File

@ -108,9 +108,9 @@ Examples (more on `GitHub <https://github.com/pyrogram/pyrogram/tree/develop/exa
app.stop()
.. _methods: ../pyrogram/Client.html#available-methods
.. _plenty of them: ../pyrogram/Client.html#available-methods
.. _types: ../pyrogram/types/index.html
.. _methods: ../pyrogram/Client.html#messages
.. _plenty of them: ../pyrogram/Client.html#messages
.. _types: ../pyrogram/Types.html
.. _Raw Functions: Usage.html#using-raw-functions
.. _Community: https://t.me/PyrogramChat
.. _project set up: Setup.html

View File

@ -23,18 +23,15 @@ __copyright__ = "Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance
"e" if sys.getfilesystemencoding() != "utf-8" else "\xe8"
)
__license__ = "GNU Lesser General Public License v3 or later (LGPLv3+)"
__version__ = "0.7.5"
__version__ = "0.8.0"
from .api.errors import Error
from .client.types import (
Audio, Chat, ChatMember, ChatPhoto, Contact, Document, InputMediaPhoto,
InputMediaVideo, InputPhoneContact, Location, Message, MessageEntity,
PhotoSize, Sticker, Update, User, UserProfilePhotos, Venue, GIF, Video,
VideoNote, Voice, CallbackQuery, Messages
)
from .client.types.reply_markup import (
ForceReply, InlineKeyboardButton, InlineKeyboardMarkup,
KeyboardButton, ReplyKeyboardMarkup, ReplyKeyboardRemove
Audio, Chat, ChatMember, ChatMembers, ChatPhoto, Contact, Document, InputMediaPhoto,
InputMediaVideo, InputMediaDocument, InputMediaAudio, InputMediaAnimation, InputPhoneContact,
Location, Message, MessageEntity, Dialog, Dialogs, Photo, PhotoSize, Sticker, Update, User,
UserProfilePhotos, Venue, Animation, Video, VideoNote, Voice, CallbackQuery, Messages, ForceReply,
InlineKeyboardButton, InlineKeyboardMarkup, KeyboardButton, ReplyKeyboardMarkup, ReplyKeyboardRemove
)
from .client import (
Client, ChatAction, ParseMode, Emoji,

View File

@ -16,6 +16,7 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
import logging
from collections import OrderedDict
from datetime import datetime
from io import BytesIO
@ -23,13 +24,23 @@ from json import JSONEncoder, dumps
from ..all import objects
log = logging.getLogger(__name__)
class Object:
all = {}
@staticmethod
def read(b: BytesIO, *args):
return Object.all[int.from_bytes(b.read(4), "little")].read(b, *args)
constructor_id = int.from_bytes(b.read(4), "little")
try:
return Object.all[constructor_id].read(b, *args)
except KeyError:
log.error("Unknown constructor found: {}. Full data: {}".format(
hex(constructor_id),
b.getvalue().hex())
)
def write(self, *args) -> bytes:
pass

View File

@ -30,7 +30,7 @@ class BoolFalse(Object):
return cls.value
def __new__(cls) -> bytes:
return int.to_bytes(cls.ID, 4, "little")
return cls.ID.to_bytes(4, "little")
class BoolTrue(BoolFalse):

View File

@ -48,7 +48,7 @@ class Bytes(Object):
else:
return (
bytes([254])
+ int.to_bytes(length, 3, "little")
+ length.to_bytes(3, "little")
+ value
+ bytes(-length % 4)
)

View File

@ -29,15 +29,12 @@ class Int(Object):
return int.from_bytes(b.read(cls.SIZE), "little", signed=signed)
def __new__(cls, value: int, signed: bool = True) -> bytes:
return int.to_bytes(value, cls.SIZE, "little", signed=signed)
return value.to_bytes(cls.SIZE, "little", signed=signed)
class Long(Int):
SIZE = 8
def __new__(cls, *args):
return super().__new__(cls, *args)
class Int128(Int):
SIZE = 16

View File

@ -29,4 +29,4 @@ class Null(Object):
return None
def __new__(cls) -> bytes:
return int.to_bytes(cls.ID, 4, "little")
return cls.ID.to_bytes(4, "little")

View File

@ -24,7 +24,7 @@ from . import Bytes
class String(Bytes):
@staticmethod
def read(b: BytesIO, *args) -> str:
return super(String, String).read(b).decode()
return super(String, String).read(b).decode(errors="replace")
def __new__(cls, value: str) -> bytes:
return super().__new__(cls, value.encode())

View File

@ -146,6 +146,7 @@ class Client(Methods, BaseClient):
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,
@ -166,6 +167,7 @@ class Client(Methods, BaseClient):
self.device_model = device_model
self.system_version = system_version
self.lang_code = lang_code
self.ipv6 = ipv6
# TODO: Make code consistent, use underscore for private/protected fields
self._proxy = proxy
self.test_mode = test_mode
@ -181,6 +183,13 @@ class Client(Methods, BaseClient):
self.dispatcher = Dispatcher(self, workers)
def __enter__(self):
self.start()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.stop()
@property
def proxy(self):
return self._proxy
@ -201,7 +210,7 @@ class Client(Methods, BaseClient):
raise ConnectionError("Client has already been started")
if self.BOT_TOKEN_RE.match(self.session_name):
self.token = self.session_name
self.bot_token = self.session_name
self.session_name = self.session_name.split(":")[0]
self.load_config()
@ -216,28 +225,33 @@ class Client(Methods, BaseClient):
self.session.start()
self.is_started = True
if self.user_id is None:
if self.token is None:
self.authorize_user()
try:
if self.user_id is None:
if self.bot_token is None:
self.authorize_user()
else:
self.authorize_bot()
self.save_session()
if self.bot_token is None:
now = time.time()
if abs(now - self.date) > Client.OFFLINE_SLEEP:
self.peers_by_username = {}
self.peers_by_phone = {}
self.get_initial_dialogs()
self.get_contacts()
else:
self.send(functions.messages.GetPinnedDialogs())
self.get_initial_dialogs_chunk()
else:
self.authorize_bot()
self.save_session()
if self.token is None:
now = time.time()
if abs(now - self.date) > Client.OFFLINE_SLEEP:
self.peers_by_username = {}
self.peers_by_phone = {}
self.get_dialogs()
self.get_contacts()
else:
self.send(functions.messages.GetPinnedDialogs())
self.get_dialogs_chunk(0)
else:
self.send(functions.updates.GetState())
self.send(functions.updates.GetState())
except Exception as e:
self.is_started = False
self.session.stop()
raise e
for i in range(self.UPDATES_WORKERS):
self.updates_workers_list.append(
@ -381,14 +395,14 @@ class Client(Methods, BaseClient):
flags=0,
api_id=self.api_id,
api_hash=self.api_hash,
bot_auth_token=self.token
bot_auth_token=self.bot_token
)
)
except UserMigrate as e:
self.session.stop()
self.dc_id = e.x
self.auth_key = Auth(self.dc_id, self.test_mode, self._proxy).create()
self.auth_key = Auth(self.dc_id, self.test_mode, self.ipv6, self._proxy).create()
self.session = Session(
self,
@ -433,7 +447,7 @@ class Client(Methods, BaseClient):
self.session.stop()
self.dc_id = e.x
self.auth_key = Auth(self.dc_id, self.test_mode, self._proxy).create()
self.auth_key = Auth(self.dc_id, self.test_mode, self.ipv6, self._proxy).create()
self.session = Session(
self,
@ -613,7 +627,7 @@ class Client(Methods, BaseClient):
if phone is not None:
self.peers_by_phone[phone] = input_peer
if isinstance(entity, types.Chat):
if isinstance(entity, (types.Chat, types.ChatForbidden)):
chat_id = entity.id
peer_id = -chat_id
@ -623,7 +637,7 @@ class Client(Methods, BaseClient):
self.peers_by_id[peer_id] = input_peer
if isinstance(entity, types.Channel):
if isinstance(entity, (types.Channel, types.ChannelForbidden)):
channel_id = entity.id
peer_id = int("-100" + str(channel_id))
@ -632,7 +646,7 @@ class Client(Methods, BaseClient):
if access_hash is None:
continue
username = entity.username
username = getattr(entity, "username", None)
input_peer = types.InputPeerChannel(
channel_id=channel_id,
@ -711,7 +725,9 @@ class Client(Methods, BaseClient):
file_name = "{}_{}_{}{}".format(
media_type_str,
datetime.fromtimestamp(media.date or time.time()).strftime("%Y-%m-%d_%H-%M-%S"),
datetime.fromtimestamp(
getattr(media, "date", None) or time.time()
).strftime("%Y-%m-%d_%H-%M-%S"),
self.rnd_id(),
extension
)
@ -776,6 +792,9 @@ class Client(Methods, BaseClient):
pts = getattr(update, "pts", None)
pts_count = getattr(update, "pts_count", None)
if isinstance(update, types.UpdateChannelTooLong):
log.warning(update)
if isinstance(update, types.UpdateNewChannelMessage):
message = update.message
@ -834,6 +853,8 @@ class Client(Methods, BaseClient):
self.dispatcher.updates.put((diff.other_updates[0], [], []))
elif isinstance(updates, types.UpdateShort):
self.dispatcher.updates.put((updates.update, [], []))
elif isinstance(updates, types.UpdatesTooLong):
log.warning(updates)
except Exception as e:
log.error(e, exc_info=True)
@ -885,30 +906,18 @@ class Client(Methods, BaseClient):
"More info: https://docs.pyrogram.ml/start/ProjectSetup#configuration"
)
for option in {"app_version", "device_model", "system_version", "lang_code"}:
for option in ["app_version", "device_model", "system_version", "lang_code"]:
if getattr(self, option):
pass
else:
setattr(self, option, Client.APP_VERSION)
if parser.has_section("pyrogram"):
setattr(self, option, parser.get(
"pyrogram",
option,
fallback=getattr(Client, option.upper())
))
if self.lang_code:
pass
else:
self.lang_code = Client.LANG_CODE
if parser.has_section("pyrogram"):
self.lang_code = parser.get(
"pyrogram",
"lang_code",
fallback=Client.LANG_CODE
)
else:
setattr(self, option, getattr(Client, option.upper()))
if self._proxy:
self._proxy["enabled"] = True
@ -929,7 +938,7 @@ class Client(Methods, BaseClient):
except FileNotFoundError:
self.dc_id = 1
self.date = 0
self.auth_key = Auth(self.dc_id, self.test_mode, self._proxy).create()
self.auth_key = Auth(self.dc_id, self.test_mode, self.ipv6, self._proxy).create()
else:
self.dc_id = s["dc_id"]
self.test_mode = s["test_mode"]
@ -971,13 +980,17 @@ class Client(Methods, BaseClient):
indent=4
)
def get_dialogs_chunk(self, offset_date):
def get_initial_dialogs_chunk(self, offset_date: int = 0):
while True:
try:
r = self.send(
functions.messages.GetDialogs(
offset_date, 0, types.InputPeerEmpty(),
self.DIALOGS_AT_ONCE, True
offset_date=offset_date,
offset_id=0,
offset_peer=types.InputPeerEmpty(),
limit=self.DIALOGS_AT_ONCE,
hash=0,
exclude_pinned=True
)
)
except FloodWait as e:
@ -987,34 +1000,32 @@ class Client(Methods, BaseClient):
log.info("Total peers: {}".format(len(self.peers_by_id)))
return r
def get_dialogs(self):
def get_initial_dialogs(self):
self.send(functions.messages.GetPinnedDialogs())
dialogs = self.get_dialogs_chunk(0)
dialogs = self.get_initial_dialogs_chunk()
offset_date = utils.get_offset_date(dialogs)
while len(dialogs.dialogs) == self.DIALOGS_AT_ONCE:
dialogs = self.get_dialogs_chunk(offset_date)
dialogs = self.get_initial_dialogs_chunk(offset_date)
offset_date = utils.get_offset_date(dialogs)
self.get_dialogs_chunk(0)
self.get_initial_dialogs_chunk()
def resolve_peer(self, peer_id: int or str):
"""Use this method to get the *InputPeer* of a known *peer_id*.
"""Use this method to get the InputPeer of a known peer_id.
It is intended to be used when working with Raw Functions (i.e: a Telegram API method you wish to use which is
not available yet in the Client class as an easy-to-use method).
This is a utility method intended to be used only when working with Raw Functions (i.e: a Telegram API method
you wish to use which is not available yet in the Client class as an easy-to-use method), whenever an InputPeer
type is required.
Args:
peer_id (``int`` | ``str`` | ``Peer``):
The Peer ID you want to extract the InputPeer from. Can be one of these types: ``int`` (direct ID),
``str`` (@username), :obj:`PeerUser <pyrogram.api.types.PeerUser>`,
:obj:`PeerChat <pyrogram.api.types.PeerChat>`, :obj:`PeerChannel <pyrogram.api.types.PeerChannel>`
peer_id (``int`` | ``str``):
The peer id you want to extract the InputPeer from.
Can be a direct id (int), a username (str) or a phone number (str).
Returns:
:obj:`InputPeerUser <pyrogram.api.types.InputPeerUser>` or
:obj:`InputPeerChat <pyrogram.api.types.InputPeerChat>` or
:obj:`InputPeerChannel <pyrogram.api.types.InputPeerChannel>` depending on the *peer_id*.
On success, the resolved peer id is returned in form of an InputPeer object.
Raises:
:class:`Error <pyrogram.Error>`
@ -1023,38 +1034,21 @@ class Client(Methods, BaseClient):
if peer_id in ("self", "me"):
return types.InputPeerSelf()
match = self.INVITE_LINK_RE.match(peer_id)
try:
decoded = base64.b64decode(match.group(1) + "=" * (-len(match.group(1)) % 4), "-_")
return self.resolve_peer(struct.unpack(">2iq", decoded)[1])
except (AttributeError, binascii.Error, struct.error):
pass
peer_id = re.sub(r"[@+\s]", "", peer_id.lower())
try:
int(peer_id)
except ValueError:
try:
return self.peers_by_username[peer_id]
except KeyError:
if peer_id not in self.peers_by_username:
self.send(functions.contacts.ResolveUsername(peer_id))
return self.peers_by_username[peer_id]
return self.peers_by_username[peer_id]
else:
try:
return self.peers_by_phone[peer_id]
except KeyError:
raise PeerIdInvalid
if type(peer_id) is not int:
if isinstance(peer_id, types.PeerUser):
peer_id = peer_id.user_id
elif isinstance(peer_id, types.PeerChat):
peer_id = -peer_id.chat_id
elif isinstance(peer_id, types.PeerChannel):
peer_id = int("-100" + str(peer_id.channel_id))
try: # User
return self.peers_by_id[peer_id]
except KeyError:
@ -1166,7 +1160,7 @@ class Client(Methods, BaseClient):
session = Session(
self,
dc_id,
Auth(dc_id, self.test_mode, self._proxy).create(),
Auth(dc_id, self.test_mode, self.ipv6, self._proxy).create(),
is_media=True
)
@ -1229,8 +1223,6 @@ class Client(Methods, BaseClient):
break
f.write(chunk)
f.flush()
os.fsync(f.fileno())
offset += limit
@ -1253,7 +1245,7 @@ class Client(Methods, BaseClient):
cdn_session = Session(
self,
r.dc_id,
Auth(r.dc_id, self.test_mode, self._proxy).create(),
Auth(r.dc_id, self.test_mode, self.ipv6, self._proxy).create(),
is_media=True,
is_cdn=True
)
@ -1313,8 +1305,6 @@ class Client(Methods, BaseClient):
assert h.hash == sha256(cdn_chunk).digest(), "Invalid CDN hash part {}".format(i)
f.write(decrypted_chunk)
f.flush()
os.fsync(f.fileno())
offset += limit

View File

@ -41,7 +41,6 @@ class BaseClient:
platform.release()
)
SYSTEM_LANG_CODE = "en"
LANG_CODE = "en"
INVITE_LINK_RE = re.compile(r"^(?:https?://)?(?:www\.)?(?:t(?:elegram)?\.(?:org|me|dog)/joinchat/)([\w-]+)$")
@ -60,12 +59,12 @@ class BaseClient:
5: "document",
8: "sticker",
9: "audio",
10: "gif",
10: "animation",
13: "video_note"
}
def __init__(self):
self.token = None
self.bot_token = None
self.dc_id = None
self.auth_key = None
self.user_id = None

View File

@ -60,6 +60,7 @@ class Str(str):
ENTITIES = {
types.MessageEntityMention.ID: "mention",
types.MessageEntityHashtag.ID: "hashtag",
types.MessageEntityCashtag.ID: "cashtag",
types.MessageEntityBotCommand.ID: "bot_command",
types.MessageEntityUrl.ID: "url",
types.MessageEntityEmail.ID: "email",
@ -68,7 +69,8 @@ ENTITIES = {
types.MessageEntityCode.ID: "code",
types.MessageEntityPre.ID: "pre",
types.MessageEntityTextUrl.ID: "text_link",
types.MessageEntityMentionName.ID: "text_mention"
types.MessageEntityMentionName.ID: "text_mention",
types.MessageEntityPhone.ID: "phone_number"
}
@ -79,18 +81,20 @@ def parse_entities(entities: list, users: dict) -> list:
entity_type = ENTITIES.get(entity.ID, None)
if entity_type:
output_entities.append(pyrogram_types.MessageEntity(
type=entity_type,
offset=entity.offset,
length=entity.length,
url=getattr(entity, "url", None),
user=parse_user(
users.get(
getattr(entity, "user_id", None),
None
output_entities.append(
pyrogram_types.MessageEntity(
type=entity_type,
offset=entity.offset,
length=entity.length,
url=getattr(entity, "url", None),
user=parse_user(
users.get(
getattr(entity, "user_id", None),
None
)
)
)
))
)
return output_entities
@ -128,6 +132,10 @@ def parse_chat_photo(photo):
def parse_user(user: types.User) -> pyrogram_types.User or None:
return pyrogram_types.User(
id=user.id,
is_self=user.is_self,
is_contact=user.contact,
is_mutual_contact=user.mutual_contact,
is_deleted=user.deleted,
is_bot=user.bot,
first_name=user.first_name,
last_name=user.last_name,
@ -288,7 +296,7 @@ def parse_messages(
venue = None
audio = None
voice = None
gif = None
animation = None
video = None
video_note = None
sticker = None
@ -329,13 +337,23 @@ def parse_messages(
),
width=size.w,
height=size.h,
file_size=file_size,
date=photo.date
file_size=file_size
)
photo_sizes.append(photo_size)
photo = photo_sizes
photo = pyrogram_types.Photo(
id=b64encode(
pack(
"<qq",
photo.id,
photo.access_hash
),
b"-_"
).decode().rstrip("="),
date=photo.date,
sizes=photo_sizes
)
elif isinstance(media, types.MessageMediaGeo):
geo_point = media.geo
@ -349,6 +367,7 @@ def parse_messages(
phone_number=media.phone_number,
first_name=media.first_name,
last_name=media.last_name or None,
vcard=media.vcard or None,
user_id=media.user_id or None
)
elif isinstance(media, types.MessageMediaVenue):
@ -359,7 +378,8 @@ def parse_messages(
),
title=media.title,
address=media.address,
foursquare_id=media.venue_id or None
foursquare_id=media.venue_id or None,
foursquare_type=media.venue_type
)
elif isinstance(media, types.MessageMediaDocument):
doc = media.document
@ -390,8 +410,7 @@ def parse_messages(
duration=audio_attributes.duration,
mime_type=doc.mime_type,
file_size=doc.size,
thumb=parse_thumb(doc.thumb),
file_name=file_name,
waveform=audio_attributes.waveform,
date=doc.date
)
else:
@ -417,7 +436,7 @@ def parse_messages(
elif types.DocumentAttributeAnimated in attributes:
video_attributes = attributes.get(types.DocumentAttributeVideo, None)
gif = pyrogram_types.GIF(
animation = pyrogram_types.Animation(
file_id=encode(
pack(
"<iiqq",
@ -454,7 +473,6 @@ def parse_messages(
duration=video_attributes.duration,
thumb=parse_thumb(doc.thumb),
file_size=doc.size,
file_name=file_name,
mime_type=doc.mime_type,
date=doc.date
)
@ -570,7 +588,7 @@ def parse_messages(
venue=venue,
audio=audio,
voice=voice,
gif=gif,
animation=animation,
video=video,
video_note=video_note,
sticker=sticker,
@ -664,13 +682,23 @@ def parse_messages(
),
width=size.w,
height=size.h,
file_size=file_size,
date=photo.date
file_size=file_size
)
photo_sizes.append(photo_size)
new_chat_photo = photo_sizes
new_chat_photo = pyrogram_types.Photo(
id=b64encode(
pack(
"<qq",
photo.id,
photo.access_hash
),
b"-_"
).decode().rstrip("="),
date=photo.date,
sizes=photo_sizes
)
m = pyrogram_types.Message(
message_id=message.id,
@ -757,7 +785,7 @@ def get_offset_date(dialogs):
return 0
def parse_photos(photos):
def parse_profile_photos(photos):
if isinstance(photos, types.photos.Photos):
total_count = len(photos.photos)
else:
@ -795,13 +823,25 @@ def parse_photos(photos):
),
width=size.w,
height=size.h,
file_size=file_size,
date=photo.date
file_size=file_size
)
photo_sizes.append(photo_size)
user_profile_photos.append(photo_sizes)
user_profile_photos.append(
pyrogram_types.Photo(
id=b64encode(
pack(
"<qq",
photo.id,
photo.access_hash
),
b"-_"
).decode().rstrip("="),
date=photo.date,
sizes=photo_sizes
)
)
return pyrogram_types.UserProfilePhotos(
total_count=total_count,
@ -852,8 +892,8 @@ def parse_chat_full(
chat_full: types.messages.ChatFull or types.UserFull
) -> pyrogram_types.Chat:
if isinstance(chat_full, types.UserFull):
chat = parse_user_chat(chat_full.user)
chat.description = chat_full.about
parsed_chat = parse_user_chat(chat_full.user)
parsed_chat.description = chat_full.about
else:
full_chat = chat_full.full_chat
chat = None
@ -863,21 +903,134 @@ def parse_chat_full(
chat = i
if isinstance(full_chat, types.ChatFull):
chat = parse_chat_chat(chat)
parsed_chat = parse_chat_chat(chat)
if isinstance(full_chat.participants, types.ChatParticipants):
parsed_chat.members_count = len(full_chat.participants.participants)
else:
chat = parse_channel_chat(chat)
chat.description = full_chat.about or None
parsed_chat = parse_channel_chat(chat)
parsed_chat.members_count = full_chat.participants_count
parsed_chat.description = full_chat.about or None
# TODO: Add StickerSet type
chat.can_set_sticker_set = full_chat.can_set_stickers
chat.sticker_set_name = full_chat.stickerset
parsed_chat.can_set_sticker_set = full_chat.can_set_stickers
parsed_chat.sticker_set_name = full_chat.stickerset
if full_chat.pinned_msg_id:
chat.pinned_message = client.get_messages(
int("-100" + str(full_chat.id)),
parsed_chat.pinned_message = client.get_messages(
parsed_chat.id,
full_chat.pinned_msg_id
)
if isinstance(full_chat.exported_invite, types.ChatInviteExported):
chat.invite_link = full_chat.exported_invite.link
parsed_chat.invite_link = full_chat.exported_invite.link
return chat
return parsed_chat
def parse_dialog_chat(peer, users: dict, chats: dict):
if isinstance(peer, types.PeerUser):
return parse_user_chat(users[peer.user_id])
elif isinstance(peer, types.PeerChat):
return parse_chat_chat(chats[peer.chat_id])
else:
return parse_channel_chat(chats[peer.channel_id])
def parse_chat_members(members: types.channels.ChannelParticipants or types.messages.ChatFull):
users = {i.id: i for i in members.users}
parsed_members = []
if isinstance(members, types.channels.ChannelParticipants):
members = members.participants
for member in members:
user = parse_user(users[member.user_id])
if isinstance(member, (types.ChannelParticipant, types.ChannelParticipantSelf)):
parsed_members.append(
pyrogram_types.ChatMember(
user=user,
status="member"
)
)
elif isinstance(member, types.ChannelParticipantCreator):
parsed_members.append(
pyrogram_types.ChatMember(
user=user,
status="creator"
)
)
elif isinstance(member, types.ChannelParticipantAdmin):
rights = member.admin_rights # type: types.ChannelAdminRights
parsed_members.append(
pyrogram_types.ChatMember(
user=user,
status="administrator",
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
)
)
elif isinstance(member, types.ChannelParticipantBanned):
rights = member.banned_rights # type: types.ChannelBannedRights
chat_member = pyrogram_types.ChatMember(
user=user,
status="kicked" if rights.view_messages else "restricted",
until_date=0 if rights.until_date == (1 << 31) - 1 else rights.until_date
)
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
parsed_members.append(chat_member)
return pyrogram_types.ChatMembers(
total_count=members.count,
chat_members=parsed_members
)
else:
members = members.full_chat.participants.participants
for member in members:
user = parse_user(users[member.user_id])
if isinstance(member, types.ChatParticipant):
parsed_members.append(
pyrogram_types.ChatMember(
user=user,
status="member"
)
)
elif isinstance(member, types.ChatParticipantCreator):
parsed_members.append(
pyrogram_types.ChatMember(
user=user,
status="creator"
)
)
elif isinstance(member, types.ChatParticipantAdmin):
parsed_members.append(
pyrogram_types.ChatMember(
user=user,
status="administrator"
)
)
return pyrogram_types.ChatMembers(
total_count=len(members),
chat_members=parsed_members
)

View File

@ -19,10 +19,32 @@
import re
from .filter import Filter
from ..types.reply_markup import InlineKeyboardMarkup, ReplyKeyboardMarkup
from ..types.bots import InlineKeyboardMarkup, ReplyKeyboardMarkup
def build(name: str, func: callable, **kwargs) -> type:
def create(name: str, func: callable, **kwargs) -> type:
"""Use this method to create a Filter.
Custom filters give you extra control over which updates are allowed or not to be processed by your handlers.
Args:
name (``str``):
Your filter's name. Can be anything you like.
func (``callable``):
A function that accepts two arguments *(filter, update)* and returns a Boolean: True if the update should be
handled, False otherwise.
The "update" argument type will vary depending on which `Handler <Handlers.html>`_ is coming from.
For example, in a :obj:`MessageHandler <pyrogram.MessageHandler>` the update type will be
a :obj:`Message <pyrogram.Message>`; in a :obj:`CallbackQueryHandler <pyrogram.CallbackQueryHandler>` the
update type will be a :obj:`CallbackQuery <pyrogram.CallbackQuery>`. Your function body can then access the
incoming update and decide whether to allow it or not.
**kwargs (``any``, *optional*):
Any keyword argument you would like to pass. Useful for custom filters that accept parameters (e.g.:
:meth:`Filters.command`, :meth:`Filters.regex`).
"""
# TODO: unpack kwargs using **kwargs into the dict itself. For Python 3.5+ only
d = {"__call__": func}
d.update(kwargs)
@ -30,112 +52,118 @@ def build(name: str, func: callable, **kwargs) -> type:
class Filters:
"""This class provides access to all Filters available in Pyrogram.
Filters are intended to be used with the :obj:`MessageHandler <pyrogram.MessageHandler>`."""
"""This class provides access to all library-defined Filters available in Pyrogram.
bot = build("Bot", lambda _, m: bool(m.from_user and m.from_user.is_bot))
The Filters listed here are intended to be used with the :obj:`MessageHandler <pyrogram.MessageHandler>` only.
At the moment, if you want to filter updates coming from different `Handlers <Handlers.html>`_ you have to create
your own filters with :meth:`Filters.create` and use them in the same way.
"""
create = create
bot = create("Bot", lambda _, m: bool(m.from_user and m.from_user.is_bot))
"""Filter messages coming from bots"""
incoming = build("Incoming", lambda _, m: not m.outgoing)
incoming = create("Incoming", lambda _, m: not m.outgoing)
"""Filter incoming messages."""
outgoing = build("Outgoing", lambda _, m: m.outgoing)
outgoing = create("Outgoing", lambda _, m: m.outgoing)
"""Filter outgoing messages."""
text = build("Text", lambda _, m: bool(m.text))
text = create("Text", lambda _, m: bool(m.text))
"""Filter text messages."""
reply = build("Reply", lambda _, m: bool(m.reply_to_message))
reply = create("Reply", lambda _, m: bool(m.reply_to_message))
"""Filter messages that are replies to other messages."""
forwarded = build("Forwarded", lambda _, m: bool(m.forward_date))
forwarded = create("Forwarded", lambda _, m: bool(m.forward_date))
"""Filter messages that are forwarded."""
caption = build("Caption", lambda _, m: bool(m.caption))
caption = create("Caption", lambda _, m: bool(m.caption))
"""Filter media messages that contain captions."""
edited = build("Edited", lambda _, m: bool(m.edit_date))
edited = create("Edited", lambda _, m: bool(m.edit_date))
"""Filter edited messages."""
audio = build("Audio", lambda _, m: bool(m.audio))
audio = create("Audio", lambda _, m: bool(m.audio))
"""Filter messages that contain :obj:`Audio <pyrogram.api.types.pyrogram.Audio>` objects."""
document = build("Document", lambda _, m: bool(m.document))
document = create("Document", lambda _, m: bool(m.document))
"""Filter messages that contain :obj:`Document <pyrogram.api.types.pyrogram.Document>` objects."""
photo = build("Photo", lambda _, m: bool(m.photo))
photo = create("Photo", lambda _, m: bool(m.photo))
"""Filter messages that contain :obj:`Photo <pyrogram.api.types.pyrogram.PhotoSize>` objects."""
sticker = build("Sticker", lambda _, m: bool(m.sticker))
sticker = create("Sticker", lambda _, m: bool(m.sticker))
"""Filter messages that contain :obj:`Sticker <pyrogram.api.types.pyrogram.Sticker>` objects."""
gif = build("GIF", lambda _, m: bool(m.gif))
"""Filter messages that contain :obj:`GIF <pyrogram.api.types.pyrogram.GIF>` objects."""
animation = create("GIF", lambda _, m: bool(m.animation))
"""Filter messages that contain :obj:`Animation <pyrogram.api.types.pyrogram.Animation>` objects."""
video = build("Video", lambda _, m: bool(m.video))
video = create("Video", lambda _, m: bool(m.video))
"""Filter messages that contain :obj:`Video <pyrogram.api.types.pyrogram.Video>` objects."""
voice = build("Voice", lambda _, m: bool(m.voice))
voice = create("Voice", lambda _, m: bool(m.voice))
"""Filter messages that contain :obj:`Voice <pyrogram.api.types.pyrogram.Voice>` note objects."""
video_note = build("Voice", lambda _, m: bool(m.video_note))
video_note = create("Voice", lambda _, m: bool(m.video_note))
"""Filter messages that contain :obj:`VideoNote <pyrogram.api.types.pyrogram.VideoNote>` objects."""
contact = build("Contact", lambda _, m: bool(m.contact))
contact = create("Contact", lambda _, m: bool(m.contact))
"""Filter messages that contain :obj:`Contact <pyrogram.api.types.pyrogram.Contact>` objects."""
location = build("Location", lambda _, m: bool(m.location))
location = create("Location", lambda _, m: bool(m.location))
"""Filter messages that contain :obj:`Location <pyrogram.api.types.pyrogram.Location>` objects."""
venue = build("Venue", lambda _, m: bool(m.venue))
venue = create("Venue", lambda _, m: bool(m.venue))
"""Filter messages that contain :obj:`Venue <pyrogram.api.types.pyrogram.Venue>` objects."""
private = build("Private", lambda _, m: bool(m.chat and m.chat.type == "private"))
private = create("Private", lambda _, m: bool(m.chat and m.chat.type == "private"))
"""Filter messages sent in private chats."""
group = build("Group", lambda _, m: bool(m.chat and m.chat.type in {"group", "supergroup"}))
group = create("Group", lambda _, m: bool(m.chat and m.chat.type in {"group", "supergroup"}))
"""Filter messages sent in group or supergroup chats."""
channel = build("Channel", lambda _, m: bool(m.chat and m.chat.type == "channel"))
channel = create("Channel", lambda _, m: bool(m.chat and m.chat.type == "channel"))
"""Filter messages sent in channels."""
new_chat_members = build("NewChatMembers", lambda _, m: bool(m.new_chat_members))
new_chat_members = create("NewChatMembers", lambda _, m: bool(m.new_chat_members))
"""Filter service messages for new chat members."""
left_chat_member = build("LeftChatMember", lambda _, m: bool(m.left_chat_member))
left_chat_member = create("LeftChatMember", lambda _, m: bool(m.left_chat_member))
"""Filter service messages for members that left the chat."""
new_chat_title = build("NewChatTitle", lambda _, m: bool(m.new_chat_title))
new_chat_title = create("NewChatTitle", lambda _, m: bool(m.new_chat_title))
"""Filter service messages for new chat titles."""
new_chat_photo = build("NewChatPhoto", lambda _, m: bool(m.new_chat_photo))
new_chat_photo = create("NewChatPhoto", lambda _, m: bool(m.new_chat_photo))
"""Filter service messages for new chat photos."""
delete_chat_photo = build("DeleteChatPhoto", lambda _, m: bool(m.delete_chat_photo))
delete_chat_photo = create("DeleteChatPhoto", lambda _, m: bool(m.delete_chat_photo))
"""Filter service messages for deleted photos."""
group_chat_created = build("GroupChatCreated", lambda _, m: bool(m.group_chat_created))
group_chat_created = create("GroupChatCreated", lambda _, m: bool(m.group_chat_created))
"""Filter service messages for group chat creations."""
supergroup_chat_created = build("SupergroupChatCreated", lambda _, m: bool(m.supergroup_chat_created))
supergroup_chat_created = create("SupergroupChatCreated", lambda _, m: bool(m.supergroup_chat_created))
"""Filter service messages for supergroup chat creations."""
channel_chat_created = build("ChannelChatCreated", lambda _, m: bool(m.channel_chat_created))
channel_chat_created = create("ChannelChatCreated", lambda _, m: bool(m.channel_chat_created))
"""Filter service messages for channel chat creations."""
migrate_to_chat_id = build("MigrateToChatId", lambda _, m: bool(m.migrate_to_chat_id))
migrate_to_chat_id = create("MigrateToChatId", lambda _, m: bool(m.migrate_to_chat_id))
"""Filter service messages that contain migrate_to_chat_id."""
migrate_from_chat_id = build("MigrateFromChatId", lambda _, m: bool(m.migrate_from_chat_id))
migrate_from_chat_id = create("MigrateFromChatId", lambda _, m: bool(m.migrate_from_chat_id))
"""Filter service messages that contain migrate_from_chat_id."""
pinned_message = build("PinnedMessage", lambda _, m: bool(m.pinned_message))
pinned_message = create("PinnedMessage", lambda _, m: bool(m.pinned_message))
"""Filter service messages for pinned messages."""
reply_keyboard = build("ReplyKeyboard", lambda _, m: isinstance(m.reply_markup, ReplyKeyboardMarkup))
reply_keyboard = create("ReplyKeyboard", lambda _, m: isinstance(m.reply_markup, ReplyKeyboardMarkup))
"""Filter messages containing reply keyboard markups"""
inline_keyboard = build("InlineKeyboard", lambda _, m: isinstance(m.reply_markup, InlineKeyboardMarkup))
inline_keyboard = create("InlineKeyboard", lambda _, m: isinstance(m.reply_markup, InlineKeyboardMarkup))
"""Filter messages containing inline keyboard markups"""
@staticmethod
@ -174,7 +202,7 @@ class Filters:
return bool(m.command)
return build(
return create(
"Command",
f,
c={command if case_sensitive
@ -206,51 +234,67 @@ class Filters:
m.matches = [i for i in _.p.finditer(m.text or "")]
return bool(m.matches)
return build("Regex", f, p=re.compile(pattern, flags))
return create("Regex", f, p=re.compile(pattern, flags))
@staticmethod
def user(user: int or str or list):
"""Filter messages coming from specific users.
# noinspection PyPep8Naming
class user(Filter, set):
"""Filter messages coming from one or more users.
You can use `set bound methods <https://docs.python.org/3/library/stdtypes.html#set>`_ to manipulate the
users container.
Args:
user (``int`` | ``str`` | ``list``):
The user or list of user IDs (int) or usernames (str) the filter should look for.
users (``int`` | ``str`` | ``list``):
Pass one or more user ids/usernames to filter users.
Defaults to None (no users).
"""
return build(
"User",
lambda _, m: bool(m.from_user
and (m.from_user.id in _.u
or (m.from_user.username
and m.from_user.username.lower() in _.u))),
u=(
{user.lower().strip("@") if type(user) is str else user}
if not isinstance(user, list)
else {i.lower().strip("@") if type(i) is str else i for i in user}
)
)
@staticmethod
def chat(chat: int or str or list):
"""Filter messages coming from specific chats.
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__(
{i.lower().strip("@") if type(i) is str else i for i in users}
if type(users) is list else
{users.lower().strip("@") if type(users) is str else 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))
)
# noinspection PyPep8Naming
class chat(Filter, set):
"""Filter messages coming from one or more chats.
You can use `set bound methods <https://docs.python.org/3/library/stdtypes.html#set>`_ to manipulate the
chats container.
Args:
chat (``int`` | ``str`` | ``list``):
The chat or list of chat IDs (int) or usernames (str) the filter should look for.
chats (``int`` | ``str`` | ``list``):
Pass one or more chat ids/usernames to filter chats.
Defaults to None (no chats).
"""
return build(
"Chat",
lambda _, m: bool(m.chat
and (m.chat.id in _.c
or (m.chat.username
and m.chat.username.lower() in _.c))),
c=(
{chat.lower().strip("@") if type(chat) is str else chat}
if not isinstance(chat, list)
else {i.lower().strip("@") if type(i) is str else i for i in chat}
)
)
service = build(
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__(
{i.lower().strip("@") if type(i) is str else i for i in chats}
if type(chats) is list else
{chats.lower().strip("@") if type(chats) is str else 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))
)
service = create(
"Service",
lambda _, m: bool(
Filters.new_chat_members(m)
@ -268,7 +312,7 @@ class Filters:
)
"""Filter all service messages."""
media = build(
media = create(
"Media",
lambda _, m: bool(
Filters.audio(m)
@ -276,7 +320,7 @@ class Filters:
or Filters.photo(m)
or Filters.sticker(m)
or Filters.video(m)
or Filters.gif(m)
or Filters.animation(m)
or Filters.voice(m)
or Filters.video_note(m)
or Filters.contact(m)

View File

@ -49,6 +49,6 @@ class CallbackQueryHandler(Handler):
def check(self, callback_query):
return (
self.filters(callback_query)
if self.filters
if callable(self.filters)
else True
)

View File

@ -50,6 +50,6 @@ class DeletedMessagesHandler(Handler):
def check(self, messages):
return (
self.filters(messages.messages[0])
if self.filters
if callable(self.filters)
else True
)

View File

@ -50,6 +50,6 @@ class MessageHandler(Handler):
def check(self, message):
return (
self.filters(message)
if self.filters
if callable(self.filters)
else True
)

View File

@ -20,10 +20,10 @@ from .bots import Bots
from .chats import Chats
from .contacts import Contacts
from .decorators import Decorators
from .download_media import DownloadMedia
from .messages import Messages
from .password import Password
from .users import Users
from .utilities import Utilities
class Methods(
@ -32,7 +32,7 @@ class Methods(
Password,
Chats,
Users,
DownloadMedia,
Utilities,
Messages,
Decorators
):

View File

@ -33,7 +33,6 @@ class RequestCallbackAnswer(BaseClient):
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).
For a private channel/supergroup you can use its *t.me/joinchat/* link.
message_id (``int``):
The message id the inline keyboard is attached on.

View File

@ -35,7 +35,6 @@ class SendInlineBotResult(BaseClient):
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).
For a private channel/supergroup you can use its *t.me/joinchat/* link.
query_id (``int``):
Unique identifier for the answered query.

View File

@ -16,14 +16,24 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
from .delete_chat_photo import DeleteChatPhoto
from .export_chat_invite_link import ExportChatInviteLink
from .get_chat import GetChat
from .get_chat_member import GetChatMember
from .get_chat_members import GetChatMembers
from .get_chat_members_count import GetChatMembersCount
from .get_dialogs import GetDialogs
from .join_chat import JoinChat
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_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
class Chats(
@ -34,6 +44,16 @@ class Chats(
KickChatMember,
UnbanChatMember,
RestrictChatMember,
PromoteChatMember
PromoteChatMember,
GetChatMembers,
GetChatMember,
SetChatPhoto,
DeleteChatPhoto,
SetChatTitle,
SetChatDescription,
PinChatMessage,
UnpinChatMessage,
GetDialogs,
GetChatMembersCount
):
pass

View File

@ -0,0 +1,63 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance>
#
# 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 <http://www.gnu.org/licenses/>.
from pyrogram.api import functions, types
from ...ext import BaseClient
class DeleteChatPhoto(BaseClient):
def delete_chat_photo(self, chat_id: int or str):
"""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.
Note:
In regular groups (non-supergroups), this method will only work if the "All Members Are Admins"
setting is off.
Args:
chat_id (``int`` | ``str``):
Unique identifier (int) or username (str) of the target chat.
Returns:
True on success.
Raises:
:class:`Error <pyrogram.Error>`
``ValueError``: If a chat_id belongs to user.
"""
peer = self.resolve_peer(chat_id)
if isinstance(peer, types.InputPeerChat):
self.send(
functions.messages.EditChatPhoto(
chat_id=peer.chat_id,
photo=types.InputChatPhotoEmpty()
)
)
elif isinstance(peer, types.InputPeerChannel):
self.send(
functions.channels.EditPhoto(
channel=peer,
photo=types.InputChatPhotoEmpty()
)
)
else:
raise ValueError("The chat_id \"{}\" belongs to a user".format(chat_id))
return True

View File

@ -25,6 +25,10 @@ class GetChat(BaseClient):
"""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.)
Args:
chat_id (``int`` | ``str``):
Unique identifier (int) or username (str) of the target chat.
Returns:
On success, a :obj:`Chat <pyrogram.Chat>` object is returned.

View File

@ -0,0 +1,75 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance>
#
# 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 <http://www.gnu.org/licenses/>.
from pyrogram.api import functions, types, errors
from ...ext import BaseClient, utils
class GetChatMember(BaseClient):
def get_chat_member(self,
chat_id: int or str,
user_id: int or str):
"""Use this method to get information about one member of a chat.
Args:
chat_id (``int`` | ``str``):
Unique identifier (int) or username (str) of the target chat.
user_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).
Returns:
On success, a :obj:`ChatMember <pyrogram.ChatMember>` object is returned.
Raises:
:class:`Error <pyrogram.Error>`
"""
chat_id = self.resolve_peer(chat_id)
user_id = self.resolve_peer(user_id)
if isinstance(chat_id, types.InputPeerChat):
full_chat = self.send(
functions.messages.GetFullChat(
chat_id=chat_id.chat_id
)
)
for member in utils.parse_chat_members(full_chat).chat_members:
if member.user.id == user_id.user_id:
return member
else:
raise errors.UserNotParticipant
elif isinstance(chat_id, types.InputPeerChannel):
r = self.send(
functions.channels.GetParticipant(
channel=chat_id,
user_id=user_id
)
)
return utils.parse_chat_members(
types.channels.ChannelParticipants(
count=1,
participants=[r.participant],
users=r.users
)
).chat_members[0]
else:
raise ValueError("The chat_id \"{}\" belongs to a user".format(chat_id))

View File

@ -0,0 +1,115 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance>
#
# 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 <http://www.gnu.org/licenses/>.
from pyrogram.api import functions, types
from ...ext import BaseClient, utils
class Filters:
ALL = "all"
KICKED = "kicked"
RESTRICTED = "restricted"
BOTS = "bots"
RECENT = "recent"
ADMINISTRATORS = "administrators"
class GetChatMembers(BaseClient):
def get_chat_members(self,
chat_id: int or str,
offset: int = 0,
limit: int = 200,
query: str = "",
filter: str = Filters.ALL):
"""Use this method to get the members list of a chat.
A chat can be either a basic group, a supergroup or a channel.
You must be admin to retrieve the members (also known as "subscribers") list of a channel.
Args:
chat_id (``int`` | ``str``):
Unique identifier (int) or username (str) of the target chat.
offset (``int``, *optional*):
Sequential number of the first member to be returned.
Defaults to 0 [1]_.
limit (``int``, *optional*):
Limits the number of members to be retrieved.
Defaults to 200, which is also the maximum limit allowed per method call.
query (``str``, *optional*):
Query string to filter members based on their display names and usernames.
Defaults to "" (empty string) [2]_.
filter (``str``, *optional*):
Filter used to select the kind of members you want to retrieve. Only applicable for supergroups
and channels. It can be any of the followings:
*"all"* - all kind of members,
*"kicked"* - kicked (banned) members only,
*"restricted"* - restricted members only,
*"bots"* - bots only,
*"recent"* - recent members only,
*"administrators"* - chat administrators only.
Defaults to *"all"*.
.. [1] On supergroups and channels you can get up to 10,000 members for a single query string.
.. [2] A query string is applicable only for *"all"*, *"kicked"* and *"restricted"* filters only.
"""
peer = self.resolve_peer(chat_id)
if isinstance(peer, types.InputPeerChat):
return utils.parse_chat_members(
self.send(
functions.messages.GetFullChat(
peer.chat_id
)
)
)
elif isinstance(peer, types.InputPeerChannel):
filter = filter.lower()
if filter == Filters.ALL:
filter = types.ChannelParticipantsSearch(q=query)
elif filter == Filters.KICKED:
filter = types.ChannelParticipantsKicked(q=query)
elif filter == Filters.RESTRICTED:
filter = types.ChannelParticipantsBanned(q=query)
elif filter == Filters.BOTS:
filter = types.ChannelParticipantsBots()
elif filter == Filters.RECENT:
filter = types.ChannelParticipantsRecent()
elif filter == Filters.ADMINISTRATORS:
filter = types.ChannelParticipantsAdmins()
else:
raise ValueError("Invalid filter \"{}\"".format(filter))
return utils.parse_chat_members(
self.send(
functions.channels.GetParticipants(
channel=peer,
filter=filter,
offset=offset,
limit=limit,
hash=0
)
)
)
else:
raise ValueError("The chat_id \"{}\" belongs to a user".format(chat_id))

View File

@ -0,0 +1,53 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance>
#
# 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 <http://www.gnu.org/licenses/>.
from pyrogram.api import functions, types
from ...ext import BaseClient
class GetChatMembersCount(BaseClient):
def get_chat_members_count(self, chat_id: int or str):
"""Use this method to get the number of members in a chat.
Args:
chat_id (``int`` | ``str``):
Unique identifier (int) or username (str) of the target chat.
Returns:
On success, an integer is returned.
Raises:
:class:`Error <pyrogram.Error>`
``ValueError``: If a chat_id belongs to user.
"""
peer = self.resolve_peer(chat_id)
if isinstance(peer, types.InputPeerChat):
return self.send(
functions.messages.GetChats(
id=[peer.chat_id]
)
).chats[0].participants_count
elif isinstance(peer, types.InputPeerChannel):
return self.send(
functions.channels.GetFullChannel(
channel=peer
)
).full_chat.participants_count
else:
raise ValueError("The chat_id \"{}\" belongs to a user".format(chat_id))

View File

@ -0,0 +1,125 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance>
#
# 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 <http://www.gnu.org/licenses/>.
import pyrogram
from pyrogram.api import functions, types
from ...ext import BaseClient, utils
class GetDialogs(BaseClient):
def get_dialogs(self,
offset_dialogs=None,
limit: int = 100,
pinned_only: bool = False):
"""Use this method to get the user's dialogs
You can get up to 100 dialogs at once.
Args:
limit (``str``, *optional*):
Limits the number of dialogs to be retrieved.
Defaults to 100
pinned_only (``bool``, *optional*):
Pass True if you want to get only pinned dialogs.
Defaults to False.
offset_dialogs (:obj:`Dialogs`):
Pass the previous dialogs object to retrieve the next dialogs chunk starting from the last dialog.
Defaults to None (start from the beginning).
Returns:
On success, a :obj:`Dialogs` object is returned.
Raises:
:class:`Error`
"""
if pinned_only:
r = self.send(functions.messages.GetPinnedDialogs())
else:
offset_date = 0
if offset_dialogs:
for dialog in reversed(offset_dialogs.dialogs):
top_message = dialog.top_message
if top_message:
message_date = top_message.date
if message_date:
offset_date = message_date
break
r = self.send(
functions.messages.GetDialogs(
offset_date=offset_date,
offset_id=0,
offset_peer=types.InputPeerEmpty(),
limit=limit,
hash=0,
exclude_pinned=True
)
)
users = {i.id: i for i in r.users}
chats = {i.id: i for i in r.chats}
messages = {}
for message in r.messages:
to_id = message.to_id
if isinstance(to_id, types.PeerUser):
if message.out:
chat_id = to_id.user_id
else:
chat_id = message.from_id
elif isinstance(to_id, types.PeerChat):
chat_id = -to_id.chat_id
else:
chat_id = int("-100" + str(to_id.channel_id))
messages[chat_id] = utils.parse_messages(self, message, users, chats)
dialogs = []
for dialog in r.dialogs:
chat_id = dialog.peer
if isinstance(chat_id, types.PeerUser):
chat_id = chat_id.user_id
elif isinstance(chat_id, types.PeerChat):
chat_id = -chat_id.chat_id
else:
chat_id = int("-100" + str(chat_id.channel_id))
dialogs.append(
pyrogram.Dialog(
chat=utils.parse_dialog_chat(dialog.peer, users, chats),
top_message=messages.get(chat_id),
unread_messages_count=dialog.unread_count,
unread_mentions_count=dialog.unread_mentions_count,
unread_mark=dialog.unread_mark,
is_pinned=dialog.pinned
)
)
return pyrogram.Dialogs(
total_count=getattr(r, "count", len(r.dialogs)),
dialogs=dialogs
)

View File

@ -38,7 +38,6 @@ class KickChatMember(BaseClient):
Args:
chat_id (``int`` | ``str``):
Unique identifier (int) or username (str) of the target chat.
For a private channel/supergroup you can use its *t.me/joinchat/* link.
user_id (``int`` | ``str``):
Unique identifier (int) or username (str) of the target user.

View File

@ -0,0 +1,62 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance>
#
# 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 <http://www.gnu.org/licenses/>.
from pyrogram.api import functions, types
from ...ext import BaseClient
class PinChatMessage(BaseClient):
def pin_chat_message(self, chat_id: int or str, message_id: int, disable_notification: bool = None):
"""Use this method to pin a message in a supergroup or a channel.
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.
Args:
chat_id (``int`` | ``str``):
Unique identifier (int) or username (str) of the target chat.
message_id (``int``):
Identifier of a message to pin.
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.
Returns:
True on success.
Raises:
:class:`Error <pyrogram.Error>`
``ValueError``: If a chat_id doesn't belong to a supergroup or a channel.
"""
peer = self.resolve_peer(chat_id)
if isinstance(peer, types.InputPeerChannel):
self.send(
functions.channels.UpdatePinnedMessage(
channel=peer,
id=message_id,
silent=disable_notification or None
)
)
elif isinstance(peer, types.InputPeerChat):
raise ValueError("The chat_id \"{}\" belongs to a basic group".format(chat_id))
else:
raise ValueError("The chat_id \"{}\" belongs to a user".format(chat_id))
return True

View File

@ -39,7 +39,6 @@ class PromoteChatMember(BaseClient):
Args:
chat_id (``int`` | ``str``):
Unique identifier (int) or username (str) of the target chat.
For a private channel/supergroup you can use its *t.me/joinchat/* link.
user_id (``int`` | ``str``):
Unique identifier (int) or username (str) of the target user.

View File

@ -36,7 +36,6 @@ class RestrictChatMember(BaseClient):
Args:
chat_id (``int`` | ``str``):
Unique identifier (int) or username (str) of the target chat.
For a private channel/supergroup you can use its *t.me/joinchat/* link.
user_id (``int`` | ``str``):
Unique identifier (int) or username (str) of the target user.

View File

@ -0,0 +1,56 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance>
#
# 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 <http://www.gnu.org/licenses/>.
from pyrogram.api import functions, types
from ...ext import BaseClient
class SetChatDescription(BaseClient):
def set_chat_description(self, chat_id: int or str, description: str):
"""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.
Args:
chat_id (``int`` | ``str``):
Unique identifier (int) or username (str) of the target chat.
description (``str``):
New chat description, 0-255 characters.
Returns:
True on success.
Raises:
:class:`Error <pyrogram.Error>`
``ValueError``: If a chat_id doesn't belong to a supergroup or a channel.
"""
peer = self.resolve_peer(chat_id)
if isinstance(peer, types.InputPeerChannel):
self.send(
functions.channels.EditAbout(
channel=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))
return True

View File

@ -0,0 +1,82 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance>
#
# 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 <http://www.gnu.org/licenses/>.
import os
from base64 import b64decode
from struct import unpack
from pyrogram.api import functions, types
from ...ext import BaseClient
class SetChatPhoto(BaseClient):
def set_chat_photo(self, chat_id: int or str, photo: str):
"""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.
Note:
In regular groups (non-supergroups), this method will only work if the "All Members Are Admins"
setting is off.
Args:
chat_id (``int`` | ``str``):
Unique identifier (int) or username (str) of the target chat.
photo (``str``):
New chat photo. You can pass a :class:`Photo` id or a file path to upload a new photo.
Returns:
True on success.
Raises:
:class:`Error <pyrogram.Error>`
``ValueError``: If a chat_id belongs to user.
"""
peer = self.resolve_peer(chat_id)
if os.path.exists(photo):
photo = types.InputChatUploadedPhoto(file=self.save_file(photo))
else:
s = unpack("<qq", b64decode(photo + "=" * (-len(photo) % 4), "-_"))
photo = types.InputChatPhoto(
id=types.InputPhoto(
id=s[0],
access_hash=s[1]
)
)
if isinstance(peer, types.InputPeerChat):
self.send(
functions.messages.EditChatPhoto(
chat_id=peer.chat_id,
photo=photo
)
)
elif isinstance(peer, types.InputPeerChannel):
self.send(
functions.channels.EditPhoto(
channel=peer,
photo=photo
)
)
else:
raise ValueError("The chat_id \"{}\" belongs to a user".format(chat_id))
return True

View File

@ -0,0 +1,66 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance>
#
# 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 <http://www.gnu.org/licenses/>.
from pyrogram.api import functions, types
from ...ext import BaseClient
class SetChatTitle(BaseClient):
def set_chat_title(self, chat_id: int or str, title: str):
"""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.
Note:
In regular groups (non-supergroups), this method will only work if the "All Members Are Admins"
setting is off.
Args:
chat_id (``int`` | ``str``):
Unique identifier (int) or username (str) of the target chat.
title (``str``):
New chat title, 1-255 characters.
Returns:
True on success.
Raises:
:class:`Error <pyrogram.Error>`
``ValueError``: If a chat_id belongs to user.
"""
peer = self.resolve_peer(chat_id)
if isinstance(peer, types.InputPeerChat):
self.send(
functions.messages.EditChatTitle(
chat_id=peer.chat_id,
title=title
)
)
elif isinstance(peer, types.InputPeerChannel):
self.send(
functions.channels.EditTitle(
channel=peer,
title=title
)
)
else:
raise ValueError("The chat_id \"{}\" belongs to a user".format(chat_id))
return True

View File

@ -31,7 +31,6 @@ class UnbanChatMember(BaseClient):
Args:
chat_id (``int`` | ``str``):
Unique identifier (int) or username (str) of the target chat.
For a private channel/supergroup you can use its *t.me/joinchat/* link.
user_id (``int`` | ``str``):
Unique identifier (int) or username (str) of the target user.

View File

@ -0,0 +1,54 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance>
#
# 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 <http://www.gnu.org/licenses/>.
from pyrogram.api import functions, types
from ...ext import BaseClient
class UnpinChatMessage(BaseClient):
def unpin_chat_message(self, chat_id: int or str):
"""Use this method to unpin a message in a supergroup or a channel.
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.
Args:
chat_id (``int`` | ``str``):
Unique identifier (int) or username (str) of the target chat.
Returns:
True on success.
Raises:
:class:`Error <pyrogram.Error>`
``ValueError``: If a chat_id doesn't belong to a supergroup or a channel.
"""
peer = self.resolve_peer(chat_id)
if isinstance(peer, types.InputPeerChannel):
self.send(
functions.channels.UpdatePinnedMessage(
channel=peer,
id=0
)
)
elif isinstance(peer, types.InputPeerChat):
raise ValueError("The chat_id \"{}\" belongs to a basic group".format(chat_id))
else:
raise ValueError("The chat_id \"{}\" belongs to a user".format(chat_id))
return True

View File

@ -18,16 +18,17 @@
from .delete_messages import DeleteMessages
from .edit_message_caption import EditMessageCaption
from .edit_message_media import EditMessageMedia
from .edit_message_reply_markup import EditMessageReplyMarkup
from .edit_message_text import EditMessageText
from .forward_messages import ForwardMessages
from .get_history import GetHistory
from .get_messages import GetMessages
from .send_animation import SendAnimation
from .send_audio import SendAudio
from .send_chat_action import SendChatAction
from .send_contact import SendContact
from .send_document import SendDocument
from .send_gif import SendGIF
from .send_location import SendLocation
from .send_media_group import SendMediaGroup
from .send_message import SendMessage
@ -43,6 +44,7 @@ class Messages(
DeleteMessages,
EditMessageCaption,
EditMessageReplyMarkup,
EditMessageMedia,
EditMessageText,
ForwardMessages,
GetHistory,
@ -51,7 +53,7 @@ class Messages(
SendChatAction,
SendContact,
SendDocument,
SendGIF,
SendAnimation,
SendLocation,
SendMediaGroup,
SendMessage,

View File

@ -38,7 +38,6 @@ class DeleteMessages(BaseClient):
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).
For a private channel/supergroup you can use its *t.me/joinchat/* link.
message_ids (``iterable``):
A list of Message identifiers to delete or a single message id.

View File

@ -34,7 +34,6 @@ class EditMessageCaption(BaseClient):
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).
For a private channel/supergroup you can use its *t.me/joinchat/* link.
message_id (``int``):
Message identifier in the chat specified in chat_id.

View File

@ -0,0 +1,312 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance>
#
# 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 <http://www.gnu.org/licenses/>.
import binascii
import mimetypes
import os
import struct
from pyrogram.api import functions, types
from pyrogram.api.errors import FileIdInvalid
from pyrogram.client.ext import BaseClient, utils
from pyrogram.client.types import (
InputMediaPhoto, InputMediaVideo, InputMediaAudio,
InputMediaAnimation, InputMediaDocument
)
class EditMessageMedia(BaseClient):
def edit_message_media(self,
chat_id: int or str,
message_id: int,
media,
reply_markup=None):
style = self.html if media.parse_mode.lower() == "html" else self.markdown
caption = media.caption
if isinstance(media, InputMediaPhoto):
if os.path.exists(media.media):
media = self.send(
functions.messages.UploadMedia(
peer=self.resolve_peer(chat_id),
media=types.InputMediaUploadedPhoto(
file=self.save_file(media.media)
)
)
)
media = types.InputMediaPhoto(
id=types.InputPhoto(
id=media.photo.id,
access_hash=media.photo.access_hash
)
)
elif media.media.startswith("http"):
media = types.InputMediaPhotoExternal(
url=media.media
)
else:
try:
decoded = utils.decode(media.media)
fmt = "<iiqqqqi" if len(decoded) > 24 else "<iiqq"
unpacked = struct.unpack(fmt, decoded)
except (AssertionError, binascii.Error, struct.error):
raise FileIdInvalid from None
else:
if unpacked[0] != 2:
media_type = BaseClient.MEDIA_TYPE_ID.get(unpacked[0], None)
if media_type:
raise FileIdInvalid("The file_id belongs to a {}".format(media_type))
else:
raise FileIdInvalid("Unknown media type: {}".format(unpacked[0]))
media = types.InputMediaPhoto(
id=types.InputPhoto(
id=unpacked[2],
access_hash=unpacked[3]
)
)
if isinstance(media, InputMediaVideo):
if os.path.exists(media.media):
media = self.send(
functions.messages.UploadMedia(
peer=self.resolve_peer(chat_id),
media=types.InputMediaUploadedDocument(
mime_type=mimetypes.types_map[".mp4"],
file=self.save_file(media.media),
attributes=[
types.DocumentAttributeVideo(
supports_streaming=media.supports_streaming or None,
duration=media.duration,
w=media.width,
h=media.height
),
types.DocumentAttributeFilename(os.path.basename(media.media))
]
)
)
)
media = types.InputMediaDocument(
id=types.InputDocument(
id=media.document.id,
access_hash=media.document.access_hash
)
)
elif media.media.startswith("http"):
media = types.InputMediaDocumentExternal(
url=media.media
)
else:
try:
decoded = utils.decode(media.media)
fmt = "<iiqqqqi" if len(decoded) > 24 else "<iiqq"
unpacked = struct.unpack(fmt, decoded)
except (AssertionError, binascii.Error, struct.error):
raise FileIdInvalid from None
else:
if unpacked[0] != 4:
media_type = BaseClient.MEDIA_TYPE_ID.get(unpacked[0], None)
if media_type:
raise FileIdInvalid("The file_id belongs to a {}".format(media_type))
else:
raise FileIdInvalid("Unknown media type: {}".format(unpacked[0]))
media = types.InputMediaDocument(
id=types.InputDocument(
id=unpacked[2],
access_hash=unpacked[3]
)
)
if isinstance(media, InputMediaAudio):
if os.path.exists(media.media):
media = self.send(
functions.messages.UploadMedia(
peer=self.resolve_peer(chat_id),
media=types.InputMediaUploadedDocument(
mime_type=mimetypes.types_map.get("." + media.media.split(".")[-1], "audio/mpeg"),
file=self.save_file(media.media),
attributes=[
types.DocumentAttributeAudio(
duration=media.duration,
performer=media.performer,
title=media.title
),
types.DocumentAttributeFilename(os.path.basename(media.media))
]
)
)
)
media = types.InputMediaDocument(
id=types.InputDocument(
id=media.document.id,
access_hash=media.document.access_hash
)
)
elif media.media.startswith("http"):
media = types.InputMediaDocumentExternal(
url=media.media
)
else:
try:
decoded = utils.decode(media.media)
fmt = "<iiqqqqi" if len(decoded) > 24 else "<iiqq"
unpacked = struct.unpack(fmt, decoded)
except (AssertionError, binascii.Error, struct.error):
raise FileIdInvalid from None
else:
if unpacked[0] != 9:
media_type = BaseClient.MEDIA_TYPE_ID.get(unpacked[0], None)
if media_type:
raise FileIdInvalid("The file_id belongs to a {}".format(media_type))
else:
raise FileIdInvalid("Unknown media type: {}".format(unpacked[0]))
media = types.InputMediaDocument(
id=types.InputDocument(
id=unpacked[2],
access_hash=unpacked[3]
)
)
if isinstance(media, InputMediaAnimation):
if os.path.exists(media.media):
media = self.send(
functions.messages.UploadMedia(
peer=self.resolve_peer(chat_id),
media=types.InputMediaUploadedDocument(
mime_type=mimetypes.types_map[".mp4"],
file=self.save_file(media.media),
attributes=[
types.DocumentAttributeVideo(
supports_streaming=True,
duration=media.duration,
w=media.width,
h=media.height
),
types.DocumentAttributeFilename(os.path.basename(media.media)),
types.DocumentAttributeAnimated()
]
)
)
)
media = types.InputMediaDocument(
id=types.InputDocument(
id=media.document.id,
access_hash=media.document.access_hash
)
)
elif media.media.startswith("http"):
media = types.InputMediaDocumentExternal(
url=media.media
)
else:
try:
decoded = utils.decode(media.media)
fmt = "<iiqqqqi" if len(decoded) > 24 else "<iiqq"
unpacked = struct.unpack(fmt, decoded)
except (AssertionError, binascii.Error, struct.error):
raise FileIdInvalid from None
else:
if unpacked[0] != 10:
media_type = BaseClient.MEDIA_TYPE_ID.get(unpacked[0], None)
if media_type:
raise FileIdInvalid("The file_id belongs to a {}".format(media_type))
else:
raise FileIdInvalid("Unknown media type: {}".format(unpacked[0]))
media = types.InputMediaDocument(
id=types.InputDocument(
id=unpacked[2],
access_hash=unpacked[3]
)
)
if isinstance(media, InputMediaDocument):
if os.path.exists(media.media):
media = self.send(
functions.messages.UploadMedia(
peer=self.resolve_peer(chat_id),
media=types.InputMediaUploadedDocument(
mime_type=mimetypes.types_map.get("." + media.media.split(".")[-1], "text/plain"),
file=self.save_file(media.media),
attributes=[
types.DocumentAttributeFilename(os.path.basename(media.media))
]
)
)
)
media = types.InputMediaDocument(
id=types.InputDocument(
id=media.document.id,
access_hash=media.document.access_hash
)
)
elif media.media.startswith("http"):
media = types.InputMediaDocumentExternal(
url=media.media
)
else:
try:
decoded = utils.decode(media.media)
fmt = "<iiqqqqi" if len(decoded) > 24 else "<iiqq"
unpacked = struct.unpack(fmt, decoded)
except (AssertionError, binascii.Error, struct.error):
raise FileIdInvalid from None
else:
if unpacked[0] not in (5, 10):
media_type = BaseClient.MEDIA_TYPE_ID.get(unpacked[0], None)
if media_type:
raise FileIdInvalid("The file_id belongs to a {}".format(media_type))
else:
raise FileIdInvalid("Unknown media type: {}".format(unpacked[0]))
media = types.InputMediaDocument(
id=types.InputDocument(
id=unpacked[2],
access_hash=unpacked[3]
)
)
r = self.send(
functions.messages.EditMessage(
peer=self.resolve_peer(chat_id),
id=message_id,
reply_markup=reply_markup.write() if reply_markup else None,
media=media,
**style.parse(caption)
)
)
for i in r.updates:
if isinstance(i, (types.UpdateEditMessage, types.UpdateEditChannelMessage)):
return utils.parse_messages(
self, i.message,
{i.id: i for i in r.users},
{i.id: i for i in r.chats}
)

View File

@ -32,7 +32,6 @@ class EditMessageReplyMarkup(BaseClient):
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).
For a private channel/supergroup you can use its *t.me/joinchat/* link.
message_id (``int``):
Message identifier in the chat specified in chat_id.

View File

@ -35,7 +35,6 @@ class EditMessageText(BaseClient):
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).
For a private channel/supergroup you can use its *t.me/joinchat/* link.
message_id (``int``):
Message identifier in the chat specified in chat_id.

View File

@ -33,13 +33,11 @@ class ForwardMessages(BaseClient):
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).
For a private channel/supergroup you can use its *t.me/joinchat/* link.
from_chat_id (``int`` | ``str``):
Unique identifier (int) or username (str) of the source chat where the original message was sent.
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).
For a private channel/supergroup you can use its *t.me/joinchat/* link.
message_ids (``iterable``):
A list of Message identifiers in the chat specified in *from_chat_id* or a single message id.

View File

@ -37,7 +37,6 @@ class GetHistory(BaseClient):
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).
For a private channel/supergroup you can use its *t.me/joinchat/* link.
offset (``int``, *optional*)
Sequential number of the first message to be returned.

Some files were not shown because too many files have changed in this diff Show More