2
0
mirror of https://github.com/pyrogram/pyrogram synced 2025-08-28 21:07:59 +00:00

Merge branch 'develop' into session_storage

# Conflicts:
#	pyrogram/client/client.py
#	pyrogram/client/ext/base_client.py
#	pyrogram/client/ext/syncer.py
#	pyrogram/client/style/html.py
#	pyrogram/client/style/markdown.py
This commit is contained in:
bakatrouble 2019-04-14 21:49:45 +03:00
commit 952f0627f1
231 changed files with 7200 additions and 1929 deletions

View File

@ -32,7 +32,7 @@ Features
- **Fast**: Crypto parts are boosted up by TgCrypto_, a high-performance library written in pure C. - **Fast**: Crypto parts are boosted up by TgCrypto_, a high-performance library written in pure C.
- **Documented**: Pyrogram API methods, types and public interfaces are well documented. - **Documented**: Pyrogram API methods, types and public interfaces are well documented.
- **Type-hinted**: Exposed Pyrogram types and method parameters are all type-hinted. - **Type-hinted**: Exposed Pyrogram types and method parameters are all type-hinted.
- **Updated**, to the latest Telegram API version, currently Layer 91 on top of `MTProto 2.0`_. - **Updated**, to the latest Telegram API version, currently Layer 97 on top of `MTProto 2.0`_.
- **Pluggable**: The Smart Plugin system allows to write components with minimal boilerplate code. - **Pluggable**: The Smart Plugin system allows to write components with minimal boilerplate code.
- **Comprehensive**: Execute any advanced action an official client is able to do, and even more. - **Comprehensive**: Execute any advanced action an official client is able to do, and even more.
@ -107,7 +107,7 @@ Copyright & License
</a> </a>
<br> <br>
<a href="compiler/api/source/main_api.tl"> <a href="compiler/api/source/main_api.tl">
<img src="https://img.shields.io/badge/schema-layer%2091-eda738.svg?longCache=true&colorA=262b30" <img src="https://img.shields.io/badge/schema-layer%2097-eda738.svg?longCache=true&colorA=262b30"
alt="Schema Layer"> alt="Schema Layer">
</a> </a>
<a href="https://github.com/pyrogram/tgcrypto"> <a href="https://github.com/pyrogram/tgcrypto">
@ -122,7 +122,7 @@ Copyright & License
.. |description| replace:: **Telegram MTProto API Framework for Python** .. |description| replace:: **Telegram MTProto API Framework for Python**
.. |schema| image:: https://img.shields.io/badge/schema-layer%2091-eda738.svg?longCache=true&colorA=262b30 .. |schema| image:: https://img.shields.io/badge/schema-layer%2097-eda738.svg?longCache=true&colorA=262b30
:target: compiler/api/source/main_api.tl :target: compiler/api/source/main_api.tl
:alt: Schema Layer :alt: Schema Layer

View File

@ -171,8 +171,8 @@ def start():
shutil.rmtree("{}/functions".format(DESTINATION), ignore_errors=True) shutil.rmtree("{}/functions".format(DESTINATION), ignore_errors=True)
with open("{}/source/auth_key.tl".format(HOME), encoding="utf-8") as auth, \ 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/sys_msgs.tl".format(HOME), encoding="utf-8") as system, \
open("{}/source/main_api.tl".format(HOME), encoding="utf-8") as api: open("{}/source/main_api.tl".format(HOME), encoding="utf-8") as api:
schema = (auth.read() + system.read() + api.read()).splitlines() schema = (auth.read() + system.read() + api.read()).splitlines()
with open("{}/template/mtproto.txt".format(HOME), encoding="utf-8") as f: with open("{}/template/mtproto.txt".format(HOME), encoding="utf-8") as f:
@ -287,9 +287,11 @@ def start():
sorted_args = sort_args(c.args) sorted_args = sort_args(c.args)
arguments = ", " + ", ".join( arguments = (
[get_argument_type(i) for i in sorted_args if i != ("flags", "#")] ", "
) if c.args else "" + ("*, " if c.args else "")
+ (", ".join([get_argument_type(i) for i in sorted_args if i != ("flags", "#")]) if c.args else "")
)
fields = "\n ".join( fields = "\n ".join(
["self.{0} = {0} # {1}".format(i[0], i[1]) for i in c.args if i != ("flags", "#")] ["self.{0} = {0} # {1}".format(i[0], i[1]) for i in c.args if i != ("flags", "#")]
@ -333,7 +335,7 @@ def start():
docstring_args = "Attributes:\n ID: ``{}``\n\n ".format(c.id) + docstring_args docstring_args = "Attributes:\n ID: ``{}``\n\n ".format(c.id) + docstring_args
if c.section == "functions": if c.section == "functions":
docstring_args += "\n\n Raises:\n :obj:`Error <pyrogram.Error>`" docstring_args += "\n\n Raises:\n :obj:`RPCError <pyrogram.RPCError>`"
docstring_args += "\n\n Returns:\n " + get_docstring_arg_type(c.return_type) docstring_args += "\n\n Returns:\n " + get_docstring_arg_type(c.return_type)
else: else:
references = get_references(".".join(filter(None, [c.namespace, c.name]))) references = get_references(".".join(filter(None, [c.namespace, c.name])))
@ -456,7 +458,11 @@ def start():
fields=fields, fields=fields,
read_types=read_types, read_types=read_types,
write_types=write_types, write_types=write_types,
return_arguments=", ".join([i[0] for i in sorted_args if i != ("flags", "#")]) return_arguments=", ".join(
["{0}={0}".format(i[0]) for i in sorted_args if i != ("flags", "#")]
),
slots=", ".join(['"{}"'.format(i[0]) for i in sorted_args if i != ("flags", "#")]),
qualname="{}{}".format("{}.".format(c.namespace) if c.namespace else "", c.name)
) )
) )

View File

@ -83,7 +83,7 @@ fileLocationUnavailable#7c596b46 volume_id:long local_id:int secret:long = FileL
fileLocation#91d11eb dc_id:int volume_id:long local_id:int secret:long file_reference:bytes = FileLocation; fileLocation#91d11eb dc_id:int volume_id:long local_id:int secret:long file_reference:bytes = FileLocation;
userEmpty#200250ba id:int = User; userEmpty#200250ba id:int = User;
user#2e13f4c3 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true id:int access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?string bot_inline_placeholder:flags.19?string lang_code:flags.22?string = User; user#2e13f4c3 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true support:flags.23?true id:int access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?string bot_inline_placeholder:flags.19?string lang_code:flags.22?string = User;
userProfilePhotoEmpty#4f11bae1 = UserProfilePhoto; userProfilePhotoEmpty#4f11bae1 = UserProfilePhoto;
userProfilePhoto#d559d8c8 photo_id:long photo_small:FileLocation photo_big:FileLocation = UserProfilePhoto; userProfilePhoto#d559d8c8 photo_id:long photo_small:FileLocation photo_big:FileLocation = UserProfilePhoto;
@ -96,12 +96,12 @@ userStatusLastWeek#7bf09fc = UserStatus;
userStatusLastMonth#77ebc742 = UserStatus; userStatusLastMonth#77ebc742 = UserStatus;
chatEmpty#9ba2d800 id:int = Chat; chatEmpty#9ba2d800 id:int = Chat;
chat#d91cdd54 flags:# creator:flags.0?true kicked:flags.1?true left:flags.2?true admins_enabled:flags.3?true admin:flags.4?true deactivated:flags.5?true id:int title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel = Chat; chat#3bda1bde flags:# creator:flags.0?true kicked:flags.1?true left:flags.2?true deactivated:flags.5?true id:int title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel admin_rights:flags.14?ChatAdminRights default_banned_rights:flags.18?ChatBannedRights = Chat;
chatForbidden#7328bdb id:int title:string = Chat; chatForbidden#7328bdb id:int title:string = Chat;
channel#c88974ac flags:# creator:flags.0?true left:flags.2?true editor:flags.3?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true democracy:flags.10?true signatures:flags.11?true min:flags.12?true id:int access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?string admin_rights:flags.14?ChannelAdminRights banned_rights:flags.15?ChannelBannedRights participants_count:flags.17?int = Chat; channel#4df30834 flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true id:int access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?string admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int = Chat;
channelForbidden#289da732 flags:# broadcast:flags.5?true megagroup:flags.8?true id:int access_hash:long title:string until_date:flags.16?int = Chat; channelForbidden#289da732 flags:# broadcast:flags.5?true megagroup:flags.8?true id:int access_hash:long title:string until_date:flags.16?int = Chat;
chatFull#edd2a791 flags:# id:int participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int = ChatFull; chatFull#22a235da flags:# can_set_username:flags.7?true id:int about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int = ChatFull;
channelFull#1c87a71a flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_view_stats:flags.12?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int = ChatFull; channelFull#1c87a71a flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_view_stats:flags.12?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int = ChatFull;
chatParticipant#c8d7493e user_id:int inviter_id:int date:int = ChatParticipant; chatParticipant#c8d7493e user_id:int inviter_id:int date:int = ChatParticipant;
@ -163,6 +163,7 @@ photo#9c477dd8 flags:# has_stickers:flags.0?true id:long access_hash:long file_r
photoSizeEmpty#e17e23c type:string = PhotoSize; photoSizeEmpty#e17e23c type:string = PhotoSize;
photoSize#77bfb61b type:string location:FileLocation w:int h:int size:int = PhotoSize; photoSize#77bfb61b type:string location:FileLocation w:int h:int size:int = PhotoSize;
photoCachedSize#e9a734fa type:string location:FileLocation w:int h:int bytes:bytes = PhotoSize; photoCachedSize#e9a734fa type:string location:FileLocation w:int h:int bytes:bytes = PhotoSize;
photoStrippedSize#e0b0bc2e type:string bytes:bytes = PhotoSize;
geoPointEmpty#1117dd5f = GeoPoint; geoPointEmpty#1117dd5f = GeoPoint;
geoPoint#296f104 long:double lat:double access_hash:long = GeoPoint; geoPoint#296f104 long:double lat:double access_hash:long = GeoPoint;
@ -186,8 +187,7 @@ peerNotifySettings#af509d20 flags:# show_previews:flags.0?Bool silent:flags.1?Bo
peerSettings#818426cd flags:# report_spam:flags.0?true = PeerSettings; peerSettings#818426cd flags:# report_spam:flags.0?true = PeerSettings;
wallPaper#ccb03657 id:int title:string sizes:Vector<PhotoSize> color:int = WallPaper; wallPaper#a437c3ed id:long flags:# creator:flags.0?true default:flags.1?true pattern:flags.3?true dark:flags.4?true access_hash:long slug:string document:Document settings:flags.2?WallPaperSettings = WallPaper;
wallPaperSolid#63117f24 id:int title:string bg_color:int color:int = WallPaper;
inputReportReasonSpam#58dbcab8 = ReportReason; inputReportReasonSpam#58dbcab8 = ReportReason;
inputReportReasonViolence#1e22c78d = ReportReason; inputReportReasonViolence#1e22c78d = ReportReason;
@ -221,7 +221,7 @@ messages.dialogsSlice#71e094f3 count:int dialogs:Vector<Dialog> messages:Vector<
messages.dialogsNotModified#f0e3e596 count:int = messages.Dialogs; messages.dialogsNotModified#f0e3e596 count:int = messages.Dialogs;
messages.messages#8c718e87 messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.Messages; 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; messages.messagesSlice#a6c47aaa flags:# inexact:flags.1?true count:int messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.Messages;
messages.channelMessages#99262e37 flags:# inexact:flags.1?true pts:int count:int messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.Messages; messages.channelMessages#99262e37 flags:# inexact:flags.1?true pts:int count:int messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.Messages;
messages.messagesNotModified#74535f21 count:int = messages.Messages; messages.messagesNotModified#74535f21 count:int = messages.Messages;
@ -281,7 +281,6 @@ updateNewChannelMessage#62ba04d9 message:Message pts:int pts_count:int = Update;
updateReadChannelInbox#4214f37f channel_id:int max_id:int = Update; updateReadChannelInbox#4214f37f channel_id:int max_id:int = Update;
updateDeleteChannelMessages#c37521c9 channel_id:int messages:Vector<int> pts:int pts_count:int = Update; updateDeleteChannelMessages#c37521c9 channel_id:int messages:Vector<int> pts:int pts_count:int = Update;
updateChannelMessageViews#98a12b4b channel_id:int id:int views:int = Update; updateChannelMessageViews#98a12b4b channel_id:int id:int views:int = Update;
updateChatAdmins#6e947941 chat_id:int enabled:Bool version:int = Update;
updateChatParticipantAdmin#b6901959 chat_id:int user_id:int is_admin:Bool version:int = Update; updateChatParticipantAdmin#b6901959 chat_id:int user_id:int is_admin:Bool version:int = Update;
updateNewStickerSet#688a30aa stickerset:messages.StickerSet = Update; updateNewStickerSet#688a30aa stickerset:messages.StickerSet = Update;
updateStickerSetsOrder#bb2d201 flags:# masks:flags.0?true order:Vector<long> = Update; updateStickerSetsOrder#bb2d201 flags:# masks:flags.0?true order:Vector<long> = Update;
@ -316,8 +315,9 @@ updateContactsReset#7084a7be = Update;
updateChannelAvailableMessages#70db6837 channel_id:int available_min_id:int = Update; updateChannelAvailableMessages#70db6837 channel_id:int available_min_id:int = Update;
updateDialogUnreadMark#e16459c3 flags:# unread:flags.0?true peer:DialogPeer = Update; updateDialogUnreadMark#e16459c3 flags:# unread:flags.0?true peer:DialogPeer = Update;
updateUserPinnedMessage#4c43da18 user_id:int id:int = Update; updateUserPinnedMessage#4c43da18 user_id:int id:int = Update;
updateChatPinnedMessage#22893b26 chat_id:int id:int = Update; updateChatPinnedMessage#e10db349 chat_id:int id:int version:int = Update;
updateMessagePoll#aca1657b flags:# poll_id:long poll:flags.0?Poll results:PollResults = Update; updateMessagePoll#aca1657b flags:# poll_id:long poll:flags.0?Poll results:PollResults = Update;
updateChatDefaultBannedRights#54c01850 peer:Peer default_banned_rights:ChatBannedRights version:int = Update;
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State; updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
@ -382,7 +382,7 @@ inputDocumentEmpty#72f0eaae = InputDocument;
inputDocument#1abfb575 id:long access_hash:long file_reference:bytes = InputDocument; inputDocument#1abfb575 id:long access_hash:long file_reference:bytes = InputDocument;
documentEmpty#36f8c871 id:long = Document; documentEmpty#36f8c871 id:long = Document;
document#59534e4c id:long access_hash:long file_reference:bytes date:int mime_type:string size:int thumb:PhotoSize dc_id:int attributes:Vector<DocumentAttribute> = Document; document#9ba29cc1 flags:# id:long access_hash:long file_reference:bytes date:int mime_type:string size:int thumbs:flags.0?Vector<PhotoSize> dc_id:int attributes:Vector<DocumentAttribute> = Document;
help.support#17c6b5f6 phone_number:string user:User = help.Support; help.support#17c6b5f6 phone_number:string user:User = help.Support;
@ -411,11 +411,15 @@ inputPrivacyKeyStatusTimestamp#4f96cb18 = InputPrivacyKey;
inputPrivacyKeyChatInvite#bdfb0426 = InputPrivacyKey; inputPrivacyKeyChatInvite#bdfb0426 = InputPrivacyKey;
inputPrivacyKeyPhoneCall#fabadc5f = InputPrivacyKey; inputPrivacyKeyPhoneCall#fabadc5f = InputPrivacyKey;
inputPrivacyKeyPhoneP2P#db9e70d2 = InputPrivacyKey; inputPrivacyKeyPhoneP2P#db9e70d2 = InputPrivacyKey;
inputPrivacyKeyForwards#a4dd4c08 = InputPrivacyKey;
inputPrivacyKeyProfilePhoto#5719bacc = InputPrivacyKey;
privacyKeyStatusTimestamp#bc2eab30 = PrivacyKey; privacyKeyStatusTimestamp#bc2eab30 = PrivacyKey;
privacyKeyChatInvite#500e6dfa = PrivacyKey; privacyKeyChatInvite#500e6dfa = PrivacyKey;
privacyKeyPhoneCall#3d662b7b = PrivacyKey; privacyKeyPhoneCall#3d662b7b = PrivacyKey;
privacyKeyPhoneP2P#39491cc8 = PrivacyKey; privacyKeyPhoneP2P#39491cc8 = PrivacyKey;
privacyKeyForwards#69ec56a3 = PrivacyKey;
privacyKeyProfilePhoto#96151fed = PrivacyKey;
inputPrivacyValueAllowContacts#d09e07b = InputPrivacyRule; inputPrivacyValueAllowContacts#d09e07b = InputPrivacyRule;
inputPrivacyValueAllowAll#184b35ce = InputPrivacyRule; inputPrivacyValueAllowAll#184b35ce = InputPrivacyRule;
@ -487,7 +491,7 @@ inputStickerSetEmpty#ffb62b95 = InputStickerSet;
inputStickerSetID#9de7a269 id:long access_hash:long = InputStickerSet; inputStickerSetID#9de7a269 id:long access_hash:long = InputStickerSet;
inputStickerSetShortName#861cc8a0 short_name:string = InputStickerSet; inputStickerSetShortName#861cc8a0 short_name:string = InputStickerSet;
stickerSet#5585a139 flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string count:int hash:int = StickerSet; stickerSet#6a90bcb7 flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string thumb:flags.4?PhotoSize count:int hash:int = StickerSet;
messages.stickerSet#b60a24a6 set:StickerSet packs:Vector<StickerPack> documents:Vector<Document> = messages.StickerSet; messages.stickerSet#b60a24a6 set:StickerSet packs:Vector<StickerPack> documents:Vector<Document> = messages.StickerSet;
@ -544,8 +548,8 @@ channelMessagesFilter#cd77d957 flags:# exclude_new_messages:flags.1?true ranges:
channelParticipant#15ebac1d user_id:int date:int = ChannelParticipant; channelParticipant#15ebac1d user_id:int date:int = ChannelParticipant;
channelParticipantSelf#a3289a6d user_id:int inviter_id:int date:int = ChannelParticipant; channelParticipantSelf#a3289a6d user_id:int inviter_id:int date:int = ChannelParticipant;
channelParticipantCreator#e3e2e1f9 user_id:int = ChannelParticipant; channelParticipantCreator#e3e2e1f9 user_id:int = ChannelParticipant;
channelParticipantAdmin#a82fa898 flags:# can_edit:flags.0?true user_id:int inviter_id:int promoted_by:int date:int admin_rights:ChannelAdminRights = ChannelParticipant; channelParticipantAdmin#5daa6e23 flags:# can_edit:flags.0?true self:flags.1?true user_id:int inviter_id:flags.1?int promoted_by:int date:int admin_rights:ChatAdminRights = ChannelParticipant;
channelParticipantBanned#222c1886 flags:# left:flags.0?true user_id:int kicked_by:int date:int banned_rights:ChannelBannedRights = ChannelParticipant; channelParticipantBanned#1c0facaf flags:# left:flags.0?true user_id:int kicked_by:int date:int banned_rights:ChatBannedRights = ChannelParticipant;
channelParticipantsRecent#de3f3c79 = ChannelParticipantsFilter; channelParticipantsRecent#de3f3c79 = ChannelParticipantsFilter;
channelParticipantsAdmins#b4608969 = ChannelParticipantsFilter; channelParticipantsAdmins#b4608969 = ChannelParticipantsFilter;
@ -553,6 +557,7 @@ channelParticipantsKicked#a3b54985 q:string = ChannelParticipantsFilter;
channelParticipantsBots#b0d1865b = ChannelParticipantsFilter; channelParticipantsBots#b0d1865b = ChannelParticipantsFilter;
channelParticipantsBanned#1427a5e1 q:string = ChannelParticipantsFilter; channelParticipantsBanned#1427a5e1 q:string = ChannelParticipantsFilter;
channelParticipantsSearch#656ac4b q:string = ChannelParticipantsFilter; channelParticipantsSearch#656ac4b q:string = ChannelParticipantsFilter;
channelParticipantsContacts#bb6ae88d q:string = ChannelParticipantsFilter;
channels.channelParticipants#f56ee2a8 count:int participants:Vector<ChannelParticipant> users:Vector<User> = channels.ChannelParticipants; channels.channelParticipants#f56ee2a8 count:int participants:Vector<ChannelParticipant> users:Vector<User> = channels.ChannelParticipants;
channels.channelParticipantsNotModified#f0173fe9 = channels.ChannelParticipants; channels.channelParticipantsNotModified#f0173fe9 = channels.ChannelParticipants;
@ -594,7 +599,7 @@ messages.botResults#947ca848 flags:# gallery:flags.0?true query_id:long next_off
exportedMessageLink#5dab1af4 link:string html:string = ExportedMessageLink; exportedMessageLink#5dab1af4 link:string html:string = ExportedMessageLink;
messageFwdHeader#559ebe6d flags:# from_id:flags.0?int date:int channel_id:flags.1?int channel_post:flags.2?int post_author:flags.3?string saved_from_peer:flags.4?Peer saved_from_msg_id:flags.4?int = MessageFwdHeader; messageFwdHeader#ec338270 flags:# from_id:flags.0?int from_name:flags.5?string date:int channel_id:flags.1?int channel_post:flags.2?int post_author:flags.3?string saved_from_peer:flags.4?Peer saved_from_msg_id:flags.4?int = MessageFwdHeader;
auth.codeTypeSms#72a3158c = auth.CodeType; auth.codeTypeSms#72a3158c = auth.CodeType;
auth.codeTypeCall#741cd3e3 = auth.CodeType; auth.codeTypeCall#741cd3e3 = auth.CodeType;
@ -789,10 +794,6 @@ langPackDifference#f385c1f6 lang_code:string from_version:int version:int string
langPackLanguage#eeca5ce3 flags:# official:flags.0?true rtl:flags.2?true beta:flags.3?true name:string native_name:string lang_code:string base_lang_code:flags.1?string plural_code:string strings_count:int translated_count:int translations_url:string = LangPackLanguage; langPackLanguage#eeca5ce3 flags:# official:flags.0?true rtl:flags.2?true beta:flags.3?true name:string native_name:string lang_code:string base_lang_code:flags.1?string plural_code:string strings_count:int translated_count:int translations_url:string = LangPackLanguage;
channelAdminRights#5d7ceba5 flags:# change_info:flags.0?true post_messages:flags.1?true edit_messages:flags.2?true delete_messages:flags.3?true ban_users:flags.4?true invite_users:flags.5?true invite_link:flags.6?true pin_messages:flags.7?true add_admins:flags.9?true manage_call:flags.10?true = ChannelAdminRights;
channelBannedRights#58cf4249 flags:# view_messages:flags.0?true send_messages:flags.1?true send_media:flags.2?true send_stickers:flags.3?true send_gifs:flags.4?true send_games:flags.5?true send_inline:flags.6?true embed_links:flags.7?true until_date:int = ChannelBannedRights;
channelAdminLogEventActionChangeTitle#e6dfb825 prev_value:string new_value:string = ChannelAdminLogEventAction; channelAdminLogEventActionChangeTitle#e6dfb825 prev_value:string new_value:string = ChannelAdminLogEventAction;
channelAdminLogEventActionChangeAbout#55188a2e prev_value:string new_value:string = ChannelAdminLogEventAction; channelAdminLogEventActionChangeAbout#55188a2e prev_value:string new_value:string = ChannelAdminLogEventAction;
channelAdminLogEventActionChangeUsername#6a4afc38 prev_value:string new_value:string = ChannelAdminLogEventAction; channelAdminLogEventActionChangeUsername#6a4afc38 prev_value:string new_value:string = ChannelAdminLogEventAction;
@ -809,6 +810,8 @@ channelAdminLogEventActionParticipantToggleBan#e6d83d7e prev_participant:Channel
channelAdminLogEventActionParticipantToggleAdmin#d5676710 prev_participant:ChannelParticipant new_participant:ChannelParticipant = ChannelAdminLogEventAction; channelAdminLogEventActionParticipantToggleAdmin#d5676710 prev_participant:ChannelParticipant new_participant:ChannelParticipant = ChannelAdminLogEventAction;
channelAdminLogEventActionChangeStickerSet#b1c3caa7 prev_stickerset:InputStickerSet new_stickerset:InputStickerSet = ChannelAdminLogEventAction; channelAdminLogEventActionChangeStickerSet#b1c3caa7 prev_stickerset:InputStickerSet new_stickerset:InputStickerSet = ChannelAdminLogEventAction;
channelAdminLogEventActionTogglePreHistoryHidden#5f5c95f1 new_value:Bool = ChannelAdminLogEventAction; channelAdminLogEventActionTogglePreHistoryHidden#5f5c95f1 new_value:Bool = ChannelAdminLogEventAction;
channelAdminLogEventActionDefaultBannedRights#2df5fc0a prev_banned_rights:ChatBannedRights new_banned_rights:ChatBannedRights = ChannelAdminLogEventAction;
channelAdminLogEventActionStopPoll#8f079643 message:Message = ChannelAdminLogEventAction;
channelAdminLogEvent#3b5a3e40 id:long date:int user_id:int action:ChannelAdminLogEventAction = ChannelAdminLogEvent; channelAdminLogEvent#3b5a3e40 id:long date:int user_id:int action:ChannelAdminLogEventAction = ChannelAdminLogEvent;
@ -972,6 +975,31 @@ chatOnlines#f041e250 onlines:int = ChatOnlines;
statsURL#47a971e0 url:string = StatsURL; statsURL#47a971e0 url:string = StatsURL;
chatAdminRights#5fb224d5 flags:# change_info:flags.0?true post_messages:flags.1?true edit_messages:flags.2?true delete_messages:flags.3?true ban_users:flags.4?true invite_users:flags.5?true pin_messages:flags.7?true add_admins:flags.9?true = ChatAdminRights;
chatBannedRights#9f120418 flags:# view_messages:flags.0?true send_messages:flags.1?true send_media:flags.2?true send_stickers:flags.3?true send_gifs:flags.4?true send_games:flags.5?true send_inline:flags.6?true embed_links:flags.7?true send_polls:flags.8?true change_info:flags.10?true invite_users:flags.15?true pin_messages:flags.17?true until_date:int = ChatBannedRights;
inputWallPaper#e630b979 id:long access_hash:long = InputWallPaper;
inputWallPaperSlug#72091c80 slug:string = InputWallPaper;
account.wallPapersNotModified#1c199183 = account.WallPapers;
account.wallPapers#702b65a9 hash:int wallpapers:Vector<WallPaper> = account.WallPapers;
codeSettings#302f59f3 flags:# allow_flashcall:flags.0?true current_number:flags.1?true app_hash_persistent:flags.2?true app_hash:flags.3?string = CodeSettings;
wallPaperSettings#a12f40b8 flags:# blur:flags.1?true motion:flags.2?true background_color:flags.0?int intensity:flags.3?int = WallPaperSettings;
autoDownloadSettings#d246fd47 flags:# disabled:flags.0?true video_preload_large:flags.1?true audio_preload_next:flags.2?true phonecalls_less_data:flags.3?true photo_size_max:int video_size_max:int file_size_max:int = AutoDownloadSettings;
account.autoDownloadSettings#63cacf26 low:AutoDownloadSettings medium:AutoDownloadSettings high:AutoDownloadSettings = account.AutoDownloadSettings;
emojiKeyword#d5b3b9f9 keyword:string emoticons:Vector<string> = EmojiKeyword;
emojiKeywordDeleted#236df622 keyword:string emoticons:Vector<string> = EmojiKeyword;
emojiKeywordsDifference#5cc761bd lang_code:string from_version:int version:int keywords:Vector<EmojiKeyword> = EmojiKeywordsDifference;
emojiURL#a575739d url:string = EmojiURL;
---functions--- ---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X; invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
@ -982,7 +1010,7 @@ invokeWithoutUpdates#bf9459b7 {X:Type} query:!X = X;
invokeWithMessagesRange#365275f2 {X:Type} range:MessageRange query:!X = X; invokeWithMessagesRange#365275f2 {X:Type} range:MessageRange query:!X = X;
invokeWithTakeout#aca9fd2e {X:Type} takeout_id:long query:!X = X; invokeWithTakeout#aca9fd2e {X:Type} takeout_id:long query:!X = X;
auth.sendCode#86aef0ec flags:# allow_flashcall:flags.0?true phone_number:string current_number:flags.0?Bool api_id:int api_hash:string = auth.SentCode; auth.sendCode#a677244f phone_number:string api_id:int api_hash:string settings:CodeSettings = auth.SentCode;
auth.signUp#1b067634 phone_number:string phone_code_hash:string phone_code:string first_name:string last_name:string = auth.Authorization; auth.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.signIn#bcd51581 phone_number:string phone_code_hash:string phone_code:string = auth.Authorization;
auth.logOut#5717da40 = Bool; auth.logOut#5717da40 = Bool;
@ -1005,7 +1033,7 @@ account.getNotifySettings#12b3ad31 peer:InputNotifyPeer = PeerNotifySettings;
account.resetNotifySettings#db7e1747 = Bool; account.resetNotifySettings#db7e1747 = Bool;
account.updateProfile#78515775 flags:# first_name:flags.0?string last_name:flags.1?string about:flags.2?string = User; account.updateProfile#78515775 flags:# first_name:flags.0?string last_name:flags.1?string about:flags.2?string = User;
account.updateStatus#6628562c offline:Bool = Bool; account.updateStatus#6628562c offline:Bool = Bool;
account.getWallPapers#c04cfac2 = Vector<WallPaper>; account.getWallPapers#aabb1763 hash:int = account.WallPapers;
account.reportPeer#ae189d5f peer:InputPeer reason:ReportReason = Bool; account.reportPeer#ae189d5f peer:InputPeer reason:ReportReason = Bool;
account.checkUsername#2714d86c username:string = Bool; account.checkUsername#2714d86c username:string = Bool;
account.updateUsername#3e0bdd7c username:string = User; account.updateUsername#3e0bdd7c username:string = User;
@ -1014,7 +1042,7 @@ account.setPrivacy#c9f81ce8 key:InputPrivacyKey rules:Vector<InputPrivacyRule> =
account.deleteAccount#418d4e0b reason:string = Bool; account.deleteAccount#418d4e0b reason:string = Bool;
account.getAccountTTL#8fc711d = AccountDaysTTL; account.getAccountTTL#8fc711d = AccountDaysTTL;
account.setAccountTTL#2442485e ttl:AccountDaysTTL = Bool; account.setAccountTTL#2442485e ttl:AccountDaysTTL = Bool;
account.sendChangePhoneCode#8e57deb flags:# allow_flashcall:flags.0?true phone_number:string current_number:flags.0?Bool = auth.SentCode; account.sendChangePhoneCode#82574ae5 phone_number:string settings:CodeSettings = auth.SentCode;
account.changePhone#70c32edb phone_number:string phone_code_hash:string phone_code:string = User; account.changePhone#70c32edb phone_number:string phone_code_hash:string phone_code:string = User;
account.updateDeviceLocked#38df3532 period:int = Bool; account.updateDeviceLocked#38df3532 period:int = Bool;
account.getAuthorizations#e320c158 = account.Authorizations; account.getAuthorizations#e320c158 = account.Authorizations;
@ -1022,7 +1050,7 @@ account.resetAuthorization#df77f3bc hash:long = Bool;
account.getPassword#548a30f5 = account.Password; account.getPassword#548a30f5 = account.Password;
account.getPasswordSettings#9cd4eaf9 password:InputCheckPasswordSRP = account.PasswordSettings; account.getPasswordSettings#9cd4eaf9 password:InputCheckPasswordSRP = account.PasswordSettings;
account.updatePasswordSettings#a59b102f password:InputCheckPasswordSRP new_settings:account.PasswordInputSettings = Bool; account.updatePasswordSettings#a59b102f password:InputCheckPasswordSRP new_settings:account.PasswordInputSettings = Bool;
account.sendConfirmPhoneCode#1516d7bd flags:# allow_flashcall:flags.0?true hash:string current_number:flags.0?Bool = auth.SentCode; account.sendConfirmPhoneCode#1b3faa88 hash:string settings:CodeSettings = auth.SentCode;
account.confirmPhone#5f2178c3 phone_code_hash:string phone_code:string = Bool; account.confirmPhone#5f2178c3 phone_code_hash:string phone_code:string = Bool;
account.getTmpPassword#449e0b51 password:InputCheckPasswordSRP period:int = account.TmpPassword; account.getTmpPassword#449e0b51 password:InputCheckPasswordSRP period:int = account.TmpPassword;
account.getWebAuthorizations#182e6d6f = account.WebAuthorizations; account.getWebAuthorizations#182e6d6f = account.WebAuthorizations;
@ -1034,7 +1062,7 @@ account.saveSecureValue#899fe31d value:InputSecureValue secure_secret_id:long =
account.deleteSecureValue#b880bc4b types:Vector<SecureValueType> = Bool; account.deleteSecureValue#b880bc4b types:Vector<SecureValueType> = Bool;
account.getAuthorizationForm#b86ba8e1 bot_id:int scope:string public_key:string = account.AuthorizationForm; account.getAuthorizationForm#b86ba8e1 bot_id:int scope:string public_key:string = account.AuthorizationForm;
account.acceptAuthorization#e7027c94 bot_id:int scope:string public_key:string value_hashes:Vector<SecureValueHash> credentials:SecureCredentialsEncrypted = Bool; account.acceptAuthorization#e7027c94 bot_id:int scope:string public_key:string value_hashes:Vector<SecureValueHash> credentials:SecureCredentialsEncrypted = Bool;
account.sendVerifyPhoneCode#823380b4 flags:# allow_flashcall:flags.0?true phone_number:string current_number:flags.0?Bool = auth.SentCode; account.sendVerifyPhoneCode#a5a356f9 phone_number:string settings:CodeSettings = auth.SentCode;
account.verifyPhone#4dd3a7f6 phone_number:string phone_code_hash:string phone_code:string = Bool; account.verifyPhone#4dd3a7f6 phone_number:string phone_code_hash:string phone_code:string = Bool;
account.sendVerifyEmailCode#7011509f email:string = account.SentEmailCode; account.sendVerifyEmailCode#7011509f email:string = account.SentEmailCode;
account.verifyEmail#ecba39db email:string code:string = Bool; account.verifyEmail#ecba39db email:string code:string = Bool;
@ -1046,6 +1074,13 @@ account.cancelPasswordEmail#c1cbd5b6 = Bool;
account.getContactSignUpNotification#9f07c728 = Bool; account.getContactSignUpNotification#9f07c728 = Bool;
account.setContactSignUpNotification#cff43f61 silent:Bool = Bool; account.setContactSignUpNotification#cff43f61 silent:Bool = Bool;
account.getNotifyExceptions#53577479 flags:# compare_sound:flags.1?true peer:flags.0?InputNotifyPeer = Updates; account.getNotifyExceptions#53577479 flags:# compare_sound:flags.1?true peer:flags.0?InputNotifyPeer = Updates;
account.getWallPaper#fc8ddbea wallpaper:InputWallPaper = WallPaper;
account.uploadWallPaper#dd853661 file:InputFile mime_type:string settings:WallPaperSettings = WallPaper;
account.saveWallPaper#6c5a5b37 wallpaper:InputWallPaper unsave:Bool settings:WallPaperSettings = Bool;
account.installWallPaper#feed5769 wallpaper:InputWallPaper settings:WallPaperSettings = Bool;
account.resetWallPapers#bb3b9804 = Bool;
account.getAutoDownloadSettings#56da0b3f = account.AutoDownloadSettings;
account.saveAutoDownloadSettings#76f36233 flags:# low:flags.0?true high:flags.1?true settings:AutoDownloadSettings = Bool;
users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>; users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
users.getFullUser#ca30a5b1 id:InputUser = UserFull; users.getFullUser#ca30a5b1 id:InputUser = UserFull;
@ -1074,7 +1109,7 @@ messages.getDialogs#b098aee6 flags:# exclude_pinned:flags.0?true offset_date:int
messages.getHistory#dcbb8260 peer:InputPeer offset_id:int offset_date:int add_offset:int limit:int max_id:int min_id:int hash:int = messages.Messages; messages.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.search#8614ef68 flags:# peer:InputPeer q:string from_id:flags.0?InputUser filter:MessagesFilter min_date:int max_date:int offset_id:int add_offset:int limit:int max_id:int min_id:int hash:int = messages.Messages;
messages.readHistory#e306d3a peer:InputPeer max_id:int = messages.AffectedMessages; messages.readHistory#e306d3a peer:InputPeer max_id:int = messages.AffectedMessages;
messages.deleteHistory#1c015b09 flags:# just_clear:flags.0?true peer:InputPeer max_id:int = messages.AffectedHistory; messages.deleteHistory#1c015b09 flags:# just_clear:flags.0?true revoke:flags.1?true peer:InputPeer max_id:int = messages.AffectedHistory;
messages.deleteMessages#e58e95d2 flags:# revoke:flags.0?true id:Vector<int> = messages.AffectedMessages; messages.deleteMessages#e58e95d2 flags:# revoke:flags.0?true id:Vector<int> = messages.AffectedMessages;
messages.receivedMessages#5a954c0 max_id:int = Vector<ReceivedNotifyMessage>; messages.receivedMessages#5a954c0 max_id:int = Vector<ReceivedNotifyMessage>;
messages.setTyping#a3825e50 peer:InputPeer action:SendMessageAction = Bool; messages.setTyping#a3825e50 peer:InputPeer action:SendMessageAction = Bool;
@ -1107,7 +1142,7 @@ messages.readMessageContents#36a73f77 id:Vector<int> = messages.AffectedMessages
messages.getStickers#43d4f2c emoticon:string hash:int = messages.Stickers; messages.getStickers#43d4f2c emoticon:string hash:int = messages.Stickers;
messages.getAllStickers#1c9618b1 hash:int = messages.AllStickers; messages.getAllStickers#1c9618b1 hash:int = messages.AllStickers;
messages.getWebPagePreview#8b68b0cc flags:# message:string entities:flags.3?Vector<MessageEntity> = MessageMedia; messages.getWebPagePreview#8b68b0cc flags:# message:string entities:flags.3?Vector<MessageEntity> = MessageMedia;
messages.exportChatInvite#7d885289 chat_id:int = ExportedChatInvite; messages.exportChatInvite#df7534c peer:InputPeer = ExportedChatInvite;
messages.checkChatInvite#3eadb1bb hash:string = ChatInvite; messages.checkChatInvite#3eadb1bb hash:string = ChatInvite;
messages.importChatInvite#6c50051c hash:string = Updates; messages.importChatInvite#6c50051c hash:string = Updates;
messages.getStickerSet#2619a90e stickerset:InputStickerSet = messages.StickerSet; messages.getStickerSet#2619a90e stickerset:InputStickerSet = messages.StickerSet;
@ -1115,7 +1150,6 @@ messages.installStickerSet#c78fe460 stickerset:InputStickerSet archived:Bool = m
messages.uninstallStickerSet#f96e55de stickerset:InputStickerSet = Bool; messages.uninstallStickerSet#f96e55de stickerset:InputStickerSet = Bool;
messages.startBot#e6df7378 bot:InputUser peer:InputPeer random_id:long start_param:string = Updates; messages.startBot#e6df7378 bot:InputUser peer:InputPeer random_id:long start_param:string = Updates;
messages.getMessagesViews#c4c8a55d peer:InputPeer id:Vector<int> increment:Bool = Vector<int>; messages.getMessagesViews#c4c8a55d peer:InputPeer id:Vector<int> increment:Bool = Vector<int>;
messages.toggleChatAdmins#ec8bd9e1 chat_id:int enabled:Bool = Updates;
messages.editChatAdmin#a9e69f2e chat_id:int user_id:InputUser is_admin:Bool = Bool; messages.editChatAdmin#a9e69f2e chat_id:int user_id:InputUser is_admin:Bool = Bool;
messages.migrateChat#15a3b8e3 chat_id:int = Updates; messages.migrateChat#15a3b8e3 chat_id:int = Updates;
messages.searchGlobal#9e3cacb0 q:string offset_date:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages; messages.searchGlobal#9e3cacb0 q:string offset_date:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages;
@ -1173,7 +1207,12 @@ messages.updatePinnedMessage#d2aaf7ec flags:# silent:flags.0?true peer:InputPeer
messages.sendVote#10ea6184 peer:InputPeer msg_id:int options:Vector<bytes> = Updates; messages.sendVote#10ea6184 peer:InputPeer msg_id:int options:Vector<bytes> = Updates;
messages.getPollResults#73bb643b peer:InputPeer msg_id:int = Updates; messages.getPollResults#73bb643b peer:InputPeer msg_id:int = Updates;
messages.getOnlines#6e2be050 peer:InputPeer = ChatOnlines; messages.getOnlines#6e2be050 peer:InputPeer = ChatOnlines;
messages.getStatsURL#83f6c0cd peer:InputPeer = StatsURL; messages.getStatsURL#812c2ae6 flags:# dark:flags.0?true peer:InputPeer params:string = StatsURL;
messages.editChatAbout#def60797 peer:InputPeer about:string = Bool;
messages.editChatDefaultBannedRights#a5866b41 peer:InputPeer banned_rights:ChatBannedRights = Updates;
messages.getEmojiKeywords#35a0e062 lang_code:string = EmojiKeywordsDifference;
messages.getEmojiKeywordsDifference#1508b6af lang_code:string from_version:int = EmojiKeywordsDifference;
messages.getEmojiURL#d5b10c26 lang_code:string = EmojiURL;
updates.getState#edd4882a = updates.State; updates.getState#edd4882a = updates.State;
updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference; updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference;
@ -1223,8 +1262,7 @@ channels.getParticipant#546dd7a6 channel:InputChannel user_id:InputUser = channe
channels.getChannels#a7f6bbb id:Vector<InputChannel> = messages.Chats; channels.getChannels#a7f6bbb id:Vector<InputChannel> = messages.Chats;
channels.getFullChannel#8736a09 channel:InputChannel = messages.ChatFull; channels.getFullChannel#8736a09 channel:InputChannel = messages.ChatFull;
channels.createChannel#f4893d7f flags:# broadcast:flags.0?true megagroup:flags.1?true title:string about:string = Updates; channels.createChannel#f4893d7f flags:# broadcast:flags.0?true megagroup:flags.1?true title:string about:string = Updates;
channels.editAbout#13e27f1e channel:InputChannel about:string = Bool; channels.editAdmin#70f893ba channel:InputChannel user_id:InputUser admin_rights:ChatAdminRights = Updates;
channels.editAdmin#20b88214 channel:InputChannel user_id:InputUser admin_rights:ChannelAdminRights = Updates;
channels.editTitle#566decd0 channel:InputChannel title:string = Updates; channels.editTitle#566decd0 channel:InputChannel title:string = Updates;
channels.editPhoto#f12e57c9 channel:InputChannel photo:InputChatPhoto = Updates; channels.editPhoto#f12e57c9 channel:InputChannel photo:InputChatPhoto = Updates;
channels.checkUsername#10e6bd2c channel:InputChannel username:string = Bool; channels.checkUsername#10e6bd2c channel:InputChannel username:string = Bool;
@ -1232,13 +1270,11 @@ channels.updateUsername#3514b3de channel:InputChannel username:string = Bool;
channels.joinChannel#24b524c5 channel:InputChannel = Updates; channels.joinChannel#24b524c5 channel:InputChannel = Updates;
channels.leaveChannel#f836aa95 channel:InputChannel = Updates; channels.leaveChannel#f836aa95 channel:InputChannel = Updates;
channels.inviteToChannel#199f3a6c channel:InputChannel users:Vector<InputUser> = Updates; channels.inviteToChannel#199f3a6c channel:InputChannel users:Vector<InputUser> = Updates;
channels.exportInvite#c7560885 channel:InputChannel = ExportedChatInvite;
channels.deleteChannel#c0111fe3 channel:InputChannel = Updates; channels.deleteChannel#c0111fe3 channel:InputChannel = Updates;
channels.toggleInvites#49609307 channel:InputChannel enabled:Bool = Updates;
channels.exportMessageLink#ceb77163 channel:InputChannel id:int grouped:Bool = ExportedMessageLink; channels.exportMessageLink#ceb77163 channel:InputChannel id:int grouped:Bool = ExportedMessageLink;
channels.toggleSignatures#1f69b606 channel:InputChannel enabled:Bool = Updates; channels.toggleSignatures#1f69b606 channel:InputChannel enabled:Bool = Updates;
channels.getAdminedPublicChannels#8d8d82d7 = messages.Chats; channels.getAdminedPublicChannels#8d8d82d7 = messages.Chats;
channels.editBanned#bfd915cd channel:InputChannel user_id:InputUser banned_rights:ChannelBannedRights = Updates; channels.editBanned#72796912 channel:InputChannel user_id:InputUser banned_rights:ChatBannedRights = Updates;
channels.getAdminLog#33ddf480 flags:# channel:InputChannel q:string events_filter:flags.0?ChannelAdminLogEventsFilter admins:flags.1?Vector<InputUser> max_id:long min_id:long limit:int = channels.AdminLogResults; channels.getAdminLog#33ddf480 flags:# channel:InputChannel q:string events_filter:flags.0?ChannelAdminLogEventsFilter admins:flags.1?Vector<InputUser> max_id:long min_id:long limit:int = channels.AdminLogResults;
channels.setStickers#ea8ca4f9 channel:InputChannel stickerset:InputStickerSet = Bool; channels.setStickers#ea8ca4f9 channel:InputChannel stickerset:InputStickerSet = Bool;
channels.readMessageContents#eab5dc38 channel:InputChannel id:Vector<int> = Bool; channels.readMessageContents#eab5dc38 channel:InputChannel id:Vector<int> = Bool;
@ -1267,13 +1303,13 @@ phone.acceptCall#3bd2b4a0 peer:InputPhoneCall g_b:bytes protocol:PhoneCallProtoc
phone.confirmCall#2efe1722 peer:InputPhoneCall g_a:bytes key_fingerprint:long protocol:PhoneCallProtocol = phone.PhoneCall; phone.confirmCall#2efe1722 peer:InputPhoneCall g_a:bytes key_fingerprint:long protocol:PhoneCallProtocol = phone.PhoneCall;
phone.receivedCall#17d54f61 peer:InputPhoneCall = Bool; phone.receivedCall#17d54f61 peer:InputPhoneCall = Bool;
phone.discardCall#78d413a6 peer:InputPhoneCall duration:int reason:PhoneCallDiscardReason connection_id:long = Updates; phone.discardCall#78d413a6 peer:InputPhoneCall duration:int reason:PhoneCallDiscardReason connection_id:long = Updates;
phone.setCallRating#1c536a34 peer:InputPhoneCall rating:int comment:string = Updates; phone.setCallRating#59ead627 flags:# user_initiative:flags.0?true peer:InputPhoneCall rating:int comment:string = Updates;
phone.saveCallDebug#277add7e peer:InputPhoneCall debug:DataJSON = Bool; phone.saveCallDebug#277add7e peer:InputPhoneCall debug:DataJSON = Bool;
langpack.getLangPack#f2f2330a lang_pack:string lang_code:string = LangPackDifference; langpack.getLangPack#f2f2330a lang_pack:string lang_code:string = LangPackDifference;
langpack.getStrings#efea3803 lang_pack:string lang_code:string keys:Vector<string> = Vector<LangPackString>; langpack.getStrings#efea3803 lang_pack:string lang_code:string keys:Vector<string> = Vector<LangPackString>;
langpack.getDifference#9d51e814 lang_code:string from_version:int = LangPackDifference; langpack.getDifference#cd984aa5 lang_pack:string lang_code:string from_version:int = LangPackDifference;
langpack.getLanguages#42c6978f lang_pack:string = Vector<LangPackLanguage>; langpack.getLanguages#42c6978f lang_pack:string = Vector<LangPackLanguage>;
langpack.getLanguage#6a596502 lang_pack:string lang_code:string = LangPackLanguage; langpack.getLanguage#6a596502 lang_pack:string lang_code:string = LangPackLanguage;
// LAYER 91 // LAYER 97

View File

@ -9,7 +9,10 @@ class {class_name}(Object):
"""{docstring_args} """{docstring_args}
""" """
__slots__ = [{slots}]
ID = {object_id} ID = {object_id}
QUALNAME = "{qualname}"
def __init__(self{arguments}): def __init__(self{arguments}):
{fields} {fields}

View File

@ -22,7 +22,7 @@ import re
import shutil import shutil
HOME = "compiler/error" HOME = "compiler/error"
DEST = "pyrogram/api/errors/exceptions" DEST = "pyrogram/errors/exceptions"
NOTICE_PATH = "NOTICE" NOTICE_PATH = "NOTICE"
@ -73,7 +73,7 @@ def start():
f_init.write("from .{}_{} import *\n".format(name.lower(), code)) f_init.write("from .{}_{} import *\n".format(name.lower(), code))
with open("{}/source/{}".format(HOME, i), encoding="utf-8") as f_csv, \ with open("{}/source/{}".format(HOME, i), encoding="utf-8") as f_csv, \
open("{}/{}_{}.py".format(DEST, name.lower(), code), "w", encoding="utf-8") as f_class: open("{}/{}_{}.py".format(DEST, name.lower(), code), "w", encoding="utf-8") as f_class:
reader = csv.reader(f_csv, delimiter="\t") reader = csv.reader(f_csv, delimiter="\t")
super_class = caml(name) super_class = caml(name)
@ -134,7 +134,7 @@ def start():
if "__main__" == __name__: if "__main__" == __name__:
HOME = "." HOME = "."
DEST = "../../pyrogram/api/errors/exceptions" DEST = "../../pyrogram/errors/exceptions"
NOTICE_PATH = "../../NOTICE" NOTICE_PATH = "../../NOTICE"
start() start()

View File

@ -88,5 +88,12 @@ MEDIA_INVALID The media is invalid
BOT_SCORE_NOT_MODIFIED The bot score was not modified BOT_SCORE_NOT_MODIFIED The bot score was not modified
USER_BOT_REQUIRED The method can be used by bots only USER_BOT_REQUIRED The method can be used by bots only
IMAGE_PROCESS_FAILED The server failed to process your image IMAGE_PROCESS_FAILED The server failed to process your image
USERNAME_NOT_MODIFIED The username was not modified
CALL_ALREADY_ACCEPTED The call is already accepted CALL_ALREADY_ACCEPTED The call is already accepted
CALL_ALREADY_DECLINED The call is already declined CALL_ALREADY_DECLINED The call is already declined
PHOTO_EXT_INVALID The photo extension is invalid
EXTERNAL_URL_INVALID The external media URL is invalid
CHAT_NOT_MODIFIED The chat settings were not modified
RESULTS_TOO_MUCH The result contains too many items
RESULT_ID_DUPLICATE The result contains items with duplicated identifiers
ACCESS_TOKEN_INVALID The bot access token is invalid
1 id message
88 BOT_SCORE_NOT_MODIFIED The bot score was not modified
89 USER_BOT_REQUIRED The method can be used by bots only
90 IMAGE_PROCESS_FAILED The server failed to process your image
91 USERNAME_NOT_MODIFIED The username was not modified
92 CALL_ALREADY_ACCEPTED The call is already accepted
93 CALL_ALREADY_DECLINED The call is already declined
94 PHOTO_EXT_INVALID The photo extension is invalid
95 EXTERNAL_URL_INVALID The external media URL is invalid
96 CHAT_NOT_MODIFIED The chat settings were not modified
97 RESULTS_TOO_MUCH The result contains too many items
98 RESULT_ID_DUPLICATE The result contains items with duplicated identifiers
99 ACCESS_TOKEN_INVALID The bot access token is invalid

View File

@ -1,12 +1,12 @@
{notice} {notice}
from ..error import Error from ..rpc_error import RPCError
class {super_class}(Error): class {super_class}(RPCError):
{docstring} {docstring}
CODE = {code} CODE = {code}
"""``int``: Error Code""" """``int``: RPC Error Code"""
NAME = __doc__ NAME = __doc__

View File

@ -1,7 +1,7 @@
class {sub_class}({super_class}): class {sub_class}({super_class}):
{docstring} {docstring}
ID = {id} ID = {id}
"""``str``: Error ID""" """``str``: RPC Error ID"""
MESSAGE = __doc__ MESSAGE = __doc__

View File

@ -25,6 +25,10 @@ sys.path.insert(0, os.path.abspath('../..'))
# Import after sys.path.insert() to avoid issues # Import after sys.path.insert() to avoid issues
from pyrogram import __version__ from pyrogram import __version__
from pygments.styles.friendly import FriendlyStyle
FriendlyStyle.background_color = "#f3f2f1"
# -- General configuration ------------------------------------------------ # -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here. # If your documentation needs a minimal Sphinx version, state it here.
@ -60,7 +64,7 @@ master_doc = 'index'
# General information about the project. # General information about the project.
project = 'Pyrogram' project = 'Pyrogram'
copyright = '2017-2018, Dan Tès' copyright = '2017-2019, Dan Tès'
author = 'Dan Tès' author = 'Dan Tès'
# The version info for the project you're documenting, acts as replacement for # The version info for the project you're documenting, acts as replacement for
@ -85,7 +89,7 @@ language = None
exclude_patterns = [] exclude_patterns = []
# The name of the Pygments (syntax highlighting) style to use. # The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'tango' pygments_style = 'friendly'
# If true, `todo` and `todoList` produce output, else they produce nothing. # If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False todo_include_todos = False

View File

@ -1,8 +1,7 @@
400 - Bad Request 400 - Bad Request
================= =================
.. module:: pyrogram.api.errors.BadRequest .. module:: pyrogram.errors.BadRequest
.. automodule:: pyrogram.api.errors.exceptions.bad_request_400 .. automodule:: pyrogram.errors.exceptions.bad_request_400
:members: :members:
:show-inheritance:

View File

@ -1,8 +1,7 @@
420 - Flood 420 - Flood
=========== ===========
.. module:: pyrogram.api.errors.Flood .. module:: pyrogram.errors.Flood
.. automodule:: pyrogram.api.errors.exceptions.flood_420 .. automodule:: pyrogram.errors.exceptions.flood_420
:members: :members:
:show-inheritance:

View File

@ -1,8 +1,7 @@
403 - Forbidden 403 - Forbidden
=============== ===============
.. module:: pyrogram.api.errors.Forbidden .. module:: pyrogram.errors.Forbidden
.. automodule:: pyrogram.api.errors.exceptions.forbidden_403 .. automodule:: pyrogram.errors.exceptions.forbidden_403
:members: :members:
:show-inheritance:

View File

@ -1,8 +1,7 @@
500 - Internal Server Error 500 - Internal Server Error
=========================== ===========================
.. module:: pyrogram.api.errors.InternalServerError .. module:: pyrogram.errors.InternalServerError
.. automodule:: pyrogram.api.errors.exceptions.internal_server_error_500 .. automodule:: pyrogram.errors.exceptions.internal_server_error_500
:members: :members:
:show-inheritance:

View File

@ -1,8 +1,7 @@
406 - Not Acceptable 406 - Not Acceptable
==================== ====================
.. module:: pyrogram.api.errors.NotAcceptable .. module:: pyrogram.errors.NotAcceptable
.. automodule:: pyrogram.api.errors.exceptions.not_acceptable_406 .. automodule:: pyrogram.errors.exceptions.not_acceptable_406
:members: :members:
:show-inheritance:

View File

@ -1,8 +1,7 @@
303 - See Other 303 - See Other
=============== ===============
.. module:: pyrogram.api.errors.SeeOther .. module:: pyrogram.errors.SeeOther
.. automodule:: pyrogram.api.errors.exceptions.see_other_303 .. automodule:: pyrogram.errors.exceptions.see_other_303
:members: :members:
:show-inheritance:

View File

@ -1,8 +1,7 @@
401 - Unauthorized 401 - Unauthorized
================== ==================
.. module:: pyrogram.api.errors.Unauthorized .. module:: pyrogram.errors.Unauthorized
.. automodule:: pyrogram.api.errors.exceptions.unauthorized_401 .. automodule:: pyrogram.errors.exceptions.unauthorized_401
:members: :members:
:show-inheritance:

View File

@ -1,8 +1,7 @@
520 - Unknown Error 520 - Unknown Error
=================== ===================
.. module:: pyrogram.api.errors.UnknownError .. module:: pyrogram.errors.UnknownError
.. autoexception:: pyrogram.api.errors.error.UnknownError .. autoexception:: pyrogram.errors.rpc_error.UnknownError
:members: :members:
:show-inheritance:

View File

@ -26,7 +26,7 @@ Welcome to Pyrogram
</a> </a>
<br> <br>
<a href="compiler/api/source/main_api.tl"> <a href="compiler/api/source/main_api.tl">
<img src="https://img.shields.io/badge/schema-layer%2091-eda738.svg?longCache=true&colorA=262b30" <img src="https://img.shields.io/badge/schema-layer%2097-eda738.svg?longCache=true&colorA=262b30"
alt="Schema Layer"> alt="Schema Layer">
</a> </a>
<a href="https://github.com/pyrogram/tgcrypto"> <a href="https://github.com/pyrogram/tgcrypto">
@ -67,7 +67,7 @@ Features
- **Fast**: Crypto parts are boosted up by TgCrypto_, a high-performance library written in pure C. - **Fast**: Crypto parts are boosted up by TgCrypto_, a high-performance library written in pure C.
- **Documented**: Pyrogram API methods, types and public interfaces are well documented. - **Documented**: Pyrogram API methods, types and public interfaces are well documented.
- **Type-hinted**: Exposed Pyrogram types and method parameters are all type-hinted. - **Type-hinted**: Exposed Pyrogram types and method parameters are all type-hinted.
- **Updated**, to the latest Telegram API version, currently Layer 91 on top of `MTProto 2.0`_. - **Updated**, to the latest Telegram API version, currently Layer 97 on top of `MTProto 2.0`_.
- **Pluggable**: The Smart Plugin system allows to write components with minimal boilerplate code. - **Pluggable**: The Smart Plugin system allows to write components with minimal boilerplate code.
- **Comprehensive**: Execute any advanced action an official client is able to do, and even more. - **Comprehensive**: Execute any advanced action an official client is able to do, and even more.
@ -115,7 +115,7 @@ To get started, press the Next button.
functions/index functions/index
types/index types/index
.. _`Telegram`: https://telegram.org/ .. _`Telegram`: https://telegram.org
.. _TgCrypto: https://docs.pyrogram.ml/resources/TgCrypto/ .. _TgCrypto: https://docs.pyrogram.ml/resources/TgCrypto
.. _`MTProto API`: https://core.telegram.org/api#telegram-api .. _`MTProto API`: https://core.telegram.org/api#telegram-api
.. _`MTProto 2.0`: https://core.telegram.org/mtproto .. _`MTProto 2.0`: https://core.telegram.org/mtproto

View File

@ -31,6 +31,7 @@ Decorators
on_message on_message
on_callback_query on_callback_query
on_inline_query
on_deleted_messages on_deleted_messages
on_user_status on_user_status
on_disconnect on_disconnect
@ -56,6 +57,7 @@ Messages
send_location send_location
send_venue send_venue
send_contact send_contact
send_cached_media
send_chat_action send_chat_action
edit_message_text edit_message_text
edit_message_caption edit_message_caption
@ -67,6 +69,7 @@ Messages
iter_history iter_history
send_poll send_poll
vote_poll vote_poll
close_poll
retract_vote retract_vote
download_media download_media
@ -97,6 +100,8 @@ Chats
iter_chat_members iter_chat_members
get_dialogs get_dialogs
iter_dialogs iter_dialogs
restrict_chat
update_chat_username
Users Users
----- -----
@ -109,6 +114,7 @@ Users
get_user_profile_photos get_user_profile_photos
set_user_profile_photo set_user_profile_photo
delete_user_profile_photos delete_user_profile_photos
update_username
Contacts Contacts
-------- --------
@ -139,10 +145,12 @@ Bots
get_inline_bot_results get_inline_bot_results
send_inline_bot_result send_inline_bot_result
answer_callback_query answer_callback_query
answer_inline_query
request_callback_answer request_callback_answer
send_game send_game
set_game_score set_game_score
get_game_high_scores get_game_high_scores
answer_inline_query
.. autoclass:: pyrogram.Client .. autoclass:: pyrogram.Client

View File

@ -9,6 +9,7 @@ Handlers
MessageHandler MessageHandler
DeletedMessagesHandler DeletedMessagesHandler
CallbackQueryHandler CallbackQueryHandler
InlineQueryHandler
UserStatusHandler UserStatusHandler
DisconnectHandler DisconnectHandler
RawUpdateHandler RawUpdateHandler
@ -22,6 +23,9 @@ Handlers
.. autoclass:: CallbackQueryHandler .. autoclass:: CallbackQueryHandler
:members: :members:
.. autoclass:: InlineQueryHandler
:members:
.. autoclass:: UserStatusHandler .. autoclass:: UserStatusHandler
:members: :members:

View File

@ -1,9 +1,8 @@
Error RPCError
===== ========
.. autoexception:: pyrogram.Error .. autoexception:: pyrogram.RPCError
:members: :members:
:show-inheritance:
.. toctree:: .. toctree::
../errors/SeeOther ../errors/SeeOther

View File

@ -16,6 +16,7 @@ Users & Chats
ChatPhoto ChatPhoto
ChatMember ChatMember
ChatMembers ChatMembers
ChatPermissions
Dialog Dialog
Dialogs Dialogs
@ -65,6 +66,7 @@ Input Media
.. autosummary:: .. autosummary::
:nosignatures: :nosignatures:
InputMedia
InputMediaPhoto InputMediaPhoto
InputMediaVideo InputMediaVideo
InputMediaAudio InputMediaAudio
@ -72,6 +74,25 @@ Input Media
InputMediaDocument InputMediaDocument
InputPhoneContact InputPhoneContact
Inline Mode
------------
.. autosummary::
:nosignatures:
InlineQuery
InlineQueryResult
InlineQueryResultArticle
InputMessageContent
-------------------
.. autosummary::
:nosignatures:
InputMessageContent
InputTextMessageContent
.. User & Chats .. User & Chats
------------ ------------
@ -96,6 +117,9 @@ Input Media
.. autoclass:: ChatMembers .. autoclass:: ChatMembers
:members: :members:
.. autoclass:: ChatPermissions
:members:
.. autoclass:: Dialog .. autoclass:: Dialog
:members: :members:
@ -195,6 +219,9 @@ Input Media
.. Input Media .. Input Media
----------- -----------
.. autoclass:: InputMedia
:members:
.. autoclass:: InputMediaPhoto .. autoclass:: InputMediaPhoto
:members: :members:
@ -212,3 +239,25 @@ Input Media
.. autoclass:: InputPhoneContact .. autoclass:: InputPhoneContact
:members: :members:
.. Inline Mode
-----------
.. autoclass:: InlineQuery
:members:
.. autoclass:: InlineQueryResult
:members:
.. autoclass:: InlineQueryResultArticle
:members:
.. InputMessageContent
-------------------
.. autoclass:: InputMessageContent
:members:
.. autoclass:: InputTextMessageContent
:members:

View File

@ -15,6 +15,6 @@ after the well established `Telegram Bot API`_ methods, thus offering a familiar
Filters Filters
ChatAction ChatAction
ParseMode ParseMode
Error RPCError
.. _Telegram Bot API: https://core.telegram.org/bots/api#available-methods .. _Telegram Bot API: https://core.telegram.org/bots/api#available-methods

View File

@ -1,40 +1,102 @@
Advanced Usage Advanced Usage
============== ==============
In this section, you'll be shown the alternative way of communicating with Telegram using Pyrogram: the main Telegram Pyrogram's API, which consists of well documented convenience methods_ and facade types_, exists to provide a much
API with its raw functions and types. easier interface to the undocumented and often confusing Telegram API.
In this section, you'll be shown the alternative way of communicating with Telegram using Pyrogram: the main "raw"
Telegram API with its functions and types.
Telegram Raw API Telegram Raw API
---------------- ----------------
If you can't find a high-level method for your needs or if you want complete, low-level access to the whole If you can't find a high-level method for your needs or if you want complete, low-level access to the whole
Telegram API, you have to use the raw :mod:`functions <pyrogram.api.functions>` and :mod:`types <pyrogram.api.types>` Telegram API, you have to use the raw :mod:`functions <pyrogram.api.functions>` and :mod:`types <pyrogram.api.types>`.
exposed by the ``pyrogram.api`` package and call any Telegram API method you wish using the
:meth:`send() <pyrogram.Client.send>` method provided by the Client class. As already hinted, raw functions and types can be really confusing, mainly because people don't realize soon enough they
accept *only* the right types and that all required parameters must be filled in. This section will therefore explain
some pitfalls to take into consideration when working with the raw API.
.. hint:: .. hint::
Every available high-level method mentioned in the previous page is built on top of these raw functions. Every available high-level methods in Pyrogram is built on top of these raw functions.
Nothing stops you from using the raw functions only, but they are rather complex and `plenty of them`_ are already Nothing stops you from using the raw functions only, but they are rather complex and `plenty of them`_ are already
re-implemented by providing a much simpler and cleaner interface which is very similar to the Bot API. re-implemented by providing a much simpler and cleaner interface which is very similar to the Bot API (yet much more
powerful).
If you think a raw function should be wrapped and added as a high-level method, feel free to ask in our Community_! If you think a raw function should be wrapped and added as a high-level method, feel free to ask in our Community_!
Caveats Invoking Functions
------- ^^^^^^^^^^^^^^^^^^
As hinted before, raw functions and types can be confusing, mainly because people don't realize they must accept Unlike the methods_ found in Pyrogram's API, which can be called in the usual simple way, functions to be invoked from
*exactly* the right values, but also because most of them don't have enough Python experience to fully grasp how things the raw Telegram API have a different way of usage and are more complex.
work.
This section will therefore explain some pitfalls to take into consideration when working with the raw API. First of all, both `raw functions`_ and `raw types`_ live in their respective packages (and sub-packages):
``pyrogram.api.functions``, ``pyrogram.api.types``. They all exist as Python classes, meaning you need to create an
instance of each every time you need them and fill them in with the correct values using named arguments.
Next, to actually invoke the raw function you have to use the :meth:`send() <pyrogram.Client.send>` method provided by
the Client class and pass the function object you created.
Here's some examples:
- Update first name, last name and bio:
.. code-block:: python
from pyrogram import Client
from pyrogram.api import functions
with Client("my_account") as app:
app.send(
functions.account.UpdateProfile(
first_name="Dan", last_name="Tès",
about="Bio written from Pyrogram"
)
)
- Disable links to your account when someone forwards your messages:
.. code-block:: python
from pyrogram import Client
from pyrogram.api import functions, types
with Client("my_account") as app:
app.send(
functions.account.SetPrivacy(
key=types.PrivacyKeyForwards(),
rules=[types.InputPrivacyValueDisallowAll()]
)
)
- Invite users to your channel/supergroup:
.. code-block:: python
from pyrogram import Client
from pyrogram.api import functions, types
with Client("my_account") as app:
app.send(
functions.channels.InviteToChannel(
channel=app.resolve_peer(123456789), # ID or Username
users=[ # The users you want to invite
app.resolve_peer(23456789), # By ID
app.resolve_peer("username"), # By username
app.resolve_peer("+393281234567"), # By phone number
]
)
)
Chat IDs Chat IDs
^^^^^^^^ ^^^^^^^^
The way Telegram works makes it impossible to directly send a message to a user or a chat by using their IDs only. The way Telegram works makes it impossible to directly send a message to a user or a chat by using their IDs only.
Instead, a pair of ``id`` and ``access_hash`` wrapped in a so called ``InputPeer`` is always needed. Instead, a pair of ``id`` and ``access_hash`` wrapped in a so called ``InputPeer`` is always needed. Pyrogram allows
sending messages with IDs only thanks to cached access hashes.
There are three different InputPeer types, one for each kind of Telegram entity. There are three different InputPeer types, one for each kind of Telegram entity.
Whenever an InputPeer is needed you must pass one of these: Whenever an InputPeer is needed you must pass one of these:
@ -56,66 +118,19 @@ kind of ID.
For example, given the ID *123456789*, here's how Pyrogram can tell entities apart: For example, given the ID *123456789*, here's how Pyrogram can tell entities apart:
- ``+ID`` - User: *123456789* - ``+ID`` User: *123456789*
- ``-ID`` - Chat: *-123456789* - ``-ID`` Chat: *-123456789*
- ``-100ID`` - Channel (and Supergroup): *-100123456789* - ``-100ID`` Channel (and Supergroup): *-100123456789*
So, every time you take a raw ID, make sure to translate it into the correct ID when you want to use it with an So, every time you take a raw ID, make sure to translate it into the correct ID when you want to use it with an
high-level method. high-level method.
Examples
--------
- Update first name, last name and bio:
.. code-block:: python
from pyrogram import Client
from pyrogram.api import functions
with Client("my_account") as app:
app.send(
functions.account.UpdateProfile(
first_name="Dan", last_name="Tès",
about="Bio written from Pyrogram"
)
)
- Share your Last Seen time only with your contacts:
.. code-block:: python
from pyrogram import Client
from pyrogram.api import functions, types
with Client("my_account") as app:
app.send(
functions.account.SetPrivacy(
key=types.InputPrivacyKeyStatusTimestamp(),
rules=[types.InputPrivacyValueAllowContacts()]
)
)
- Invite users to your channel/supergroup:
.. code-block:: python
from pyrogram import Client
from pyrogram.api import functions, types
with Client("my_account") as app:
app.send(
functions.channels.InviteToChannel(
channel=app.resolve_peer(123456789), # ID or Username
users=[ # The users you want to invite
app.resolve_peer(23456789), # By ID
app.resolve_peer("username"), # By username
app.resolve_peer("393281234567"), # By phone number
]
)
)
.. _methods: ../pyrogram/Client.html#messages
.. _types: ../pyrogram/Types.html
.. _plenty of them: ../pyrogram/Client.html#messages .. _plenty of them: ../pyrogram/Client.html#messages
.. _Raw Functions: Usage.html#using-raw-functions .. _raw functions: ../pyrogram/functions
.. _raw types: ../pyrogram/types
.. _Community: https://t.me/PyrogramChat .. _Community: https://t.me/PyrogramChat

View File

@ -1,13 +1,13 @@
Configuration File Configuration File
================== ==================
As already mentioned in previous sections, Pyrogram can also be configured by the use of an INI file. As already mentioned in previous sections, Pyrogram can be configured by the use of an INI file.
This page explains how this file is structured in Pyrogram, how to use it and why. This page explains how this file is structured in Pyrogram, how to use it and why.
Introduction Introduction
------------ ------------
The idea behind using a configuration file is to help keeping your code free of settings (private) information such as The idea behind using a configuration file is to help keeping your code free of private settings information such as
the API Key and Proxy without having you to even deal with how to load such settings. The configuration file, usually the API Key and Proxy without having you to even deal with how to load such settings. The configuration file, usually
referred as ``config.ini`` file, is automatically loaded from the root of your working directory; all you need to do is referred as ``config.ini`` file, is automatically loaded from the root of your working directory; all you need to do is
fill in the necessary parts. fill in the necessary parts.
@ -46,7 +46,7 @@ These are all the sections Pyrogram uses in its configuration file:
Pyrogram Pyrogram
^^^^^^^^ ^^^^^^^^
The ``[pyrogram]`` section contains your Telegram API credentials *api_id* and *api_hash*. The ``[pyrogram]`` section contains your Telegram API credentials: *api_id* and *api_hash*.
.. code-block:: ini .. code-block:: ini

View File

@ -1,19 +1,19 @@
Customize Sessions Customize Sessions
================== ==================
As you may probably know, Telegram allows Users (and Bots) having more than one session (authorizations) registered As you may probably know, Telegram allows users (and bots) having more than one session (authorizations) registered
in the system at the same time. in the system at the same time.
Briefly explaining, sessions are simply new logins in your account. They can be reviewed in the settings of an official Briefly explaining, sessions are simply new logins in your account. They can be reviewed in the settings of an official
app (or by invoking `GetAuthorizations <../functions/account/GetAuthorizations.html>`_ with Pyrogram) and store some useful app (or by invoking `GetAuthorizations <../functions/account/GetAuthorizations.html>`_ with Pyrogram). They store some
information about the client who generated them. useful information such as the client who's using them and from which country and IP address.
.. figure:: https://i.imgur.com/lzGPCdZ.png .. figure:: https://i.imgur.com/lzGPCdZ.png
:width: 70% :width: 70%
:align: center :align: center
A Pyrogram session running on Linux, Python 3.6. **A Pyrogram session running on Linux, Python 3.6.**
That's how a session looks like on the Android app, showing the three main pieces of information. That's how a session looks like on the Android app, showing the three main pieces of information.

View File

@ -4,18 +4,18 @@ Error Handling
Errors are inevitable when working with the API, and they must be correctly handled with ``try..except`` blocks. Errors are inevitable when working with the API, and they must be correctly handled with ``try..except`` blocks.
There are many errors that Telegram could return, but they all fall in one of these categories There are many errors that Telegram could return, but they all fall in one of these categories
(which are in turn children of the :obj:`pyrogram.Error` superclass) (which are in turn children of the :obj:`RPCError <pyrogram.RPCError>` superclass):
- :obj:`303 - See Other <pyrogram.api.errors.SeeOther>` - :obj:`303 - See Other <pyrogram.errors.SeeOther>`
- :obj:`400 - Bad Request <pyrogram.api.errors.BadRequest>` - :obj:`400 - Bad Request <pyrogram.errors.BadRequest>`
- :obj:`401 - Unauthorized <pyrogram.api.errors.Unauthorized>` - :obj:`401 - Unauthorized <pyrogram.errors.Unauthorized>`
- :obj:`403 - Forbidden <pyrogram.api.errors.Forbidden>` - :obj:`403 - Forbidden <pyrogram.errors.Forbidden>`
- :obj:`406 - Not Acceptable <pyrogram.api.errors.NotAcceptable>` - :obj:`406 - Not Acceptable <pyrogram.errors.NotAcceptable>`
- :obj:`420 - Flood <pyrogram.api.errors.Flood>` - :obj:`420 - Flood <pyrogram.errors.Flood>`
- :obj:`500 - Internal Server Error <pyrogram.api.errors.InternalServerError>` - :obj:`500 - Internal Server Error <pyrogram.errors.InternalServerError>`
As stated above, there are really many (too many) errors, and in case Pyrogram does not know anything yet about a As stated above, there are really many (too many) errors, and in case Pyrogram does not know anything yet about a
specific one, it raises a special :obj:`520 Unknown Error <pyrogram.api.errors.UnknownError>` exception and logs it specific one, it raises a special :obj:`520 Unknown Error <pyrogram.errors.UnknownError>` exception and logs it
in the ``unknown_errors.txt`` file. Users are invited to report these unknown errors; in later versions of Pyrogram in the ``unknown_errors.txt`` file. Users are invited to report these unknown errors; in later versions of Pyrogram
some kind of automatic error reporting module might be implemented. some kind of automatic error reporting module might be implemented.
@ -24,7 +24,7 @@ Examples
.. code-block:: python .. code-block:: python
from pyrogram.api.errors import ( from pyrogram.errors import (
BadRequest, Flood, InternalServerError, BadRequest, Flood, InternalServerError,
SeeOther, Unauthorized, UnknownError SeeOther, Unauthorized, UnknownError
) )
@ -45,13 +45,13 @@ Examples
pass pass
Exception objects may also contain some informative values. Exception objects may also contain some informative values.
E.g.: :obj:`FloodWait <pyrogram.api.errors.exceptions.flood_420.FloodWait>` holds the amount of seconds you have to wait E.g.: :obj:`FloodWait <pyrogram.errors.exceptions.flood_420.FloodWait>` holds the amount of seconds you have to wait
before you can try again. The value is always stored in the ``x`` field of the returned exception object: before you can try again. The value is always stored in the ``x`` field of the returned exception object:
.. code-block:: python .. code-block:: python
import time import time
from pyrogram.api.errors import FloodWait from pyrogram.errors import FloodWait
try: try:
... ...

View File

@ -1,24 +1,22 @@
More on Updates More on Updates
=============== ===============
Here we'll show some advanced usages when working with updates. Here we'll show some advanced usages when working with `update handlers`_ and `filters`_.
.. note::
This page makes use of Handlers and Filters to show you how to handle updates.
Learn more at `Update Handling <UpdateHandling.html>`_ and `Using Filters <UsingFilters.html>`_.
Handler Groups Handler Groups
-------------- --------------
If you register handlers with overlapping filters, only the first one is executed and any other handler will be ignored. If you register handlers with overlapping (conflicting) filters, only the first one is executed and any other handler
will be ignored. This is intended by design.
In order to process the same update more than once, you can register your handler in a different group. In order to handle the very same update more than once, you have to register your handler in a different dispatching
Groups are identified by a number (number 0 being the default) and are sorted, that is, a lower group number has a group. Dispatching groups hold one or more handlers and are processed sequentially, they are identified by a number
higher priority. (number 0 being the default) and sorted, that is, a lower group number has a higher priority:
For example, in: For example, take these two handlers:
.. code-block:: python .. code-block:: python
:emphasize-lines: 1, 6
@app.on_message(Filters.text | Filters.sticker) @app.on_message(Filters.text | Filters.sticker)
def text_or_sticker(client, message): def text_or_sticker(client, message):
@ -29,8 +27,8 @@ For example, in:
def just_text(client, message): def just_text(client, message):
print("Just Text") print("Just Text")
``just_text`` is never executed because ``text_or_sticker`` already handles texts. To enable it, simply register the Here, ``just_text`` is never executed because ``text_or_sticker``, which has been registered first, already handles
function using a different group: texts (``Filters.text`` is shared and conflicting). To enable it, register the function using a different group:
.. code-block:: python .. code-block:: python
@ -69,7 +67,7 @@ continue to propagate the same update to the next groups until all the handlers
@app.on_message(Filters.private, group=1) @app.on_message(Filters.private, group=1)
def _(client, message): def _(client, message):
print(1 / 0) # Unhandled exception: ZeroDivisionError raise Exception("Unhandled exception!") # Simulate an unhandled exception
@app.on_message(Filters.private, group=2) @app.on_message(Filters.private, group=2)
@ -82,7 +80,7 @@ The output for each incoming update will therefore be:
.. code-block:: text .. code-block:: text
0 0
ZeroDivisionError: division by zero Exception: Unhandled exception!
2 2
Stop Propagation Stop Propagation
@ -153,9 +151,9 @@ Continue Propagation
As opposed to `stopping the update propagation <#stop-propagation>`_ and also as an alternative to the As opposed to `stopping the update propagation <#stop-propagation>`_ and also as an alternative to the
`handler groups <#handler-groups>`_, you can signal the internal dispatcher to continue the update propagation within `handler groups <#handler-groups>`_, you can signal the internal dispatcher to continue the update propagation within
the group regardless of the next handler's filters. This allows you to register multiple handlers with overlapping **the same group** regardless of the next handler's filters. This allows you to register multiple handlers with
filters in the same group; to let the dispatcher process the next handler you can do *one* of the following in each overlapping filters in the same group; to let the dispatcher process the next handler you can do *one* of the following
handler you want to grant permission to continue: in each handler you want to grant permission to continue:
- Call the update's bound-method ``.continue_propagation()`` (preferred way). - Call the update's bound-method ``.continue_propagation()`` (preferred way).
- Manually ``raise ContinuePropagation`` exception (more suitable for raw updates only). - Manually ``raise ContinuePropagation`` exception (more suitable for raw updates only).
@ -218,4 +216,7 @@ The output of both (equivalent) examples will be:
0 0
1 1
2 2
.. _`update handlers`: UpdateHandling.html
.. _`filters`: UsingFilters.html

View File

@ -13,8 +13,8 @@ Introduction
------------ ------------
Prior to the Smart Plugin system, pluggable handlers were already possible. For example, if you wanted to modularize Prior to the Smart Plugin system, pluggable handlers were already possible. For example, if you wanted to modularize
your applications, you had to put your function definitions in separate files and register them inside your main script, your applications, you had to put your function definitions in separate files and register them inside your main script
like this: after importing your modules, like this:
.. note:: .. note::
@ -72,7 +72,7 @@ functions. So, what if you could? Smart Plugins solve this issue by taking care
Using Smart Plugins Using Smart Plugins
------------------- -------------------
Setting up your Pyrogram project to accommodate Smart Plugins is straightforward: Setting up your Pyrogram project to accommodate Smart Plugins is pretty straightforward:
#. Create a new folder to store all the plugins (e.g.: "plugins", "handlers", ...). #. Create a new folder to store all the plugins (e.g.: "plugins", "handlers", ...).
#. Put your python files full of plugins inside. Organize them as you wish. #. Put your python files full of plugins inside. Organize them as you wish.
@ -129,18 +129,17 @@ Setting up your Pyrogram project to accommodate Smart Plugins is straightforward
from pyrogram import Client from pyrogram import Client
plugins = dict( plugins = dict(root="plugins")
root="plugins"
)
Client("my_account", plugins=plugins).run() Client("my_account", plugins=plugins).run()
The first important thing to note is the new ``plugins`` folder. You can put *any python file* in *any subfolder* and The first important thing to note is the new ``plugins`` folder. You can put *any python file* in *any subfolder* and
each file can contain *any decorated function* (handlers) with one limitation: within a single module (file) you must each file can contain *any decorated function* (handlers) with one limitation: within a single module (file) you must
use different names for each decorated function. use different names for each decorated function.
The second thing is telling Pyrogram where to look for your plugins: you can either use the *config.ini* file or The second thing is telling Pyrogram where to look for your plugins: you can either use the *config.ini* file or
the Client parameter "plugins"; the *root* value must match the name of your plugins folder. Your Pyrogram Client the Client parameter "plugins"; the *root* value must match the name of your plugins root folder. Your Pyrogram Client
instance will **automatically** scan the folder upon starting to search for valid handlers and register them for you. instance will **automatically** scan the folder upon starting to search for valid handlers and register them for you.
Then you'll notice you can now use decorators. That's right, you can apply the usual decorators to your callback Then you'll notice you can now use decorators. That's right, you can apply the usual decorators to your callback
@ -161,7 +160,7 @@ found inside each module will be, instead, loaded in the order they are defined,
This default loading behaviour is usually enough, but sometimes you want to have more control on what to include (or This default loading behaviour is usually enough, but sometimes you want to have more control on what to include (or
exclude) and in which exact order to load plugins. The way to do this is to make use of ``include`` and ``exclude`` exclude) and in which exact order to load plugins. The way to do this is to make use of ``include`` and ``exclude``
keys, either in the *config.ini* file or in the dictionary passed as Client argument. Here's how they work: directives, either in the *config.ini* file or in the dictionary passed as Client argument. Here's how they work:
- If both ``include`` and ``exclude`` are omitted, all plugins are loaded as described above. - If both ``include`` and ``exclude`` are omitted, all plugins are loaded as described above.
- If ``include`` is given, only the specified plugins will be loaded, in the order they are passed. - If ``include`` is given, only the specified plugins will be loaded, in the order they are passed.
@ -214,9 +213,7 @@ also organized in subfolders:
.. code-block:: python .. code-block:: python
plugins = dict( plugins = dict(root="plugins")
root="plugins"
)
Client("my_account", plugins=plugins).run() Client("my_account", plugins=plugins).run()
@ -293,7 +290,7 @@ Load/Unload Plugins at Runtime
In the `previous section <#specifying-the-plugins-to-include>`_ we've explained how to specify which plugins to load and In the `previous section <#specifying-the-plugins-to-include>`_ we've explained how to specify which plugins to load and
which to ignore before your Client starts. Here we'll show, instead, how to unload and load again a previously which to ignore before your Client starts. Here we'll show, instead, how to unload and load again a previously
registered plugins at runtime. registered plugin at runtime.
Each function decorated with the usual ``on_message`` decorator (or any other decorator that deals with Telegram updates Each function decorated with the usual ``on_message`` decorator (or any other decorator that deals with Telegram updates
) will be modified in such a way that, when you reference them later on, they will be actually pointing to a tuple of ) will be modified in such a way that, when you reference them later on, they will be actually pointing to a tuple of
@ -314,14 +311,14 @@ attribute. Here's an example:
- Printing ``echo`` will show something like ``(<MessageHandler object at 0x10e3abc50>, 0)``. - Printing ``echo`` will show something like ``(<MessageHandler object at 0x10e3abc50>, 0)``.
- Printing ``echo[0].callback``, that is, the *callback* attribute of the first eleent of the tuple, which is an - Printing ``echo[0].callback``, that is, the *callback* attribute of the first element of the tuple, which is an
Handler, will reveal the actual callback ``<function echo at 0x10e3b6598>``. Handler, will reveal the actual callback ``<function echo at 0x10e3b6598>``.
Unloading Unloading
^^^^^^^^^ ^^^^^^^^^
In order to unload a plugin, or any other handler, all you need to do is obtain a reference to it (by importing the In order to unload a plugin, or any other handler, all you need to do is obtain a reference to it by importing the
relevant module) and call :meth:`remove_handler <pyrogram.Client.remove_handler>` Client's method with your function relevant module and call :meth:`remove_handler() <pyrogram.Client.remove_handler>` Client's method with your function
name preceded by the star ``*`` operator as argument. Example: name preceded by the star ``*`` operator as argument. Example:
- ``main.py`` - ``main.py``
@ -346,7 +343,7 @@ Loading
^^^^^^^ ^^^^^^^
Similarly to the unloading process, in order to load again a previously unloaded plugin you do the same, but this time Similarly to the unloading process, in order to load again a previously unloaded plugin you do the same, but this time
using :meth:`add_handler <pyrogram.Client.add_handler>` instead. Example: using :meth:`add_handler() <pyrogram.Client.add_handler>` instead. Example:
- ``main.py`` - ``main.py``

View File

@ -1,11 +1,13 @@
Update Handling Update Handling
=============== ===============
Updates are events that happen in your Telegram account (incoming messages, new channel posts, new members join, ...) Let's now dive right into the core of the framework.
and can be handled by registering one or more callback functions in your app by using `Handlers <../pyrogram/Handlers.html>`_.
To put it simply, whenever an update is received from Telegram it will be dispatched and your previously defined callback Updates are events that happen in your Telegram account (incoming messages, new channel posts, new members join, ...)
function(s) matching it will be called back with the update itself as argument. and are handled by registering one or more callback functions in your app using `Handlers <../pyrogram/Handlers.html>`_.
Each handler deals with a specific event and once a matching update arrives from Telegram, your registered callback
function will be called.
Registering an Handler Registering an Handler
---------------------- ----------------------
@ -15,13 +17,34 @@ To explain how handlers work let's have a look at the most used one, the
updates coming from all around your chats. Every other handler shares the same setup logic; you should not have troubles 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. settings them up once you learn from this section.
Using add_handler()
-------------------
The :meth:`add_handler() <pyrogram.Client.add_handler>` method takes any handler instance that wraps around your defined
callback function and registers it in your Client. Here's a full example that prints out the content of a message as
soon as it arrives:
.. code-block:: python
from pyrogram import Client, MessageHandler
def my_function(client, message):
print(message)
app = Client("my_account")
my_handler = MessageHandler(my_function)
app.add_handler(my_handler)
app.run()
Using Decorators Using Decorators
---------------- ----------------
The easiest and nicest way to register a MessageHandler is by decorating your function with the A much nicer way to register a MessageHandler is by decorating your callback function with the
:meth:`on_message() <pyrogram.Client.on_message>` decorator. Here's a full example that prints out the content :meth:`on_message() <pyrogram.Client.on_message>` decorator, which will still make use of add_handler() under the hood.
of a message as soon as it arrives.
.. code-block:: python .. code-block:: python
@ -37,23 +60,13 @@ of a message as soon as it arrives.
app.run() app.run()
Using add_handler()
-------------------
If you prefer not to use decorators for any reason, there is an alternative way for registering Handlers. .. note::
This is useful, for example, when you want to keep your callback functions in separate files.
.. code-block:: python Due to how these decorators work in Pyrogram, they will wrap your defined callback function in a tuple consisting of
``(handler, group)``; this will be the value held by your function identifier (e.g.: *my_function* from the example
above).
from pyrogram import Client, MessageHandler In case, for some reason, you want to get your own function back after it has been decorated, you need to access
``my_function[0].callback``, that is, the *callback* field of the *handler* object which is the first element in the
tuple.
def my_handler(client, message):
print(message)
app = Client("my_account")
app.add_handler(MessageHandler(my_handler))
app.run()

View File

@ -1,17 +1,19 @@
Using Filters Using Filters
============= =============
For a finer grained control over what kind of messages will be allowed or not in your callback functions, you can use So far we've seen how to register a callback function that executes every time a specific update comes from the server,
:class:`Filters <pyrogram.Filters>`. but there's much more than that to come.
.. note:: Here we'll discuss about :class:`Filters <pyrogram.Filters>`. Filters enable a fine-grain control over what kind of
This page makes use of Handlers to show you how to handle updates. updates are allowed or not to be passed in your callback functions, based on their inner details.
Learn more at `Update Handling <UpdateHandling.html>`_.
Let's start right away with a simple example:
- This example will show you how to **only** handle messages containing an :obj:`Audio <pyrogram.Audio>` object and - This example will show you how to **only** handle messages containing an :obj:`Audio <pyrogram.Audio>` object and
ignore any other message: ignore any other message. Filters are passed as the first argument of the decorator:
.. code-block:: python .. code-block:: python
:emphasize-lines: 4
from pyrogram import Filters from pyrogram import Filters
@ -20,9 +22,10 @@ For a finer grained control over what kind of messages will be allowed or not in
def my_handler(client, message): def my_handler(client, message):
print(message) print(message)
- or, without decorators: - or, without decorators. Here filters are passed as the second argument of the handler constructor:
.. code-block:: python .. code-block:: python
:emphasize-lines: 8
from pyrogram import Filters, MessageHandler from pyrogram import Filters, MessageHandler
@ -37,7 +40,7 @@ Combining Filters
----------------- -----------------
Filters can also be used in a more advanced way by inverting and combining more filters together using bitwise Filters can also be used in a more advanced way by inverting and combining more filters together using bitwise
operators: operators ``~``, ``&`` and ``|``:
- Use ``~`` to invert a filter (behaves like the ``not`` operator). - Use ``~`` to invert a filter (behaves like the ``not`` operator).
- Use ``&`` and ``|`` to merge two filters (behave like ``and``, ``or`` operators respectively). - Use ``&`` and ``|`` to merge two filters (behave like ``and``, ``or`` operators respectively).
@ -74,7 +77,7 @@ can also accept arguments:
def my_handler(client, message): def my_handler(client, message):
print(message) print(message)
- Message is a **text** message matching the given **regex** pattern. - Message is a **text** message or a media **caption** matching the given **regex** pattern.
.. code-block:: python .. code-block:: python
@ -104,17 +107,17 @@ Custom Filters
-------------- --------------
Pyrogram already provides lots of built-in :class:`Filters <pyrogram.Filters>` to work with, but in case you can't find 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 a specific one for your needs or want to build a custom filter by yourself (to be used in a different kind of handler,
example) you can use :meth:`Filters.create() <pyrogram.Filters.create>`. for example) you can use :meth:`Filters.create() <pyrogram.Filters.create>`.
.. note:: .. note::
At the moment, the built-in filters are intended to be used with the :obj:`MessageHandler <pyrogram.MessageHandler>` At the moment, the built-in filters are intended to be used with the :obj:`MessageHandler <pyrogram.MessageHandler>`
only. only.
An example to demonstrate how custom filters work is to show how to create and use one for the 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; :obj:`CallbackQueryHandler <pyrogram.CallbackQueryHandler>`. Note that callback queries updates are only received by
create and `authorize your bot <../start/Setup.html#bot-authorization>`_, then send a message with an inline keyboard to bots; create and `authorize your bot <../start/Setup.html#bot-authorization>`_, then send a message with an inline
yourself. This allows you to test your filter by pressing the inline button: keyboard to yourself. This allows you to test your filter by pressing the inline button:
.. code-block:: python .. code-block:: python
@ -133,26 +136,27 @@ Basic Filters
For this basic filter we will be using only the first two parameters of :meth:`Filters.create() <pyrogram.Filters.create>`. 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 The code below creates a simple filter for hardcoded, static callback data. This filter will only allow callback queries
containing "pyrogram" as data: containing "Pyrogram" as data, that is, the function *func* you pass returns True in case the callback query data
equals to ``b"Pyrogram"``.
.. code-block:: python .. code-block:: python
hardcoded_data = Filters.create( static_data = Filters.create(
name="HardcodedData", name="StaticdData",
func=lambda filter, callback_query: callback_query.data == b"pyrogram" func=lambda flt, callback_query: callback_query.data == b"Pyrogram"
) )
The ``lambda`` operator in python is used to create small anonymous functions and is perfect for this example, the same The ``lambda`` operator in python is used to create small anonymous functions and is perfect for this example, the same
could be achieved with a normal function, but we don't really need it as it makes sense only inside the filter itself: could be achieved with a normal function, but we don't really need it as it makes sense only inside the filter's scope:
.. code-block:: python .. code-block:: python
def func(filter, callback_query): def func(flt, callback_query):
return callback_query.data == b"pyrogram" return callback_query.data == b"Pyrogram"
hardcoded_data = Filters.create( static_data = Filters.create(
name="HardcodedData", name="StaticData",
func=func func=func
) )
@ -160,14 +164,14 @@ The filter usage remains the same:
.. code-block:: python .. code-block:: python
@app.on_callback_query(hardcoded_data) @app.on_callback_query(static_data)
def pyrogram_data(client, callback_query): def pyrogram_data(client, callback_query):
client.answer_callback_query(callback_query.id, "it works!") client.answer_callback_query(callback_query.id, "it works!")
Filters with Arguments Filters with Arguments
^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^
A much cooler filter would be one that accepts "pyrogram" or any other data as argument at usage time. A much cooler filter would be one that accepts "Pyrogram" or any other data as argument at usage time.
A dynamic filter like this will make use of the third parameter of :meth:`Filters.create() <pyrogram.Filters.create>`. 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: This is how a dynamic custom filter looks like:
@ -177,7 +181,7 @@ This is how a dynamic custom filter looks like:
def dynamic_data(data): def dynamic_data(data):
return Filters.create( return Filters.create(
name="DynamicData", name="DynamicData",
func=lambda filter, callback_query: filter.data == callback_query.data, func=lambda flt, callback_query: flt.data == callback_query.data,
data=data # "data" kwarg is accessed with "filter.data" data=data # "data" kwarg is accessed with "filter.data"
) )
@ -185,6 +189,6 @@ And its usage:
.. code-block:: python .. code-block:: python
@app.on_callback_query(dynamic_data(b"pyrogram")) @app.on_callback_query(dynamic_data(b"Pyrogram"))
def pyrogram_data(client, callback_query): def pyrogram_data(client, callback_query):
client.answer_callback_query(callback_query.id, "it works!") client.answer_callback_query(callback_query.id, "it works!")

View File

@ -1,11 +1,11 @@
Installation Installation
============ ============
Being a Python library, Pyrogram requires Python to be installed in your system. Being a Python library, **Pyrogram** requires Python to be installed in your system.
We recommend using the latest version of Python 3 and pip. 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 - Get **Python 3** from https://www.python.org/downloads/ (or with your package manager)
by following the instructions at https://pip.pypa.io/en/latest/installing/. - Get **pip** by following the instructions at https://pip.pypa.io/en/latest/installing/.
.. important:: .. important::
@ -29,8 +29,12 @@ Install Pyrogram
Bleeding Edge Bleeding Edge
------------- -------------
If you want the latest development version of Pyrogram, you can install it straight from the develop_ Things are constantly evolving in Pyrogram, although new releases are published only when enough changes are added,
branch using this command (note "develop.zip" in the link): but this doesn't mean you can't try new features right now!
In case you would like to try out the latest Pyrogram features and additions, the `GitHub repo`_ is always kept updated
with new changes; you can install the development version straight from the ``develop`` branch using this command
(note "develop.zip" in the link):
.. code-block:: text .. code-block:: text
@ -39,10 +43,10 @@ branch using this command (note "develop.zip" in the link):
Asynchronous Asynchronous
------------ ------------
Pyrogram heavily depends on IO-bound network code (it's a cloud-based messaging client library after all), and here's Pyrogram heavily depends on IO-bound network code (it's a cloud-based messaging framework after all), and here's
where asyncio shines the most by providing extra performance while running on a single OS-level thread only. where asyncio shines the most by providing extra performance while running on a single OS-level thread only.
**A fully asynchronous variant of Pyrogram is therefore available** (Python 3.5+ required). **A fully asynchronous variant of Pyrogram is therefore available** (Python 3.5.3+ required).
Use this command to install (note "asyncio.zip" in the link): Use this command to install (note "asyncio.zip" in the link):
.. code-block:: text .. code-block:: text
@ -82,7 +86,7 @@ If no error shows up you are good to go.
>>> import pyrogram >>> import pyrogram
>>> pyrogram.__version__ >>> pyrogram.__version__
'0.11.0' '0.12.0'
.. _TgCrypto: https://docs.pyrogram.ml/resources/TgCrypto .. _TgCrypto: https://docs.pyrogram.ml/resources/TgCrypto
.. _develop: http://github.com/pyrogram/pyrogram .. _`Github repo`: http://github.com/pyrogram/pyrogram

View File

@ -15,23 +15,22 @@ If you already have one you can skip this step, otherwise:
#. Fill out the form to register a new Telegram application. #. Fill out the form to register a new Telegram application.
#. Done. The API key consists of two parts: **App api_id** and **App api_hash**. #. Done. The API key consists of two parts: **App api_id** and **App api_hash**.
.. important:: .. important::
This API key is personal and should be kept secret. This API key is personal and must be kept secret.
Configuration Configuration
------------- -------------
The API key obtained in the `previous step <#api-keys>`_ defines a token for your application allowing you to access The API key obtained in the `previous step <#api-keys>`_ defines a token for your application allowing you to access
the Telegram database using the MTProto API — **it is therefore required for all authorizations of both Users and Bots**. the Telegram database using the MTProto API — **it is therefore required for all authorizations of both users and bots**.
Having it handy, it's time to configure your Pyrogram project. There are two ways to do so, and you can choose what 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: fits better for you:
- Create a new ``config.ini`` file at the root of your working directory, copy-paste the following and replace the - Create a new ``config.ini`` file at the root of your working directory, copy-paste the following and replace the
**api_id** and **api_hash** values with your own. This is the preferred method because allows you **api_id** and **api_hash** values with your own. This is the preferred method because allows you to keep your
to keep your credentials out of your code without having to deal with how to load them: credentials out of your code without having to deal with how to load them:
.. code-block:: ini .. code-block:: ini
@ -39,8 +38,8 @@ fits better for you:
api_id = 12345 api_id = 12345
api_hash = 0123456789abcdef0123456789abcdef api_hash = 0123456789abcdef0123456789abcdef
- Alternatively, you can pass your API key to Pyrogram by simply using the *api_id* and *api_hash* - Alternatively, you can pass your API key to Pyrogram by simply using the *api_id* and *api_hash* parameters of the
parameters of the Client class. This way you can have full control on how to store and load your credentials: Client class. This way you can have full control on how to store and load your credentials:
.. code-block:: python .. code-block:: python
@ -54,16 +53,16 @@ fits better for you:
.. note:: .. note::
The examples below assume you have created a ``config.ini`` file, thus they won't show the *api_id* From now on, the code snippets assume you are using the ``config.ini`` file, thus they won't show the *api_id* and
and *api_hash* parameters usage. *api_hash* parameters usage to keep them as clean as possible.
User Authorization User Authorization
------------------ ------------------
In order to use the API, Telegram requires that Users be authorized via their phone numbers. In order to use the API, Telegram requires that users be authorized via their phone numbers.
Pyrogram automatically manages this access, all you need to do is create an instance of Pyrogram automatically manages this access, all you need to do is create an instance of the
the :class:`Client <pyrogram.Client>` class by passing to it a ``session_name`` of your choice :class:`Client <pyrogram.Client>` class by passing to it a ``session_name`` of your choice (e.g.: "my_account") and call
(e.g.: "my_account") and call the :meth:`run() <pyrogram.Client.run>` method: the :meth:`run() <pyrogram.Client.run>` method:
.. code-block:: python .. code-block:: python
@ -80,30 +79,40 @@ and the **phone code** you will receive:
Enter phone number: +39********** Enter phone number: +39**********
Is "+39**********" correct? (y/n): y Is "+39**********" correct? (y/n): y
Enter phone code: 32768 Enter phone code: 32768
Logged in successfully as Dan
After successfully authorizing yourself, a new file called ``my_account.session`` will be created allowing After successfully authorizing yourself, a new file called ``my_account.session`` will be created allowing Pyrogram
Pyrogram executing API calls with your identity. This file will be loaded again when you restart your app, executing API calls with your identity. This file will be loaded again when you restart your app, and as long as you
and as long as you keep the session alive, Pyrogram won't ask you again to enter your phone number. keep the session alive, Pyrogram won't ask you again to enter your phone number.
.. important:: .. important::
Your ``*.session`` files are personal and must be kept secret. Your ``*.session`` files are personal and must be kept secret.
.. note::
The code above does nothing except asking for credentials and keeping the client online, hit ``CTRL+C`` now to stop
your application and keep reading.
Bot Authorization Bot Authorization
----------------- -----------------
Bots are a special kind of users and are authorized via their tokens (instead of phone numbers), which are created by Bots are a special kind of users that are authorized via their tokens (instead of phone numbers), which are created by
BotFather_. Bot tokens replace the Users' phone numbers only — you still need to 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. `configure a Telegram API key <#configuration>`_ with Pyrogram, even when using bots.
The authorization process is automatically managed. All you need to do is pass the bot token as ``session_name``. The authorization process is automatically managed. All you need to do is choose a ``session_name`` (can be anything,
The session file will be named after the Bot user_id, which is ``123456.session`` for the example below. usually your bot username) and pass your bot token using the ``bot_token`` parameter. The session file will be named
after the session name, which will be ``pyrogrambot.session`` for the example below.
.. code-block:: python .. code-block:: python
from pyrogram import Client from pyrogram import Client
app = Client("123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11") app = Client(
"pyrogrambot",
bot_token="123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11"
)
app.run() app.run()
.. _installed Pyrogram: Installation.html .. _installed Pyrogram: Installation.html

View File

@ -1,7 +1,7 @@
Usage Usage
===== =====
Having your `project set up`_ and your account authorized_, it's time to play with the API. Let's start! Having your `project set up`_ and your account authorized_, it's time to start playing with the API. Let's start!
High-level API High-level API
-------------- --------------
@ -11,34 +11,36 @@ named after the `Telegram Bot API`_.
Here's a simple example: Here's a simple example:
.. code-block:: python .. code-block:: python
from pyrogram import Client from pyrogram import Client
app = Client("my_account") app = Client("my_account")
app.start() app.start()
print(app.get_me()) print(app.get_me())
app.send_message("me", "Hi there! I'm using **Pyrogram**") app.send_message("me", "Hi there! I'm using **Pyrogram**")
app.send_location("me", 51.500729, -0.124583) app.send_location("me", 51.500729, -0.124583)
app.send_sticker("me", "CAADBAADyg4AAvLQYAEYD4F7vcZ43AI")
app.stop() app.stop()
You can also use Pyrogram in a context manager with the ``with`` statement. The Client will automatically You can also use Pyrogram in a context manager with the ``with`` statement. The Client will automatically
:meth:`start <pyrogram.Client.start>` and :meth:`stop <pyrogram.Client.stop>` gracefully, even in case of unhandled :meth:`start <pyrogram.Client.start>` and :meth:`stop <pyrogram.Client.stop>` gracefully, even in case of unhandled
exceptions in your code: exceptions in your code:
.. code-block:: python .. code-block:: python
from pyrogram import Client from pyrogram import Client
app = Client("my_account") app = Client("my_account")
with app: with app:
print(app.get_me()) print(app.get_me())
app.send_message("me", "Hi there! I'm using **Pyrogram**") app.send_message("me", "Hi there! I'm using **Pyrogram**")
app.send_location("me", 51.500729, -0.124583) app.send_location("me", 51.500729, -0.124583)
app.send_sticker("me", "CAADBAADyg4AAvLQYAEYD4F7vcZ43AI")
More examples on `GitHub <https://github.com/pyrogram/pyrogram/tree/develop/examples>`_. More examples on `GitHub <https://github.com/pyrogram/pyrogram/tree/develop/examples>`_.
@ -46,4 +48,4 @@ More examples on `GitHub <https://github.com/pyrogram/pyrogram/tree/develop/exam
.. _authorized: Setup.html#user-authorization .. _authorized: Setup.html#user-authorization
.. _Telegram Bot API: https://core.telegram.org/bots/api .. _Telegram Bot API: https://core.telegram.org/bots/api
.. _methods: ../pyrogram/Client.html#messages .. _methods: ../pyrogram/Client.html#messages
.. _types: ../pyrogram/Types.html .. _types: ../pyrogram/Types.html

View File

@ -10,13 +10,14 @@ can be freely used as basic building blocks for your own applications without wo
Example | Description Example | Description
---: | :--- ---: | :---
[**hello**](hello.py) | Demonstration of basic API usage [**hello_world**](hello_world.py) | Demonstration of basic API usage
[**echo**](echo.py) | Reply to every private text message [**echobot**](echobot.py) | Echo every private text message
[**welcome**](welcome.py) | The Welcome Bot in [@PyrogramChat](https://t.me/pyrogramchat) [**welcome**](welcome.py) | The Welcome Bot in [@PyrogramChat](https://t.me/pyrogramchat)
[**history**](history.py) | Get the full message history of a chat [**history**](history.py) | Get the full message history of a chat
[**chat_members**](chat_members.py) | Get all the members of a chat [**chat_members**](chat_members.py) | Get all the members of a chat
[**dialogs**](dialogs.py) | Get all of your dialog chats [**dialogs**](dialogs.py) | Get all of your dialog chats
[**inline_bots**](inline_bots.py) | Query an inline bot and send a result to a chat [**using_inline_bots**](using_inline_bots.py) | Query an inline bot (as user) and send a result to a chat
[**keyboards**](keyboards.py) | Send normal and inline keyboards using regular bots [**keyboards**](keyboards.py) | Send normal and inline keyboards using regular bots
[**callback_queries**](callback_queries.py) | Handle queries coming from inline button presses [**callback_queries**](callback_queries.py) | Handle queries coming from inline button presses
[**inline_queries**](inline_queries.py) | Handle inline queries
[**raw_updates**](raw_updates.py) | Handle raw updates (old, should be avoided) [**raw_updates**](raw_updates.py) | Handle raw updates (old, should be avoided)

View File

@ -5,12 +5,12 @@ It uses the @on_callback_query decorator to register a CallbackQueryHandler.
from pyrogram import Client from pyrogram import Client
app = Client("123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11") app = Client("my_bot", bot_token="123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11")
@app.on_callback_query() @app.on_callback_query()
def answer(client, callback_query): def answer(client, callback_query):
callback_query.answer('Button contains: "{}"'.format(callback_query.data), show_alert=True) callback_query.answer("Button contains: '{}'".format(callback_query.data), show_alert=True)
app.run() # Automatically start() and idle() app.run() # Automatically start() and idle()

View File

@ -2,7 +2,7 @@
from pyrogram import Client from pyrogram import Client
app = Client("my_count") app = Client("my_account")
target = "pyrogramchat" # Target channel/supergroup target = "pyrogramchat" # Target channel/supergroup
with app: with app:

View File

@ -0,0 +1,54 @@
"""This example shows how to handle inline queries.
Two results are generated when users invoke the bot inline mode, e.g.: @pyrogrambot hi.
It uses the @on_inline_query decorator to register an InlineQueryHandler.
"""
from uuid import uuid4
from pyrogram import (
Client, InlineQueryResultArticle, InputTextMessageContent, InlineKeyboardMarkup, InlineKeyboardButton
)
app = Client("my_bot", bot_token="123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11")
@app.on_inline_query()
def answer(client, inline_query):
inline_query.answer(
results=[
InlineQueryResultArticle(
id=uuid4(),
title="Installation",
input_message_content=InputTextMessageContent(
"Here's how to install **Pyrogram**"
),
url="https://docs.pyrogram.ml/start/Installation",
description="How to install Pyrogram",
thumb_url="https://i.imgur.com/JyxrStE.png",
reply_markup=InlineKeyboardMarkup(
[
[InlineKeyboardButton("Open website", url="https://docs.pyrogram.ml/start/Installation")]
]
)
),
InlineQueryResultArticle(
id=uuid4(),
title="Usage",
input_message_content=InputTextMessageContent(
"Here's how to use **Pyrogram**"
),
url="https://docs.pyrogram.ml/start/Usage",
description="How to use Pyrogram",
thumb_url="https://i.imgur.com/JyxrStE.png",
reply_markup=InlineKeyboardMarkup(
[
[InlineKeyboardButton("Open website", url="https://docs.pyrogram.ml/start/Usage")]
]
)
)
],
cache_time=1
)
app.run() # Automatically start() and idle()

View File

@ -10,7 +10,7 @@ like send_audio(), send_document(), send_location(), etc...
from pyrogram import Client, ReplyKeyboardMarkup, InlineKeyboardMarkup, InlineKeyboardButton from pyrogram import Client, ReplyKeyboardMarkup, InlineKeyboardMarkup, InlineKeyboardButton
# Create a client using your bot token # Create a client using your bot token
app = Client("123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11") app = Client("my_bot", bot_token="123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11")
with app: with app:
app.send_message( app.send_message(
@ -33,19 +33,17 @@ with app:
reply_markup=InlineKeyboardMarkup( reply_markup=InlineKeyboardMarkup(
[ [
[ # First row [ # First row
InlineKeyboardButton( # Generates a callback query when pressed InlineKeyboardButton( # Generates a callback query when pressed
"Button", "Button",
callback_data=b"data" callback_data=b"data" # Note how callback_data must be bytes
), # Note how callback_data must be bytes ),
InlineKeyboardButton( # Opens a web URL InlineKeyboardButton( # Opens a web URL
"URL", "URL",
url="https://docs.pyrogram.ml" url="https://docs.pyrogram.ml"
), ),
], ],
[ # Second row [ # Second row
# Opens the inline interface InlineKeyboardButton( # Opens the inline interface
InlineKeyboardButton(
"Choose chat", "Choose chat",
switch_inline_query="pyrogram" switch_inline_query="pyrogram"
), ),

View File

@ -24,25 +24,13 @@ if sys.version_info[:3] in [(3, 5, 0), (3, 5, 1), (3, 5, 2)]:
# Monkey patch the standard "typing" module because Python versions from 3.5.0 to 3.5.2 have a broken one. # Monkey patch the standard "typing" module because Python versions from 3.5.0 to 3.5.2 have a broken one.
sys.modules["typing"] = typing sys.modules["typing"] = typing
__copyright__ = "Copyright (C) 2017-2019 Dan Tès <https://github.com/delivrance>".replace( __version__ = "0.12.0"
"\xe8",
"e" if sys.getfilesystemencoding() != "utf-8" else "\xe8"
)
__license__ = "GNU Lesser General Public License v3 or later (LGPLv3+)" __license__ = "GNU Lesser General Public License v3 or later (LGPLv3+)"
__version__ = "0.11.1.develop" __copyright__ = "Copyright (C) 2017-2019 Dan Tès <https://github.com/delivrance>".replace(
"\xe8", "e" if sys.getfilesystemencoding() != "utf-8" else "\xe8"
)
from .api.errors import Error from .errors import RPCError
from .client.types import ( from .client import *
Audio, Chat, ChatMember, ChatMembers, ChatPhoto, Contact, Document, InputMediaPhoto, from .client.handlers import *
InputMediaVideo, InputMediaDocument, InputMediaAudio, InputMediaAnimation, InputPhoneContact, from .client.types import *
Location, Message, MessageEntity, Dialog, Dialogs, Photo, PhotoSize, Sticker, User, UserStatus,
UserProfilePhotos, Venue, Animation, Video, VideoNote, Voice, CallbackQuery, Messages, ForceReply,
InlineKeyboardButton, InlineKeyboardMarkup, KeyboardButton, ReplyKeyboardMarkup, ReplyKeyboardRemove,
Poll, PollOption, ChatPreview, StopPropagation, ContinuePropagation, Game, CallbackGame, GameHighScore,
GameHighScores
)
from .client import (
Client, ChatAction, ParseMode, Emoji,
MessageHandler, DeletedMessagesHandler, CallbackQueryHandler,
RawUpdateHandler, DisconnectHandler, UserStatusHandler, Filters
)

View File

@ -26,6 +26,10 @@ from .primitives import Int, Long
class FutureSalt(Object): class FutureSalt(Object):
ID = 0x0949d9dc ID = 0x0949d9dc
__slots__ = ["valid_since", "valid_until", "salt"]
QUALNAME = "FutureSalt"
def __init__(self, valid_since: int or datetime, valid_until: int or datetime, salt: int): def __init__(self, valid_since: int or datetime, valid_until: int or datetime, salt: int):
self.valid_since = valid_since self.valid_since = valid_since
self.valid_until = valid_until self.valid_until = valid_until

View File

@ -27,6 +27,10 @@ from .primitives import Int, Long
class FutureSalts(Object): class FutureSalts(Object):
ID = 0xae500895 ID = 0xae500895
__slots__ = ["req_msg_id", "now", "salts"]
QUALNAME = "FutureSalts"
def __init__(self, req_msg_id: int, now: int or datetime, salts: list): def __init__(self, req_msg_id: int, now: int or datetime, salts: list):
self.req_msg_id = req_msg_id self.req_msg_id = req_msg_id
self.now = now self.now = now

View File

@ -26,6 +26,10 @@ from .primitives import Int, Bytes
class GzipPacked(Object): class GzipPacked(Object):
ID = 0x3072cfa1 ID = 0x3072cfa1
__slots__ = ["packed_data"]
QUALNAME = "GzipPacked"
def __init__(self, packed_data: Object): def __init__(self, packed_data: Object):
self.packed_data = packed_data self.packed_data = packed_data

View File

@ -25,6 +25,10 @@ from .primitives import Int, Long
class Message(Object): class Message(Object):
ID = 0x5bb8e511 # hex(crc32(b"message msg_id:long seqno:int bytes:int body:Object = Message")) ID = 0x5bb8e511 # hex(crc32(b"message msg_id:long seqno:int bytes:int body:Object = Message"))
__slots__ = ["msg_id", "seq_no", "length", "body"]
QUALNAME = "Message"
def __init__(self, body: Object, msg_id: int, seq_no: int, length: int): def __init__(self, body: Object, msg_id: int, seq_no: int, length: int):
self.msg_id = msg_id self.msg_id = msg_id
self.seq_no = seq_no self.seq_no = seq_no

View File

@ -26,6 +26,10 @@ from .primitives import Int
class MsgContainer(Object): class MsgContainer(Object):
ID = 0x73f1f8dc ID = 0x73f1f8dc
__slots__ = ["messages"]
QUALNAME = "MsgContainer"
def __init__(self, messages: list): def __init__(self, messages: list):
self.messages = messages self.messages = messages

View File

@ -19,14 +19,16 @@
from collections import OrderedDict from collections import OrderedDict
from datetime import datetime from datetime import datetime
from io import BytesIO from io import BytesIO
from json import JSONEncoder, dumps from json import dumps
from ..all import objects
class Object: class Object:
all = {} all = {}
__slots__ = []
QUALNAME = "Base"
@staticmethod @staticmethod
def read(b: BytesIO, *args): def read(b: BytesIO, *args):
return Object.all[int.from_bytes(b.read(4), "little")].read(b, *args) return Object.all[int.from_bytes(b.read(4), "little")].read(b, *args)
@ -35,20 +37,11 @@ class Object:
pass pass
def __str__(self) -> str: def __str__(self) -> str:
return dumps(self, cls=Encoder, indent=4) return dumps(self, indent=4, default=default, ensure_ascii=False)
def __bool__(self) -> bool:
return True
def __eq__(self, other) -> bool:
return self.__dict__ == other.__dict__
def __len__(self) -> int: def __len__(self) -> int:
return len(self.write()) return len(self.write())
def __call__(self):
pass
def __getitem__(self, item): def __getitem__(self, item):
return getattr(self, item) return getattr(self, item)
@ -62,29 +55,18 @@ def remove_none(obj):
return obj return obj
class Encoder(JSONEncoder): def default(o: "Object"):
def default(self, o: Object): try:
try: content = {i: getattr(o, i) for i in o.__slots__}
content = o.__dict__
except AttributeError:
if isinstance(o, datetime):
return o.strftime("%d-%b-%Y %H:%M:%S")
else:
return repr(o)
name = o.__class__.__name__ return remove_none(
o = objects.get(getattr(o, "ID", None), None) OrderedDict(
[("_", o.QUALNAME)]
if o is not None: + [i for i in content.items()]
if o.startswith("pyrogram.client"): )
r = remove_none(OrderedDict([("_", "pyrogram:" + name)] + [i for i in content.items()])) )
r.pop("_client", None) except AttributeError:
if isinstance(o, datetime):
return r return o.strftime("%d-%b-%Y %H:%M:%S")
else:
return OrderedDict(
[("_", o.replace("pyrogram.api.types.", "telegram:"))]
+ [i for i in content.items()]
)
else: else:
return None return repr(o)

View File

@ -19,8 +19,7 @@
from .client import Client from .client import Client
from .ext import BaseClient, ChatAction, Emoji, ParseMode from .ext import BaseClient, ChatAction, Emoji, ParseMode
from .filters import Filters from .filters import Filters
from .handlers import (
MessageHandler, DeletedMessagesHandler, __all__ = [
CallbackQueryHandler, RawUpdateHandler, "Client", "BaseClient", "ChatAction", "Emoji", "ParseMode", "Filters",
DisconnectHandler, UserStatusHandler ]
)

View File

@ -39,7 +39,11 @@ from typing import Union, List, Type
from pyrogram.api import functions, types from pyrogram.api import functions, types
from pyrogram.api.core import Object from pyrogram.api.core import Object
from pyrogram.api.errors import ( from pyrogram.client.handlers import DisconnectHandler
from pyrogram.client.handlers.handler import Handler
from pyrogram.client.methods.password.utils import compute_check
from pyrogram.crypto import AES
from pyrogram.errors import (
PhoneMigrate, NetworkMigrate, PhoneNumberInvalid, PhoneMigrate, NetworkMigrate, PhoneNumberInvalid,
PhoneNumberUnoccupied, PhoneCodeInvalid, PhoneCodeHashEmpty, PhoneNumberUnoccupied, PhoneCodeInvalid, PhoneCodeHashEmpty,
PhoneCodeExpired, PhoneCodeEmpty, SessionPasswordNeeded, PhoneCodeExpired, PhoneCodeEmpty, SessionPasswordNeeded,
@ -47,13 +51,8 @@ from pyrogram.api.errors import (
VolumeLocNotFound, UserMigrate, FileIdInvalid, ChannelPrivate, PhoneNumberOccupied, VolumeLocNotFound, UserMigrate, FileIdInvalid, ChannelPrivate, PhoneNumberOccupied,
PasswordRecoveryNa, PasswordEmpty PasswordRecoveryNa, PasswordEmpty
) )
from pyrogram.client.handlers import DisconnectHandler
from pyrogram.client.handlers.handler import Handler
from pyrogram.client.methods.password.utils import compute_check
from pyrogram.crypto import AES
from pyrogram.session import Auth, Session from pyrogram.session import Auth, Session
from .dispatcher import Dispatcher from .ext import utils, Syncer, BaseClient, Dispatcher
from .ext import utils, Syncer, BaseClient
from .methods import Methods from .methods import Methods
from .session_storage import ( from .session_storage import (
SessionDoesNotExist, SessionStorage, MemorySessionStorage, JsonSessionStorage, SessionDoesNotExist, SessionStorage, MemorySessionStorage, JsonSessionStorage,
@ -70,9 +69,10 @@ class Client(Methods, BaseClient):
Args: Args:
session_name (``str``): session_name (``str``):
Name to uniquely identify a session of either a User or a Bot, e.g.: "my_main_account". Name to uniquely identify a session of either a User or a Bot, e.g.: "my_account". This name will be used
You still can use bot token here, but it will be deprecated in next release. to save a file to disk that stores details needed for reconnecting without asking again for credentials.
Note: as long as a valid User session file exists, Pyrogram won't ask you again to input your phone number. Note for bots: You can pass a bot token here, but this usage will be deprecated in next releases.
Use *bot_token* instead.
api_id (``int``, *optional*): api_id (``int``, *optional*):
The *api_id* part of your Telegram API Key, as integer. E.g.: 12345 The *api_id* part of your Telegram API Key, as integer. E.g.: 12345
@ -182,31 +182,35 @@ class Client(Methods, BaseClient):
Defaults to False (normal session). Defaults to False (normal session).
""" """
def __init__(self, terms_of_service_displayed = False
session_name: Union[str, SessionStorage],
api_id: Union[int, str] = None, def __init__(
api_hash: str = None, self,
app_version: str = None, session_name: str,
device_model: str = None, api_id: Union[int, str] = None,
system_version: str = None, api_hash: str = None,
lang_code: str = None, app_version: str = None,
ipv6: bool = False, device_model: str = None,
proxy: dict = None, system_version: str = None,
test_mode: bool = False, lang_code: str = None,
phone_number: str = None, ipv6: bool = False,
phone_code: Union[str, callable] = None, proxy: dict = None,
password: str = None, test_mode: bool = False,
recovery_code: callable = None, phone_number: str = None,
force_sms: bool = False, phone_code: Union[str, callable] = None,
bot_token: str = None, password: str = None,
first_name: str = None, recovery_code: callable = None,
last_name: str = None, force_sms: bool = False,
workers: int = BaseClient.WORKERS, bot_token: str = None,
workdir: str = BaseClient.WORKDIR, first_name: str = None,
config_file: str = BaseClient.CONFIG_FILE, last_name: str = None,
plugins: dict = None, workers: int = BaseClient.WORKERS,
no_updates: bool = None, workdir: str = BaseClient.WORKDIR,
takeout: bool = None): config_file: str = BaseClient.CONFIG_FILE,
plugins: dict = None,
no_updates: bool = None,
takeout: bool = None
):
if isinstance(session_name, str): if isinstance(session_name, str):
if session_name == ':memory:': if session_name == ':memory:':
@ -222,6 +226,8 @@ class Client(Methods, BaseClient):
super().__init__(session_storage) super().__init__(session_storage)
super().__init__(session_storage)
self.session_name = str(session_name) # TODO: build correct session name self.session_name = str(session_name) # TODO: build correct session name
self.api_id = int(api_id) if api_id else None self.api_id = int(api_id) if api_id else None
self.api_hash = api_hash self.api_hash = api_hash
@ -277,7 +283,7 @@ class Client(Methods, BaseClient):
Requires no parameters. Requires no parameters.
Raises: Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error. :class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
``ConnectionError`` in case you try to start an already started Client. ``ConnectionError`` in case you try to start an already started Client.
""" """
if self.is_started: if self.is_started:
@ -288,10 +294,10 @@ class Client(Methods, BaseClient):
self.session_storage.is_bot = True self.session_storage.is_bot = True
self.bot_token = self.session_storage._session_name self.bot_token = self.session_storage._session_name
self.session_storage._session_name = self.session_storage._session_name.split(":")[0] self.session_storage._session_name = self.session_storage._session_name.split(":")[0]
warnings.warn('\nYou are using a bot token as session name.\n' warnings.warn('\nWARNING: You are using a bot token as session name!\n'
'It will be deprecated in next update, please use session file name to load ' 'This usage will be deprecated soon. Please use a session file name to load '
'existing sessions and bot_token argument to create new sessions.', 'an existing session and the bot_token argument to create new sessions.\n'
DeprecationWarning, stacklevel=2) 'More info: https://docs.pyrogram.ml/start/Setup#bot-authorization\n')
self.load_config() self.load_config()
self.load_session() self.load_session()
@ -309,6 +315,7 @@ class Client(Methods, BaseClient):
try: try:
if self.session_storage.user_id is None: if self.session_storage.user_id is None:
if self.bot_token is None: if self.bot_token is None:
self.is_bot = False
self.authorize_user() self.authorize_user()
else: else:
self.session_storage.is_bot = True self.session_storage.is_bot = True
@ -446,7 +453,7 @@ class Client(Methods, BaseClient):
Requires no parameters. Requires no parameters.
Raises: Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error. :class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
""" """
self.start() self.start()
self.idle() self.idle()
@ -558,9 +565,10 @@ class Client(Methods, BaseClient):
try: try:
r = self.send( r = self.send(
functions.auth.SendCode( functions.auth.SendCode(
self.phone_number, phone_number=self.phone_number,
self.api_id, api_id=self.api_id,
self.api_hash api_hash=self.api_hash,
settings=types.CodeSettings()
) )
) )
except (PhoneMigrate, NetworkMigrate) as e: except (PhoneMigrate, NetworkMigrate) as e:
@ -604,8 +612,9 @@ class Client(Methods, BaseClient):
phone_code_hash = r.phone_code_hash phone_code_hash = r.phone_code_hash
terms_of_service = r.terms_of_service terms_of_service = r.terms_of_service
if terms_of_service: if terms_of_service and not Client.terms_of_service_displayed:
print("\n" + terms_of_service.text + "\n") print("\n" + terms_of_service.text + "\n")
Client.terms_of_service_displayed = True
if self.force_sms: if self.force_sms:
self.send( self.send(
@ -640,9 +649,9 @@ class Client(Methods, BaseClient):
try: try:
r = self.send( r = self.send(
functions.auth.SignIn( functions.auth.SignIn(
self.phone_number, phone_number=self.phone_number,
phone_code_hash, phone_code_hash=phone_code_hash,
self.phone_code phone_code=self.phone_code
) )
) )
except PhoneNumberUnoccupied: except PhoneNumberUnoccupied:
@ -653,11 +662,11 @@ class Client(Methods, BaseClient):
try: try:
r = self.send( r = self.send(
functions.auth.SignUp( functions.auth.SignUp(
self.phone_number, phone_number=self.phone_number,
phone_code_hash, phone_code_hash=phone_code_hash,
self.phone_code, phone_code=self.phone_code,
self.first_name, first_name=self.first_name,
self.last_name last_name=self.last_name
) )
) )
except PhoneNumberOccupied: except PhoneNumberOccupied:
@ -751,7 +760,11 @@ class Client(Methods, BaseClient):
break break
if terms_of_service: if terms_of_service:
assert self.send(functions.help.AcceptTermsOfService(terms_of_service.id)) assert self.send(
functions.help.AcceptTermsOfService(
id=terms_of_service.id
)
)
self.password = None self.password = None
self.session_storage.user_id = r.user.id self.session_storage.user_id = r.user.id
@ -992,16 +1005,16 @@ class Client(Methods, BaseClient):
Timeout in seconds. Timeout in seconds.
Raises: Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error. :class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
""" """
if not self.is_started: if not self.is_started:
raise ConnectionError("Client has not been started") raise ConnectionError("Client has not been started")
if self.no_updates: if self.no_updates:
data = functions.InvokeWithoutUpdates(data) data = functions.InvokeWithoutUpdates(query=data)
if self.takeout_id: if self.takeout_id:
data = functions.InvokeWithTakeout(self.takeout_id, data) data = functions.InvokeWithTakeout(takeout_id=self.takeout_id, query=data)
r = self.session.send(data, retries, timeout) r = self.session.send(data, retries, timeout)
@ -1118,7 +1131,7 @@ class Client(Methods, BaseClient):
try: try:
module = import_module(module_path) module = import_module(module_path)
except ModuleNotFoundError: except ImportError:
log.warning('[LOAD] Ignoring non-existent module "{}"'.format(module_path)) log.warning('[LOAD] Ignoring non-existent module "{}"'.format(module_path))
continue continue
@ -1154,7 +1167,7 @@ class Client(Methods, BaseClient):
try: try:
module = import_module(module_path) module = import_module(module_path)
except ModuleNotFoundError: except ImportError:
log.warning('[UNLOAD] Ignoring non-existent module "{}"'.format(module_path)) log.warning('[UNLOAD] Ignoring non-existent module "{}"'.format(module_path))
continue continue
@ -1241,7 +1254,7 @@ class Client(Methods, BaseClient):
On success, the resolved peer id is returned in form of an InputPeer object. On success, the resolved peer id is returned in form of an InputPeer object.
Raises: Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error. :class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
``KeyError`` in case the peer doesn't exist in the internal database. ``KeyError`` in case the peer doesn't exist in the internal database.
""" """
try: try:
@ -1276,7 +1289,7 @@ class Client(Methods, BaseClient):
self.fetch_peers( self.fetch_peers(
self.send( self.send(
functions.users.GetUsers( functions.users.GetUsers(
id=[types.InputUser(peer_id, 0)] id=[types.InputUser(user_id=peer_id, access_hash=0)]
) )
) )
) )
@ -1284,7 +1297,7 @@ class Client(Methods, BaseClient):
if str(peer_id).startswith("-100"): if str(peer_id).startswith("-100"):
self.send( self.send(
functions.channels.GetChannels( functions.channels.GetChannels(
id=[types.InputChannel(int(str(peer_id)[4:]), 0)] id=[types.InputChannel(channel_id=int(str(peer_id)[4:]), access_hash=0)]
) )
) )
else: else:
@ -1348,7 +1361,7 @@ class Client(Methods, BaseClient):
On success, the uploaded file is returned in form of an InputFile object. On success, the uploaded file is returned in form of an InputFile object.
Raises: Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error. :class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
""" """
part_size = 512 * 1024 part_size = 512 * 1024
file_size = os.path.getsize(path) file_size = os.path.getsize(path)
@ -1591,8 +1604,8 @@ class Client(Methods, BaseClient):
hashes = session.send( hashes = session.send(
functions.upload.GetCdnFileHashes( functions.upload.GetCdnFileHashes(
r.file_token, file_token=r.file_token,
offset offset=offset
) )
) )

View File

@ -18,6 +18,7 @@
from .base_client import BaseClient from .base_client import BaseClient
from .chat_action import ChatAction from .chat_action import ChatAction
from .dispatcher import Dispatcher
from .emoji import Emoji from .emoji import Emoji
from .parse_mode import ParseMode from .parse_mode import ParseMode
from .syncer import Syncer from .syncer import Syncer

View File

@ -74,8 +74,8 @@ class BaseClient:
self.rnd_id = MsgId self.rnd_id = MsgId
self.channels_pts = {} self.channels_pts = {}
self.markdown = Markdown(self.session_storage) self.markdown = Markdown(self.session_storage, self)
self.html = HTML(self.session_storage) self.html = HTML(self.session_storage, self)
self.session = None self.session = None
self.media_sessions = {} self.media_sessions = {}
@ -122,3 +122,6 @@ class BaseClient:
def get_chat_members_count(self, *args, **kwargs): def get_chat_members_count(self, *args, **kwargs):
pass pass
def answer_inline_query(self, *args, **kwargs):
pass

View File

@ -24,7 +24,10 @@ from threading import Thread
import pyrogram import pyrogram
from pyrogram.api import types from pyrogram.api import types
from ..handlers import CallbackQueryHandler, MessageHandler, RawUpdateHandler, UserStatusHandler, DeletedMessagesHandler from ..handlers import (
CallbackQueryHandler, MessageHandler, DeletedMessagesHandler,
UserStatusHandler, RawUpdateHandler, InlineQueryHandler
)
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -73,7 +76,10 @@ class Dispatcher:
(types.UpdateUserStatus,): (types.UpdateUserStatus,):
lambda upd, usr, cht: ( lambda upd, usr, cht: (
pyrogram.UserStatus._parse(self.client, upd.status, upd.user_id), UserStatusHandler pyrogram.UserStatus._parse(self.client, upd.status, upd.user_id), UserStatusHandler
) ),
(types.UpdateBotInlineQuery,):
lambda upd, usr, cht: (pyrogram.InlineQuery._parse(self.client, upd, usr), InlineQueryHandler)
} }
self.update_parsers = {key: value for key_tuple, value in self.update_parsers.items() for key in key_tuple} self.update_parsers = {key: value for key_tuple, value in self.update_parsers.items() for key in key_tuple}

View File

@ -67,10 +67,10 @@ def get_peer_id(input_peer) -> int:
def get_input_peer(peer_id: int, access_hash: int): def get_input_peer(peer_id: int, access_hash: int):
return ( return (
types.InputPeerUser(peer_id, access_hash) if peer_id > 0 types.InputPeerUser(user_id=peer_id, access_hash=access_hash) if peer_id > 0
else types.InputPeerChannel(int(str(peer_id)[4:]), access_hash) else types.InputPeerChannel(channel_id=int(str(peer_id)[4:]), access_hash=access_hash)
if (str(peer_id).startswith("-100") and access_hash) if (str(peer_id).startswith("-100") and access_hash)
else types.InputPeerChat(-peer_id) else types.InputPeerChat(chat_id=-peer_id)
) )

View File

@ -115,7 +115,7 @@ class Filters:
voice = create("Voice", lambda _, m: bool(m.voice)) voice = create("Voice", lambda _, m: bool(m.voice))
"""Filter messages that contain :obj:`Voice <pyrogram.Voice>` note objects.""" """Filter messages that contain :obj:`Voice <pyrogram.Voice>` note objects."""
video_note = create("Voice", lambda _, m: bool(m.video_note)) video_note = create("VideoNote", lambda _, m: bool(m.video_note))
"""Filter messages that contain :obj:`VideoNote <pyrogram.VideoNote>` objects.""" """Filter messages that contain :obj:`VideoNote <pyrogram.VideoNote>` objects."""
contact = create("Contact", lambda _, m: bool(m.contact)) contact = create("Contact", lambda _, m: bool(m.contact))
@ -222,14 +222,16 @@ class Filters:
- poll""" - poll"""
@staticmethod @staticmethod
def command(command: str or list, def command(
prefix: str or list = "/", commands: str or list,
separator: str = " ", prefix: str or list = "/",
case_sensitive: bool = False): separator: str = " ",
case_sensitive: bool = False
):
"""Filter commands, i.e.: text messages starting with "/" or any other custom prefix. """Filter commands, i.e.: text messages starting with "/" or any other custom prefix.
Args: Args:
command (``str`` | ``list``): commands (``str`` | ``list``):
The command or list of commands as string the filter should look for. The command or list of commands as string the filter should look for.
Examples: "start", ["start", "help", "settings"]. When a message text containing Examples: "start", ["start", "help", "settings"]. When a message text containing
a command arrives, the command itself and its arguments will be stored in the *command* a command arrives, the command itself and its arguments will be stored in the *command*
@ -249,31 +251,25 @@ class Filters:
Examples: when True, command="Start" would trigger /Start but not /start. Examples: when True, command="Start" would trigger /Start but not /start.
""" """
def f(_, m): def func(flt, message):
if m.text: text = message.text or message.caption
for i in _.p:
if m.text.startswith(i): if text:
t = m.text.split(_.s) for p in flt.p:
c, a = t[0][len(i):], t[1:] if text.startswith(p):
c = c if _.cs else c.lower() s = text.split(flt.s)
m.command = ([c] + a) if c in _.c else None c, a = s[0][len(p):], s[1:]
c = c if flt.cs else c.lower()
message.command = ([c] + a) if c in flt.c else None
break break
return bool(m.command) return bool(message.command)
return create( commands = commands if type(commands) is list else [commands]
"Command", commands = {c if case_sensitive else c.lower() for c in commands}
f, prefixes = set(prefix) if prefix else {""}
c={command if case_sensitive
else command.lower()} return create("Command", func=func, c=commands, p=prefixes, s=separator, cs=case_sensitive)
if not isinstance(command, list)
else {c if case_sensitive
else c.lower()
for c in command},
p=set(prefix) if prefix else {""},
s=separator,
cs=case_sensitive
)
@staticmethod @staticmethod
def regex(pattern, flags: int = 0): def regex(pattern, flags: int = 0):
@ -311,21 +307,20 @@ class Filters:
def __init__(self, users: int or str or list = None): def __init__(self, users: int or str or list = None):
users = [] if users is None else users if type(users) is list else [users] users = [] if users is None else users if type(users) is list else [users]
super().__init__( super().__init__(
{"me" if i in ["me", "self"] else i.lower().strip("@") if type(i) is str else i for i in users} "me" if u in ["me", "self"]
if type(users) is list else else u.lower().strip("@") if type(u) is str
{"me" if users in ["me", "self"] else users.lower().strip("@") if type(users) is str else users} else u for u in users
) )
def __call__(self, message): def __call__(self, message):
return bool( return (message.from_user
message.from_user and (message.from_user.id in self
and (message.from_user.id in self or (message.from_user.username
or (message.from_user.username and message.from_user.username.lower() in self)
and message.from_user.username.lower() in self) or ("me" in self
or ("me" in self and message.from_user.is_self)))
and message.from_user.is_self))
)
# noinspection PyPep8Naming # noinspection PyPep8Naming
class chat(Filter, set): class chat(Filter, set):
@ -343,21 +338,21 @@ class Filters:
def __init__(self, chats: int or str or list = None): def __init__(self, chats: int or str or list = None):
chats = [] if chats is None else chats if type(chats) is list else [chats] chats = [] if chats is None else chats if type(chats) is list else [chats]
super().__init__( super().__init__(
{"me" if i in ["me", "self"] else i.lower().strip("@") if type(i) is str else i for i in chats} "me" if c in ["me", "self"]
if type(chats) is list else else c.lower().strip("@") if type(c) is str
{"me" if chats in ["me", "self"] else chats.lower().strip("@") if type(chats) is str else chats} else c for c in chats
) )
def __call__(self, message): def __call__(self, message):
return bool( return (message.chat
message.chat and (message.chat.id in self
and (message.chat.id in self or (message.chat.username
or (message.chat.username and message.chat.username.lower() in self)
and message.chat.username.lower() in self) or ("me" in self
or ("me" in self and message.from_user and message.from_user
and message.from_user.is_self and message.from_user.is_self
and not message.outgoing)) and not message.outgoing)))
)
dan = create("Dan", lambda _, m: bool(m.from_user and m.from_user.id == 23122162)) dan = create("Dan", lambda _, m: bool(m.from_user and m.from_user.id == 23122162))

View File

@ -19,6 +19,12 @@
from .callback_query_handler import CallbackQueryHandler from .callback_query_handler import CallbackQueryHandler
from .deleted_messages_handler import DeletedMessagesHandler from .deleted_messages_handler import DeletedMessagesHandler
from .disconnect_handler import DisconnectHandler from .disconnect_handler import DisconnectHandler
from .inline_query_handler import InlineQueryHandler
from .message_handler import MessageHandler from .message_handler import MessageHandler
from .raw_update_handler import RawUpdateHandler from .raw_update_handler import RawUpdateHandler
from .user_status_handler import UserStatusHandler from .user_status_handler import UserStatusHandler
__all__ = [
"MessageHandler", "DeletedMessagesHandler", "CallbackQueryHandler", "RawUpdateHandler", "DisconnectHandler",
"UserStatusHandler", "InlineQueryHandler"
]

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 .handler import Handler
class InlineQueryHandler(Handler):
"""The InlineQuery handler class. Used to handle inline queries.
It is intended to be used with :meth:`add_handler() <pyrogram.Client.add_handler>`
For a nicer way to register this handler, have a look at the
:meth:`on_inline_query() <pyrogram.Client.on_inline_query>` decorator.
Args:
callback (``callable``):
Pass a function that will be called when a new InlineQuery arrives. It takes *(client, inline_query)*
as positional arguments (look at the section below for a detailed description).
filters (:obj:`Filters <pyrogram.Filters>`):
Pass one or more filters to allow only a subset of inline queries to be passed
in your callback function.
Other parameters:
client (:obj:`Client <pyrogram.Client>`):
The Client itself, useful when you want to call other API methods inside the inline query handler.
inline_query (:obj:`InlineQuery <pyrogram.InlineQuery>`):
The received inline query.
"""
def __init__(self, callback: callable, filters=None):
super().__init__(callback, filters)
def check(self, callback_query):
return (
self.filters(callback_query)
if callable(self.filters)
else True
)

View File

@ -17,6 +17,7 @@
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>. # along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
from .answer_callback_query import AnswerCallbackQuery from .answer_callback_query import AnswerCallbackQuery
from .answer_inline_query import AnswerInlineQuery
from .get_game_high_scores import GetGameHighScores from .get_game_high_scores import GetGameHighScores
from .get_inline_bot_results import GetInlineBotResults from .get_inline_bot_results import GetInlineBotResults
from .request_callback_answer import RequestCallbackAnswer from .request_callback_answer import RequestCallbackAnswer
@ -27,6 +28,7 @@ from .set_game_score import SetGameScore
class Bots( class Bots(
AnswerCallbackQuery, AnswerCallbackQuery,
AnswerInlineQuery,
GetInlineBotResults, GetInlineBotResults,
RequestCallbackAnswer, RequestCallbackAnswer,
SendInlineBotResult, SendInlineBotResult,

View File

@ -21,12 +21,14 @@ from pyrogram.client.ext import BaseClient
class AnswerCallbackQuery(BaseClient): class AnswerCallbackQuery(BaseClient):
def answer_callback_query(self, def answer_callback_query(
callback_query_id: str, self,
text: str = None, callback_query_id: str,
show_alert: bool = None, text: str = None,
url: str = None, show_alert: bool = None,
cache_time: int = 0): url: str = None,
cache_time: int = 0
):
"""Use this method to send answers to callback queries sent from inline keyboards. """Use this method to send answers to callback queries sent from inline keyboards.
The answer will be displayed to the user as a notification at the top of the chat screen or as an alert. The answer will be displayed to the user as a notification at the top of the chat screen or as an alert.
@ -55,7 +57,7 @@ class AnswerCallbackQuery(BaseClient):
True, on success. True, on success.
Raises: Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error. :class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
""" """
return self.send( return self.send(
functions.messages.SetBotCallbackAnswer( functions.messages.SetBotCallbackAnswer(

View File

@ -0,0 +1,91 @@
# 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 typing import List
from pyrogram.api import functions, types
from pyrogram.client.ext import BaseClient
from ...types.inline_mode import InlineQueryResult
class AnswerInlineQuery(BaseClient):
def answer_inline_query(
self,
inline_query_id: str,
results: List[InlineQueryResult],
cache_time: int = 300,
is_personal: bool = None,
next_offset: str = "",
switch_pm_text: str = "",
switch_pm_parameter: str = ""
):
"""Use this method to send answers to an inline query.
No more than 50 results per query are allowed.
Args:
inline_query_id (``str``):
Unique identifier for the answered query.
results (List of :obj:`InlineQueryResult <pyrogram.InlineQueryResult>`):
A list of results for the inline query.
cache_time (``int``, *optional*):
The maximum amount of time in seconds that the result of the inline query may be cached on the server.
Defaults to 300.
is_personal (``bool``, *optional*):
Pass True, if results may be cached on the server side only for the user that sent the query.
By default, results may be returned to any user who sends the same query.
next_offset (``str``, *optional*):
Pass the offset that a client should send in the next query with the same text to receive more results.
Pass an empty string if there are no more results or if you dont support pagination.
Offset length cant exceed 64 bytes.
switch_pm_text (``str``, *optional*):
If passed, clients will display a button with specified text that switches the user to a private chat
with the bot and sends the bot a start message with the parameter switch_pm_parameter
switch_pm_parameter (``str``, *optional*):
`Deep-linking <https://core.telegram.org/bots#deep-linking>`_ parameter for the /start message sent to
the bot when user presses the switch button. 1-64 characters, only A-Z, a-z, 0-9, _ and - are allowed.
Example: An inline bot that sends YouTube videos can ask the user to connect the bot to their YouTube
account to adapt search results accordingly. To do this, it displays a "Connect your YouTube account"
button above the results, or even before showing any. The user presses the button, switches to a private
chat with the bot and, in doing so, passes a start parameter that instructs the bot to return an oauth
link. Once done, the bot can offer a switch_inline button so that the user can easily return to the chat
where they wanted to use the bot's inline capabilities.
Returns:
On success, True is returned.
"""
return self.send(
functions.messages.SetInlineBotResults(
query_id=int(inline_query_id),
results=[r.write() for r in results],
cache_time=cache_time,
gallery=None,
private=is_personal or None,
next_offset=next_offset or None,
switch_pm=types.InlineBotSwitchPM(
text=switch_pm_text,
start_param=switch_pm_parameter
) if switch_pm_text else None
)
)

View File

@ -24,10 +24,12 @@ from pyrogram.client.ext import BaseClient
class GetGameHighScores(BaseClient): class GetGameHighScores(BaseClient):
def get_game_high_scores(self, def get_game_high_scores(
user_id: Union[int, str], self,
chat_id: Union[int, str], user_id: Union[int, str],
message_id: int = None): chat_id: Union[int, str],
message_id: int = None
):
"""Use this method to get data for high score tables. """Use this method to get data for high score tables.
Args: Args:
@ -50,7 +52,7 @@ class GetGameHighScores(BaseClient):
On success, a :obj:`GameHighScores <pyrogram.GameHighScores>` object is returned. On success, a :obj:`GameHighScores <pyrogram.GameHighScores>` object is returned.
Raises: Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error. :class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
""" """
# TODO: inline_message_id # TODO: inline_message_id

View File

@ -19,17 +19,19 @@
from typing import Union from typing import Union
from pyrogram.api import functions, types from pyrogram.api import functions, types
from pyrogram.api.errors import UnknownError from pyrogram.errors import UnknownError
from pyrogram.client.ext import BaseClient from pyrogram.client.ext import BaseClient
class GetInlineBotResults(BaseClient): class GetInlineBotResults(BaseClient):
def get_inline_bot_results(self, def get_inline_bot_results(
bot: Union[int, str], self,
query: str, bot: Union[int, str],
offset: str = "", query: str,
latitude: float = None, offset: str = "",
longitude: float = None): latitude: float = None,
longitude: float = None
):
"""Use this method to get bot results via inline queries. """Use this method to get bot results via inline queries.
You can then send a result using :obj:`send_inline_bot_result <pyrogram.Client.send_inline_bot_result>` You can then send a result using :obj:`send_inline_bot_result <pyrogram.Client.send_inline_bot_result>`
@ -56,7 +58,7 @@ class GetInlineBotResults(BaseClient):
On Success, :obj:`BotResults <pyrogram.api.types.messages.BotResults>` is returned. On Success, :obj:`BotResults <pyrogram.api.types.messages.BotResults>` is returned.
Raises: Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error. :class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
``TimeoutError`` if the bot fails to answer within 10 seconds ``TimeoutError`` if the bot fails to answer within 10 seconds
""" """
# TODO: Don't return the raw type # TODO: Don't return the raw type

View File

@ -23,12 +23,14 @@ from pyrogram.client.ext import BaseClient
class RequestCallbackAnswer(BaseClient): class RequestCallbackAnswer(BaseClient):
def request_callback_answer(self, def request_callback_answer(
chat_id: Union[int, str], self,
message_id: int, chat_id: Union[int, str],
callback_data: bytes): message_id: int,
"""Use this method to request a callback answer from bots. This is the equivalent of clicking an callback_data: bytes
inline button containing callback data. ):
"""Use this method to request a callback answer from bots.
This is the equivalent of clicking an inline button containing callback data.
Args: Args:
chat_id (``int`` | ``str``): chat_id (``int`` | ``str``):
@ -47,7 +49,7 @@ class RequestCallbackAnswer(BaseClient):
or as an alert. or as an alert.
Raises: Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error. :class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
``TimeoutError`` if the bot fails to answer within 10 seconds. ``TimeoutError`` if the bot fails to answer within 10 seconds.
""" """
return self.send( return self.send(

View File

@ -24,15 +24,19 @@ from pyrogram.client.ext import BaseClient
class SendGame(BaseClient): class SendGame(BaseClient):
def send_game(self, def send_game(
chat_id: Union[int, str], self,
game_short_name: str, chat_id: Union[int, str],
disable_notification: bool = None, game_short_name: str,
reply_to_message_id: int = None, disable_notification: bool = None,
reply_markup: Union["pyrogram.InlineKeyboardMarkup", reply_to_message_id: int = None,
"pyrogram.ReplyKeyboardMarkup", reply_markup: Union[
"pyrogram.ReplyKeyboardRemove", "pyrogram.InlineKeyboardMarkup",
"pyrogram.ForceReply"] = None) -> "pyrogram.Message": "pyrogram.ReplyKeyboardMarkup",
"pyrogram.ReplyKeyboardRemove",
"pyrogram.ForceReply"
] = None
) -> "pyrogram.Message":
"""Use this method to send a game. """Use this method to send a game.
Args: Args:
@ -59,7 +63,7 @@ class SendGame(BaseClient):
On success, the sent :obj:`Message` is returned. On success, the sent :obj:`Message` is returned.
Raises: Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error. :class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
""" """
r = self.send( r = self.send(
functions.messages.SendMedia( functions.messages.SendMedia(

View File

@ -23,13 +23,15 @@ from pyrogram.client.ext import BaseClient
class SendInlineBotResult(BaseClient): class SendInlineBotResult(BaseClient):
def send_inline_bot_result(self, def send_inline_bot_result(
chat_id: Union[int, str], self,
query_id: int, chat_id: Union[int, str],
result_id: str, query_id: int,
disable_notification: bool = None, result_id: str,
reply_to_message_id: int = None, disable_notification: bool = None,
hide_via: bool = None): reply_to_message_id: int = None,
hide_via: bool = None
):
"""Use this method to send an inline bot result. """Use this method to send an inline bot result.
Bot results can be retrieved using :obj:`get_inline_bot_results <pyrogram.Client.get_inline_bot_results>` Bot results can be retrieved using :obj:`get_inline_bot_results <pyrogram.Client.get_inline_bot_results>`
@ -59,7 +61,7 @@ class SendInlineBotResult(BaseClient):
On success, the sent Message is returned. On success, the sent Message is returned.
Raises: Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error. :class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
""" """
return self.send( return self.send(
functions.messages.SendInlineBotResult( functions.messages.SendInlineBotResult(

View File

@ -24,13 +24,15 @@ from pyrogram.client.ext import BaseClient
class SetGameScore(BaseClient): class SetGameScore(BaseClient):
def set_game_score(self, def set_game_score(
user_id: Union[int, str], self,
score: int, user_id: Union[int, str],
force: bool = None, score: int,
disable_edit_message: bool = None, force: bool = None,
chat_id: Union[int, str] = None, disable_edit_message: bool = None,
message_id: int = None): chat_id: Union[int, str] = None,
message_id: int = None
):
# inline_message_id: str = None): TODO Add inline_message_id # inline_message_id: str = None): TODO Add inline_message_id
"""Use this method to set the score of the specified user in a game. """Use this method to set the score of the specified user in a game.
@ -65,7 +67,7 @@ class SetGameScore(BaseClient):
otherwise returns True. otherwise returns True.
Raises: Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error. :class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
:class:`BotScoreNotModified` if the new score is not greater than the user's current score in the chat and force is False. :class:`BotScoreNotModified` if the new score is not greater than the user's current score in the chat and force is False.
""" """
r = self.send( r = self.send(

View File

@ -31,12 +31,14 @@ from .kick_chat_member import KickChatMember
from .leave_chat import LeaveChat from .leave_chat import LeaveChat
from .pin_chat_message import PinChatMessage from .pin_chat_message import PinChatMessage
from .promote_chat_member import PromoteChatMember from .promote_chat_member import PromoteChatMember
from .restrict_chat import RestrictChat
from .restrict_chat_member import RestrictChatMember from .restrict_chat_member import RestrictChatMember
from .set_chat_description import SetChatDescription from .set_chat_description import SetChatDescription
from .set_chat_photo import SetChatPhoto from .set_chat_photo import SetChatPhoto
from .set_chat_title import SetChatTitle from .set_chat_title import SetChatTitle
from .unban_chat_member import UnbanChatMember from .unban_chat_member import UnbanChatMember
from .unpin_chat_message import UnpinChatMessage from .unpin_chat_message import UnpinChatMessage
from .update_chat_username import UpdateChatUsername
class Chats( class Chats(
@ -60,6 +62,8 @@ class Chats(
GetChatMembersCount, GetChatMembersCount,
GetChatPreview, GetChatPreview,
IterDialogs, IterDialogs,
IterChatMembers IterChatMembers,
UpdateChatUsername,
RestrictChat
): ):
pass pass

View File

@ -23,8 +23,10 @@ from ...ext import BaseClient
class DeleteChatPhoto(BaseClient): class DeleteChatPhoto(BaseClient):
def delete_chat_photo(self, def delete_chat_photo(
chat_id: Union[int, str]) -> bool: self,
chat_id: Union[int, str]
) -> bool:
"""Use this method to delete a chat photo. """Use this method to delete a chat photo.
Photos can't be changed for private chats. 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. You must be an administrator in the chat for this to work and must have the appropriate admin rights.
@ -41,7 +43,7 @@ class DeleteChatPhoto(BaseClient):
True on success. True on success.
Raises: Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error. :class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
``ValueError`` if a chat_id belongs to user. ``ValueError`` if a chat_id belongs to user.
""" """
peer = self.resolve_peer(chat_id) peer = self.resolve_peer(chat_id)

View File

@ -23,8 +23,10 @@ from ...ext import BaseClient
class ExportChatInviteLink(BaseClient): class ExportChatInviteLink(BaseClient):
def export_chat_invite_link(self, def export_chat_invite_link(
chat_id: Union[int, str]) -> str: self,
chat_id: Union[int, str]
) -> str:
"""Use this method to generate a new invite link for a chat; any previously generated link is revoked. """Use this method to generate a new invite link for a chat; any previously generated link is revoked.
You must be an administrator in the chat for this to work and have the appropriate admin rights. You must be an administrator in the chat for this to work and have the appropriate admin rights.
@ -38,14 +40,14 @@ class ExportChatInviteLink(BaseClient):
On success, the exported invite link as string is returned. On success, the exported invite link as string is returned.
Raises: Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error. :class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
""" """
peer = self.resolve_peer(chat_id) peer = self.resolve_peer(chat_id)
if isinstance(peer, types.InputPeerChat): if isinstance(peer, types.InputPeerChat):
return self.send( return self.send(
functions.messages.ExportChatInvite( functions.messages.ExportChatInvite(
chat_id=peer.chat_id peer=peer.chat_id
) )
).link ).link
elif isinstance(peer, types.InputPeerChannel): elif isinstance(peer, types.InputPeerChannel):

View File

@ -24,10 +24,13 @@ from ...ext import BaseClient
class GetChat(BaseClient): class GetChat(BaseClient):
def get_chat(self, def get_chat(
chat_id: Union[int, str]) -> "pyrogram.Chat": self,
"""Use this method to get up to date information about the chat (current name of the user for chat_id: Union[int, str]
one-on-one conversations, current username of a user, group or channel, etc.) ) -> "pyrogram.Chat":
"""Use this method to get up to date information about the chat.
Information include current name of the user for one-on-one conversations, current username of a user, group or
channel, etc.
Args: Args:
chat_id (``int`` | ``str``): chat_id (``int`` | ``str``):
@ -39,7 +42,7 @@ class GetChat(BaseClient):
On success, a :obj:`Chat <pyrogram.Chat>` object is returned. On success, a :obj:`Chat <pyrogram.Chat>` object is returned.
Raises: Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error. :class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
``ValueError`` in case the chat invite link refers to a chat you haven't joined yet. ``ValueError`` in case the chat invite link refers to a chat you haven't joined yet.
""" """
match = self.INVITE_LINK_RE.match(str(chat_id)) match = self.INVITE_LINK_RE.match(str(chat_id))
@ -67,10 +70,10 @@ class GetChat(BaseClient):
peer = self.resolve_peer(chat_id) peer = self.resolve_peer(chat_id)
if isinstance(peer, types.InputPeerChannel): if isinstance(peer, types.InputPeerChannel):
r = self.send(functions.channels.GetFullChannel(peer)) r = self.send(functions.channels.GetFullChannel(channel=peer))
elif isinstance(peer, (types.InputPeerUser, types.InputPeerSelf)): elif isinstance(peer, (types.InputPeerUser, types.InputPeerSelf)):
r = self.send(functions.users.GetFullUser(peer)) r = self.send(functions.users.GetFullUser(id=peer))
else: else:
r = self.send(functions.messages.GetFullChat(peer.chat_id)) r = self.send(functions.messages.GetFullChat(chat_id=peer.chat_id))
return pyrogram.Chat._parse_full(self, r) return pyrogram.Chat._parse_full(self, r)

View File

@ -19,14 +19,17 @@
from typing import Union from typing import Union
import pyrogram import pyrogram
from pyrogram.api import functions, types, errors from pyrogram.api import functions, types
from pyrogram.errors import UserNotParticipant
from ...ext import BaseClient from ...ext import BaseClient
class GetChatMember(BaseClient): class GetChatMember(BaseClient):
def get_chat_member(self, def get_chat_member(
chat_id: Union[int, str], self,
user_id: Union[int, str]) -> "pyrogram.ChatMember": chat_id: Union[int, str],
user_id: Union[int, str]
) -> "pyrogram.ChatMember":
"""Use this method to get information about one member of a chat. """Use this method to get information about one member of a chat.
Args: Args:
@ -42,7 +45,7 @@ class GetChatMember(BaseClient):
On success, a :obj:`ChatMember <pyrogram.ChatMember>` object is returned. On success, a :obj:`ChatMember <pyrogram.ChatMember>` object is returned.
Raises: Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error. :class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
""" """
chat_id = self.resolve_peer(chat_id) chat_id = self.resolve_peer(chat_id)
user_id = self.resolve_peer(user_id) user_id = self.resolve_peer(user_id)
@ -55,10 +58,10 @@ class GetChatMember(BaseClient):
) )
for member in pyrogram.ChatMembers._parse(self, full_chat).chat_members: for member in pyrogram.ChatMembers._parse(self, full_chat).chat_members:
if member.user.id == user_id.user_id: if member.user.is_self:
return member return member
else: else:
raise errors.UserNotParticipant raise UserNotParticipant
elif isinstance(chat_id, types.InputPeerChannel): elif isinstance(chat_id, types.InputPeerChannel):
r = self.send( r = self.send(
functions.channels.GetParticipant( functions.channels.GetParticipant(

View File

@ -16,12 +16,17 @@
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>. # along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
import logging
import time
from typing import Union from typing import Union
import pyrogram import pyrogram
from pyrogram.api import functions, types from pyrogram.api import functions, types
from pyrogram.errors import FloodWait
from ...ext import BaseClient from ...ext import BaseClient
log = logging.getLogger(__name__)
class Filters: class Filters:
ALL = "all" ALL = "all"
@ -33,12 +38,14 @@ class Filters:
class GetChatMembers(BaseClient): class GetChatMembers(BaseClient):
def get_chat_members(self, def get_chat_members(
chat_id: Union[int, str], self,
offset: int = 0, chat_id: Union[int, str],
limit: int = 200, offset: int = 0,
query: str = "", limit: int = 200,
filter: str = Filters.ALL) -> "pyrogram.ChatMembers": query: str = "",
filter: str = Filters.ALL
) -> "pyrogram.ChatMembers":
"""Use this method to get a chunk of the members list of a chat. """Use this method to get a chunk of the members list of a chat.
You can get up to 200 chat members at once. You can get up to 200 chat members at once.
@ -82,7 +89,7 @@ class GetChatMembers(BaseClient):
On success, a :obj:`ChatMembers` object is returned. On success, a :obj:`ChatMembers` object is returned.
Raises: Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error. :class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
``ValueError`` if you used an invalid filter or a chat_id that belongs to a user. ``ValueError`` if you used an invalid filter or a chat_id that belongs to a user.
""" """
peer = self.resolve_peer(chat_id) peer = self.resolve_peer(chat_id)
@ -92,7 +99,7 @@ class GetChatMembers(BaseClient):
self, self,
self.send( self.send(
functions.messages.GetFullChat( functions.messages.GetFullChat(
peer.chat_id chat_id=peer.chat_id
) )
) )
) )
@ -114,17 +121,22 @@ class GetChatMembers(BaseClient):
else: else:
raise ValueError("Invalid filter \"{}\"".format(filter)) raise ValueError("Invalid filter \"{}\"".format(filter))
return pyrogram.ChatMembers._parse( while True:
self, try:
self.send( return pyrogram.ChatMembers._parse(
functions.channels.GetParticipants( self,
channel=peer, self.send(
filter=filter, functions.channels.GetParticipants(
offset=offset, channel=peer,
limit=limit, filter=filter,
hash=0 offset=offset,
limit=limit,
hash=0
)
)
) )
) except FloodWait as e:
) log.warning("Sleeping for {}s".format(e.x))
time.sleep(e.x)
else: else:
raise ValueError("The chat_id \"{}\" belongs to a user".format(chat_id)) raise ValueError("The chat_id \"{}\" belongs to a user".format(chat_id))

View File

@ -23,8 +23,10 @@ from ...ext import BaseClient
class GetChatMembersCount(BaseClient): class GetChatMembersCount(BaseClient):
def get_chat_members_count(self, def get_chat_members_count(
chat_id: Union[int, str]) -> int: self,
chat_id: Union[int, str]
) -> int:
"""Use this method to get the number of members in a chat. """Use this method to get the number of members in a chat.
Args: Args:
@ -35,7 +37,7 @@ class GetChatMembersCount(BaseClient):
On success, an integer is returned. On success, an integer is returned.
Raises: Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error. :class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
``ValueError`` if a chat_id belongs to user. ``ValueError`` if a chat_id belongs to user.
""" """
peer = self.resolve_peer(chat_id) peer = self.resolve_peer(chat_id)

View File

@ -22,8 +22,10 @@ from ...ext import BaseClient
class GetChatPreview(BaseClient): class GetChatPreview(BaseClient):
def get_chat_preview(self, def get_chat_preview(
invite_link: str): self,
invite_link: str
):
"""Use this method to get the preview of a chat using the invite link. """Use this method to get the preview of a chat using the invite link.
This method only returns a chat preview, if you want to join a chat use :meth:`join_chat` This method only returns a chat preview, if you want to join a chat use :meth:`join_chat`
@ -36,7 +38,7 @@ class GetChatPreview(BaseClient):
Either :obj:`Chat` or :obj:`ChatPreview`, depending on whether you already joined the chat or not. Either :obj:`Chat` or :obj:`ChatPreview`, depending on whether you already joined the chat or not.
Raises: Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error. :class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
``ValueError`` in case of an invalid invite_link. ``ValueError`` in case of an invalid invite_link.
""" """
match = self.INVITE_LINK_RE.match(invite_link) match = self.INVITE_LINK_RE.match(invite_link)

View File

@ -21,18 +21,20 @@ import time
import pyrogram import pyrogram
from pyrogram.api import functions, types from pyrogram.api import functions, types
from pyrogram.api.errors import FloodWait from pyrogram.errors import FloodWait
from ...ext import BaseClient from ...ext import BaseClient
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class GetDialogs(BaseClient): class GetDialogs(BaseClient):
def get_dialogs(self, def get_dialogs(
offset_date: int = 0, self,
limit: int = 100, offset_date: int = 0,
pinned_only: bool = False) -> "pyrogram.Dialogs": limit: int = 100,
"""Use this method to get a chunk of the user's dialogs pinned_only: bool = False
) -> "pyrogram.Dialogs":
"""Use this method to get a chunk of the user's dialogs.
You can get up to 100 dialogs at once. You can get up to 100 dialogs at once.
For a more convenient way of getting a user's dialogs see :meth:`iter_dialogs`. For a more convenient way of getting a user's dialogs see :meth:`iter_dialogs`.
@ -54,7 +56,7 @@ class GetDialogs(BaseClient):
On success, a :obj:`Dialogs` object is returned. On success, a :obj:`Dialogs` object is returned.
Raises: Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error. :class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
""" """
while True: while True:

View File

@ -20,6 +20,7 @@ from string import ascii_lowercase
from typing import Union, Generator from typing import Union, Generator
import pyrogram import pyrogram
from pyrogram.api import types
from ...ext import BaseClient from ...ext import BaseClient
@ -37,11 +38,13 @@ QUERYABLE_FILTERS = (Filters.ALL, Filters.KICKED, Filters.RESTRICTED)
class IterChatMembers(BaseClient): class IterChatMembers(BaseClient):
def iter_chat_members(self, def iter_chat_members(
chat_id: Union[int, str], self,
limit: int = 0, chat_id: Union[int, str],
query: str = "", limit: int = 0,
filter: str = Filters.ALL) -> Generator["pyrogram.ChatMember", None, None]: query: str = "",
filter: str = Filters.ALL
) -> Generator["pyrogram.ChatMember", None, None]:
"""Use this method to iterate through the members of a chat sequentially. """Use this method to iterate through the members of a chat sequentially.
This convenience method does the same as repeatedly calling :meth:`get_chat_members` in a loop, thus saving you This convenience method does the same as repeatedly calling :meth:`get_chat_members` in a loop, thus saving you
@ -75,13 +78,14 @@ class IterChatMembers(BaseClient):
A generator yielding :obj:`ChatMember <pyrogram.ChatMember>` objects. A generator yielding :obj:`ChatMember <pyrogram.ChatMember>` objects.
Raises: Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error. :class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
""" """
current = 0 current = 0
yielded = set() yielded = set()
queries = [query] if query else QUERIES queries = [query] if query else QUERIES
total = limit or (1 << 31) - 1 total = limit or (1 << 31) - 1
limit = min(200, total) limit = min(200, total)
resolved_chat_id = self.resolve_peer(chat_id)
filter = ( filter = (
Filters.RECENT Filters.RECENT
@ -107,6 +111,9 @@ class IterChatMembers(BaseClient):
if not chat_members: if not chat_members:
break break
if isinstance(resolved_chat_id, types.InputPeerChat):
total = len(chat_members)
offset += len(chat_members) offset += len(chat_members)
for chat_member in chat_members: for chat_member in chat_members:

View File

@ -23,9 +23,11 @@ from ...ext import BaseClient
class IterDialogs(BaseClient): class IterDialogs(BaseClient):
def iter_dialogs(self, def iter_dialogs(
offset_date: int = 0, self,
limit: int = 0) -> Generator["pyrogram.Dialog", None, None]: offset_date: int = 0,
limit: int = 0
) -> Generator["pyrogram.Dialog", None, None]:
"""Use this method to iterate through a user's dialogs sequentially. """Use this method to iterate through a user's dialogs sequentially.
This convenience method does the same as repeatedly calling :meth:`get_dialogs` in a loop, thus saving you from This convenience method does the same as repeatedly calling :meth:`get_dialogs` in a loop, thus saving you from
@ -44,7 +46,7 @@ class IterDialogs(BaseClient):
A generator yielding :obj:`Dialog <pyrogram.Dialog>` objects. A generator yielding :obj:`Dialog <pyrogram.Dialog>` objects.
Raises: Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error. :class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
""" """
current = 0 current = 0
total = limit or (1 << 31) - 1 total = limit or (1 << 31) - 1

View File

@ -16,13 +16,16 @@
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>. # along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
import pyrogram
from pyrogram.api import functions, types from pyrogram.api import functions, types
from ...ext import BaseClient from ...ext import BaseClient
class JoinChat(BaseClient): class JoinChat(BaseClient):
def join_chat(self, def join_chat(
chat_id: str): self,
chat_id: str
):
"""Use this method to join a group chat or channel. """Use this method to join a group chat or channel.
Args: Args:
@ -30,17 +33,24 @@ class JoinChat(BaseClient):
Unique identifier for the target chat in form of a *t.me/joinchat/* link or username of the target Unique identifier for the target chat in form of a *t.me/joinchat/* link or username of the target
channel/supergroup (in the format @username). channel/supergroup (in the format @username).
Returns:
On success, a :obj:`Chat <pyrogram.Chat>` object is returned.
Raises: Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error. :class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
""" """
match = self.INVITE_LINK_RE.match(chat_id) match = self.INVITE_LINK_RE.match(chat_id)
if match: if match:
return self.send( chat = self.send(
functions.messages.ImportChatInvite( functions.messages.ImportChatInvite(
hash=match.group(1) hash=match.group(1)
) )
) )
if isinstance(chat.chats[0], types.Chat):
return pyrogram.Chat._parse_chat_chat(self, chat.chats[0])
elif isinstance(chat.chats[0], types.Channel):
return pyrogram.Chat._parse_channel_chat(self, chat.chats[0])
else: else:
resolved_peer = self.send( resolved_peer = self.send(
functions.contacts.ResolveUsername( functions.contacts.ResolveUsername(
@ -53,8 +63,10 @@ class JoinChat(BaseClient):
access_hash=resolved_peer.chats[0].access_hash access_hash=resolved_peer.chats[0].access_hash
) )
return self.send( chat = self.send(
functions.channels.JoinChannel( functions.channels.JoinChannel(
channel=channel channel=channel
) )
) )
return pyrogram.Chat._parse_channel_chat(self, chat.chats[0])

View File

@ -24,10 +24,12 @@ from ...ext import BaseClient
class KickChatMember(BaseClient): class KickChatMember(BaseClient):
def kick_chat_member(self, def kick_chat_member(
chat_id: Union[int, str], self,
user_id: Union[int, str], chat_id: Union[int, str],
until_date: int = 0) -> Union["pyrogram.Message", bool]: user_id: Union[int, str],
until_date: int = 0
) -> Union["pyrogram.Message", bool]:
"""Use this method to kick a user from a group, a supergroup or a channel. """Use this method to kick a user from a group, a supergroup or a channel.
In the case of supergroups and channels, the user will not be able to return to the group on their own using In the case of supergroups and channels, the user will not be able to return to the group on their own using
invite links, etc., unless unbanned first. You must be an administrator in the chat for this to work and must invite links, etc., unless unbanned first. You must be an administrator in the chat for this to work and must
@ -55,7 +57,7 @@ class KickChatMember(BaseClient):
On success, either True or a service :obj:`Message <pyrogram.Message>` will be returned (when applicable). On success, either True or a service :obj:`Message <pyrogram.Message>` will be returned (when applicable).
Raises: Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error. :class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
""" """
chat_peer = self.resolve_peer(chat_id) chat_peer = self.resolve_peer(chat_id)
user_peer = self.resolve_peer(user_id) user_peer = self.resolve_peer(user_id)
@ -65,7 +67,7 @@ class KickChatMember(BaseClient):
functions.channels.EditBanned( functions.channels.EditBanned(
channel=chat_peer, channel=chat_peer,
user_id=user_peer, user_id=user_peer,
banned_rights=types.ChannelBannedRights( banned_rights=types.ChatBannedRights(
until_date=until_date, until_date=until_date,
view_messages=True, view_messages=True,
send_messages=True, send_messages=True,

View File

@ -23,9 +23,11 @@ from ...ext import BaseClient
class LeaveChat(BaseClient): class LeaveChat(BaseClient):
def leave_chat(self, def leave_chat(
chat_id: Union[int, str], self,
delete: bool = False): chat_id: Union[int, str],
delete: bool = False
):
"""Use this method to leave a group chat or channel. """Use this method to leave a group chat or channel.
Args: Args:
@ -37,7 +39,7 @@ class LeaveChat(BaseClient):
Deletes the group chat dialog after leaving (for simple group chats, not supergroups). Deletes the group chat dialog after leaving (for simple group chats, not supergroups).
Raises: Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error. :class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
""" """
peer = self.resolve_peer(chat_id) peer = self.resolve_peer(chat_id)

View File

@ -23,10 +23,12 @@ from ...ext import BaseClient
class PinChatMessage(BaseClient): class PinChatMessage(BaseClient):
def pin_chat_message(self, def pin_chat_message(
chat_id: Union[int, str], self,
message_id: int, chat_id: Union[int, str],
disable_notification: bool = None) -> bool: message_id: int,
disable_notification: bool = None
) -> bool:
"""Use this method to pin a message in a group, channel or your own chat. """Use this method to pin a message in a group, channel or your own chat.
You must be an administrator in the chat for this to work and must have the "can_pin_messages" admin right in 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. the supergroup or "can_edit_messages" admin right in the channel.
@ -46,7 +48,7 @@ class PinChatMessage(BaseClient):
True on success. True on success.
Raises: Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error. :class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
""" """
self.send( self.send(
functions.messages.UpdatePinnedMessage( functions.messages.UpdatePinnedMessage(
@ -55,3 +57,5 @@ class PinChatMessage(BaseClient):
silent=disable_notification or None silent=disable_notification or None
) )
) )
return True

View File

@ -23,18 +23,21 @@ from ...ext import BaseClient
class PromoteChatMember(BaseClient): class PromoteChatMember(BaseClient):
def promote_chat_member(self, def promote_chat_member(
chat_id: Union[int, str], self,
user_id: Union[int, str], chat_id: Union[int, str],
can_change_info: bool = True, user_id: Union[int, str],
can_post_messages: bool = False, can_change_info: bool = True,
can_edit_messages: bool = False, can_post_messages: bool = False,
can_delete_messages: bool = True, can_edit_messages: bool = False,
can_invite_users: bool = True, can_delete_messages: bool = True,
can_restrict_members: bool = True, can_restrict_members: bool = True,
can_pin_messages: bool = False, can_invite_users: bool = True,
can_promote_members: bool = False) -> bool: can_pin_messages: bool = False,
can_promote_members: bool = False
) -> bool:
"""Use this method to promote or demote a user in a supergroup or a channel. """Use this method to promote or demote a user in a supergroup or a channel.
You must be an administrator in the chat for this to work and must have the appropriate admin rights. You must be an administrator in the chat for this to work and must have the appropriate admin rights.
Pass False for all boolean parameters to demote a user. Pass False for all boolean parameters to demote a user.
@ -58,12 +61,12 @@ class PromoteChatMember(BaseClient):
can_delete_messages (``bool``, *optional*): can_delete_messages (``bool``, *optional*):
Pass True, if the administrator can delete messages of other users. Pass True, if the administrator can delete messages of other users.
can_invite_users (``bool``, *optional*):
Pass True, if the administrator can invite new users to the chat.
can_restrict_members (``bool``, *optional*): can_restrict_members (``bool``, *optional*):
Pass True, if the administrator can restrict, ban or unban chat members. Pass True, if the administrator can restrict, ban or unban chat members.
can_invite_users (``bool``, *optional*):
Pass True, if the administrator can invite new users to the chat.
can_pin_messages (``bool``, *optional*): can_pin_messages (``bool``, *optional*):
Pass True, if the administrator can pin messages, supergroups only. Pass True, if the administrator can pin messages, supergroups only.
@ -76,23 +79,21 @@ class PromoteChatMember(BaseClient):
True on success. True on success.
Raises: Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error. :class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
""" """
self.send( self.send(
functions.channels.EditAdmin( functions.channels.EditAdmin(
channel=self.resolve_peer(chat_id), channel=self.resolve_peer(chat_id),
user_id=self.resolve_peer(user_id), user_id=self.resolve_peer(user_id),
admin_rights=types.ChannelAdminRights( admin_rights=types.ChatAdminRights(
change_info=can_change_info or None, change_info=can_change_info or None,
post_messages=can_post_messages or None, post_messages=can_post_messages or None,
edit_messages=can_edit_messages or None, edit_messages=can_edit_messages or None,
delete_messages=can_delete_messages or None, delete_messages=can_delete_messages or None,
ban_users=can_restrict_members or None, ban_users=can_restrict_members or None,
invite_users=can_invite_users or None, invite_users=can_invite_users or None,
invite_link=can_invite_users or None,
pin_messages=can_pin_messages or None, pin_messages=can_pin_messages or None,
add_admins=can_promote_members or None, add_admins=can_promote_members or None,
manage_call=None
) )
) )
) )

View File

@ -0,0 +1,143 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2019 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 typing import Union
from pyrogram.api import functions, types
from ...ext import BaseClient
from ...types.user_and_chats import Chat
class RestrictChat(BaseClient):
def restrict_chat(
self,
chat_id: Union[int, str],
can_send_messages: bool = False,
can_send_media_messages: bool = False,
can_send_other_messages: bool = False,
can_add_web_page_previews: bool = False,
can_send_polls: bool = False,
can_change_info: bool = False,
can_invite_users: bool = False,
can_pin_messages: bool = False
) -> Chat:
"""Use this method to restrict a chat.
Pass True for all boolean parameters to lift restrictions from a chat.
Args:
chat_id (``int`` | ``str``):
Unique identifier (int) or username (str) of the target chat.
can_send_messages (``bool``, *optional*):
Pass True, if the user can send text messages, contacts, locations and venues.
can_send_media_messages (``bool``, *optional*):
Pass True, if the user can send audios, documents, photos, videos, video notes and voice notes,
implies can_send_messages.
can_send_other_messages (``bool``, *optional*):
Pass True, if the user can send animations, games, stickers and use inline bots,
implies can_send_media_messages.
can_add_web_page_previews (``bool``, *optional*):
Pass True, if the user may add web page previews to their messages, implies can_send_media_messages.
can_send_polls (``bool``, *optional*):
Pass True, if the user can send polls, implies can_send_media_messages.
can_change_info (``bool``, *optional*):
Pass True, if the user can change the chat title, photo and other settings.
can_invite_users (``bool``, *optional*):
Pass True, if the user can invite new users to the chat.
can_pin_messages (``bool``, *optional*):
Pass True, if the user can pin messages.
Returns:
On success, a :obj:`Chat <pyrogram.Chat>` object is returned.
Raises:
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
"""
send_messages = True
send_media = True
send_stickers = True
send_gifs = True
send_games = True
send_inline = True
embed_links = True
send_polls = True
change_info = True
invite_users = True
pin_messages = True
if can_send_messages:
send_messages = None
if can_send_media_messages:
send_messages = None
send_media = None
if can_send_other_messages:
send_messages = None
send_media = None
send_stickers = None
send_gifs = None
send_games = None
send_inline = None
if can_add_web_page_previews:
send_messages = None
send_media = None
embed_links = None
if can_send_polls:
send_messages = None
send_polls = None
if can_change_info:
change_info = None
if can_invite_users:
invite_users = None
if can_pin_messages:
pin_messages = None
r = self.send(
functions.messages.EditChatDefaultBannedRights(
peer=self.resolve_peer(chat_id),
banned_rights=types.ChatBannedRights(
until_date=0,
send_messages=send_messages,
send_media=send_media,
send_stickers=send_stickers,
send_gifs=send_gifs,
send_games=send_games,
send_inline=send_inline,
embed_links=embed_links,
send_polls=send_polls,
change_info=change_info,
invite_users=invite_users,
pin_messages=pin_messages
)
)
)
return Chat._parse_chat(self, r.chats[0])

View File

@ -20,20 +20,28 @@ from typing import Union
from pyrogram.api import functions, types from pyrogram.api import functions, types
from ...ext import BaseClient from ...ext import BaseClient
from ...types.user_and_chats import Chat
class RestrictChatMember(BaseClient): class RestrictChatMember(BaseClient):
def restrict_chat_member(self, def restrict_chat_member(
chat_id: Union[int, str], self,
user_id: Union[int, str], chat_id: Union[int, str],
until_date: int = 0, user_id: Union[int, str],
can_send_messages: bool = False, until_date: int = 0,
can_send_media_messages: bool = False, can_send_messages: bool = False,
can_send_other_messages: bool = False, can_send_media_messages: bool = False,
can_add_web_page_previews: bool = False) -> bool: can_send_other_messages: bool = False,
"""Use this method to restrict a user in a supergroup. The bot must be an administrator in the supergroup for can_add_web_page_previews: bool = False,
this to work and must have the appropriate admin rights. Pass True for all boolean parameters to lift can_send_polls: bool = False,
restrictions from a user. can_change_info: bool = False,
can_invite_users: bool = False,
can_pin_messages: bool = False
) -> Chat:
"""Use this method to restrict a user in a supergroup.
The bot must be an administrator in the supergroup for this to work and must have the appropriate admin rights.
Pass True for all boolean parameters to lift restrictions from a user.
Args: Args:
chat_id (``int`` | ``str``): chat_id (``int`` | ``str``):
@ -60,13 +68,25 @@ class RestrictChatMember(BaseClient):
implies can_send_media_messages. implies can_send_media_messages.
can_add_web_page_previews (``bool``, *optional*): can_add_web_page_previews (``bool``, *optional*):
Pass True, if the user may add web page previews to their messages, implies can_send_media_messages Pass True, if the user may add web page previews to their messages, implies can_send_media_messages.
can_send_polls (``bool``, *optional*):
Pass True, if the user can send polls, implies can_send_media_messages.
can_change_info (``bool``, *optional*):
Pass True, if the user can change the chat title, photo and other settings.
can_invite_users (``bool``, *optional*):
Pass True, if the user can invite new users to the chat.
can_pin_messages (``bool``, *optional*):
Pass True, if the user can pin messages.
Returns: Returns:
True on success. On success, a :obj:`Chat <pyrogram.Chat>` object is returned.
Raises: Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error. :class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
""" """
send_messages = True send_messages = True
send_media = True send_media = True
@ -75,6 +95,10 @@ class RestrictChatMember(BaseClient):
send_games = True send_games = True
send_inline = True send_inline = True
embed_links = True embed_links = True
send_polls = True
change_info = True
invite_users = True
pin_messages = True
if can_send_messages: if can_send_messages:
send_messages = None send_messages = None
@ -84,6 +108,7 @@ class RestrictChatMember(BaseClient):
send_media = None send_media = None
if can_send_other_messages: if can_send_other_messages:
send_messages = None
send_media = None send_media = None
send_stickers = None send_stickers = None
send_gifs = None send_gifs = None
@ -91,14 +116,28 @@ class RestrictChatMember(BaseClient):
send_inline = None send_inline = None
if can_add_web_page_previews: if can_add_web_page_previews:
send_messages = None
send_media = None send_media = None
embed_links = None embed_links = None
self.send( if can_send_polls:
send_messages = None
send_polls = None
if can_change_info:
change_info = None
if can_invite_users:
invite_users = None
if can_pin_messages:
pin_messages = None
r = self.send(
functions.channels.EditBanned( functions.channels.EditBanned(
channel=self.resolve_peer(chat_id), channel=self.resolve_peer(chat_id),
user_id=self.resolve_peer(user_id), user_id=self.resolve_peer(user_id),
banned_rights=types.ChannelBannedRights( banned_rights=types.ChatBannedRights(
until_date=until_date, until_date=until_date,
send_messages=send_messages, send_messages=send_messages,
send_media=send_media, send_media=send_media,
@ -106,9 +145,13 @@ class RestrictChatMember(BaseClient):
send_gifs=send_gifs, send_gifs=send_gifs,
send_games=send_games, send_games=send_games,
send_inline=send_inline, send_inline=send_inline,
embed_links=embed_links embed_links=embed_links,
send_polls=send_polls,
change_info=change_info,
invite_users=invite_users,
pin_messages=pin_messages
) )
) )
) )
return True return Chat._parse_chat(self, r.chats[0])

View File

@ -23,9 +23,11 @@ from ...ext import BaseClient
class SetChatDescription(BaseClient): class SetChatDescription(BaseClient):
def set_chat_description(self, def set_chat_description(
chat_id: Union[int, str], self,
description: str) -> bool: chat_id: Union[int, str],
description: str
) -> bool:
"""Use this method to change the description of a supergroup or a channel. """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. You must be an administrator in the chat for this to work and must have the appropriate admin rights.
@ -40,20 +42,18 @@ class SetChatDescription(BaseClient):
True on success. True on success.
Raises: Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error. :class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
``ValueError`` if a chat_id doesn't belong to a supergroup or a channel. ``ValueError`` if a chat_id doesn't belong to a supergroup or a channel.
""" """
peer = self.resolve_peer(chat_id) peer = self.resolve_peer(chat_id)
if isinstance(peer, types.InputPeerChannel): if isinstance(peer, (types.InputPeerChannel, types.InputPeerChat)):
self.send( self.send(
functions.channels.EditAbout( functions.messages.EditChatAbout(
channel=peer, peer=peer,
about=description about=description
) )
) )
elif isinstance(peer, types.InputPeerChat):
raise ValueError("The chat_id \"{}\" belongs to a basic group".format(chat_id))
else: else:
raise ValueError("The chat_id \"{}\" belongs to a user".format(chat_id)) raise ValueError("The chat_id \"{}\" belongs to a user".format(chat_id))

View File

@ -26,9 +26,11 @@ from ...ext import BaseClient
class SetChatPhoto(BaseClient): class SetChatPhoto(BaseClient):
def set_chat_photo(self, def set_chat_photo(
chat_id: Union[int, str], self,
photo: str) -> bool: chat_id: Union[int, str],
photo: str
) -> bool:
"""Use this method to set a new profile photo for the chat. """Use this method to set a new profile photo for the chat.
Photos can't be changed for private chats. 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. You must be an administrator in the chat for this to work and must have the appropriate admin rights.
@ -48,7 +50,7 @@ class SetChatPhoto(BaseClient):
True on success. True on success.
Raises: Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error. :class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
``ValueError`` if a chat_id belongs to user. ``ValueError`` if a chat_id belongs to user.
""" """
peer = self.resolve_peer(chat_id) peer = self.resolve_peer(chat_id)

View File

@ -23,9 +23,11 @@ from ...ext import BaseClient
class SetChatTitle(BaseClient): class SetChatTitle(BaseClient):
def set_chat_title(self, def set_chat_title(
chat_id: Union[int, str], self,
title: str) -> bool: chat_id: Union[int, str],
title: str
) -> bool:
"""Use this method to change the title of a chat. """Use this method to change the title of a chat.
Titles can't be changed for private chats. 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. You must be an administrator in the chat for this to work and must have the appropriate admin rights.
@ -45,7 +47,7 @@ class SetChatTitle(BaseClient):
True on success. True on success.
Raises: Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error. :class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
``ValueError`` if a chat_id belongs to user. ``ValueError`` if a chat_id belongs to user.
""" """
peer = self.resolve_peer(chat_id) peer = self.resolve_peer(chat_id)

View File

@ -23,9 +23,11 @@ from ...ext import BaseClient
class UnbanChatMember(BaseClient): class UnbanChatMember(BaseClient):
def unban_chat_member(self, def unban_chat_member(
chat_id: Union[int, str], self,
user_id: Union[int, str]) -> bool: chat_id: Union[int, str],
user_id: Union[int, str]
) -> bool:
"""Use this method to unban a previously kicked user in a supergroup or channel. """Use this method to unban a previously kicked user in a supergroup or channel.
The user will **not** return to the group or channel automatically, but will be able to join via link, etc. The user will **not** return to the group or channel automatically, but will be able to join via link, etc.
You must be an administrator for this to work. You must be an administrator for this to work.
@ -42,13 +44,13 @@ class UnbanChatMember(BaseClient):
True on success. True on success.
Raises: Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error. :class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
""" """
self.send( self.send(
functions.channels.EditBanned( functions.channels.EditBanned(
channel=self.resolve_peer(chat_id), channel=self.resolve_peer(chat_id),
user_id=self.resolve_peer(user_id), user_id=self.resolve_peer(user_id),
banned_rights=types.ChannelBannedRights( banned_rights=types.ChatBannedRights(
until_date=0 until_date=0
) )
) )

View File

@ -23,8 +23,10 @@ from ...ext import BaseClient
class UnpinChatMessage(BaseClient): class UnpinChatMessage(BaseClient):
def unpin_chat_message(self, def unpin_chat_message(
chat_id: Union[int, str]) -> bool: self,
chat_id: Union[int, str]
) -> bool:
"""Use this method to unpin a message in a group, channel or your own chat. """Use this method to unpin a message in a group, channel or your own chat.
You must be an administrator in the chat for this to work and must have the "can_pin_messages" admin 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. right in the supergroup or "can_edit_messages" admin right in the channel.
@ -37,7 +39,7 @@ class UnpinChatMessage(BaseClient):
True on success. True on success.
Raises: Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error. :class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
""" """
self.send( self.send(
functions.messages.UpdatePinnedMessage( functions.messages.UpdatePinnedMessage(

View File

@ -0,0 +1,61 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2019 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 typing import Union
from pyrogram.api import functions, types
from ...ext import BaseClient
class UpdateChatUsername(BaseClient):
def update_chat_username(
self,
chat_id: Union[int, str],
username: Union[str, None]
) -> bool:
"""Use this method to update a channel or a supergroup username.
To update your own username (for users only, not bots) you can use :meth:`update_username`.
Args:
chat_id (``int`` | ``str``)
Unique identifier (int) or username (str) of the target chat.
username (``str`` | ``None``):
Username to set. Pass "" (empty string) or None to remove the username.
Returns:
True on success.
Raises:
:class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
``ValueError`` if a chat_id belongs to a user or chat.
"""
peer = self.resolve_peer(chat_id)
if isinstance(peer, types.InputPeerChannel):
return bool(
self.send(
functions.channels.UpdateUsername(
channel=peer,
username=username or ""
)
)
)
else:
raise ValueError("The chat_id \"{}\" belongs to a user or chat".format(chat_id))

View File

@ -24,8 +24,10 @@ from ...ext import BaseClient
class AddContacts(BaseClient): class AddContacts(BaseClient):
def add_contacts(self, def add_contacts(
contacts: List["pyrogram.InputPhoneContact"]): self,
contacts: List["pyrogram.InputPhoneContact"]
):
"""Use this method to add contacts to your Telegram address book. """Use this method to add contacts to your Telegram address book.
Args: Args:
@ -36,7 +38,7 @@ class AddContacts(BaseClient):
On success, the added contacts are returned. On success, the added contacts are returned.
Raises: Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error. :class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
""" """
imported_contacts = self.send( imported_contacts = self.send(
functions.contacts.ImportContacts( functions.contacts.ImportContacts(

View File

@ -19,14 +19,16 @@
from typing import List from typing import List
from pyrogram.api import functions, types from pyrogram.api import functions, types
from pyrogram.api.errors import PeerIdInvalid from pyrogram.errors import PeerIdInvalid
from ...ext import BaseClient from ...ext import BaseClient
class DeleteContacts(BaseClient): class DeleteContacts(BaseClient):
def delete_contacts(self, def delete_contacts(
ids: List[int]): self,
"""Use this method to delete contacts from your Telegram address book ids: List[int]
):
"""Use this method to delete contacts from your Telegram address book.
Args: Args:
ids (List of ``int``): ids (List of ``int``):
@ -37,7 +39,7 @@ class DeleteContacts(BaseClient):
True on success. True on success.
Raises: Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error. :class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
""" """
contacts = [] contacts = []

View File

@ -21,7 +21,7 @@ import time
import pyrogram import pyrogram
from pyrogram.api import functions from pyrogram.api import functions
from pyrogram.api.errors import FloodWait from pyrogram.errors import FloodWait
from ...ext import BaseClient from ...ext import BaseClient
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -35,11 +35,11 @@ class GetContacts(BaseClient):
On success, a list of :obj:`User` objects is returned. On success, a list of :obj:`User` objects is returned.
Raises: Raises:
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error. :class:`RPCError <pyrogram.RPCError>` in case of a Telegram RPC error.
""" """
while True: while True:
try: try:
contacts = self.send(functions.contacts.GetContacts(0)) contacts = self.send(functions.contacts.GetContacts(hash=0))
except FloodWait as e: except FloodWait as e:
log.warning("get_contacts flood: waiting {} seconds".format(e.x)) log.warning("get_contacts flood: waiting {} seconds".format(e.x))
time.sleep(e.x) time.sleep(e.x)

View File

@ -19,6 +19,7 @@
from .on_callback_query import OnCallbackQuery from .on_callback_query import OnCallbackQuery
from .on_deleted_messages import OnDeletedMessages from .on_deleted_messages import OnDeletedMessages
from .on_disconnect import OnDisconnect from .on_disconnect import OnDisconnect
from .on_inline_query import OnInlineQuery
from .on_message import OnMessage from .on_message import OnMessage
from .on_raw_update import OnRawUpdate from .on_raw_update import OnRawUpdate
from .on_user_status import OnUserStatus from .on_user_status import OnUserStatus
@ -30,6 +31,7 @@ class Decorators(
OnCallbackQuery, OnCallbackQuery,
OnRawUpdate, OnRawUpdate,
OnDisconnect, OnDisconnect,
OnUserStatus OnUserStatus,
OnInlineQuery
): ):
pass pass

View File

@ -25,19 +25,13 @@ from ...ext import BaseClient
class OnCallbackQuery(BaseClient): class OnCallbackQuery(BaseClient):
def on_callback_query(self=None, def on_callback_query(
filters=None, self=None,
group: int = 0) -> callable: filters=None,
"""Use this decorator to automatically register a function for handling group: int = 0
callback queries. This does the same thing as :meth:`add_handler` using the ) -> callable:
:class:`CallbackQueryHandler`. """Use this decorator to automatically register a function for handling callback queries.
This does the same thing as :meth:`add_handler` using the :class:`CallbackQueryHandler`.
.. note::
This decorator will wrap your defined function in a tuple consisting of *(Handler, group)*.
To reference your own function after it has been decorated, you need to access
*my_function[0].callback*, that is, the *callback* field of Handler object which is the the
first element in the tuple.
Args: Args:
filters (:obj:`Filters <pyrogram.Filters>`): filters (:obj:`Filters <pyrogram.Filters>`):

View File

@ -25,19 +25,13 @@ from ...ext import BaseClient
class OnDeletedMessages(BaseClient): class OnDeletedMessages(BaseClient):
def on_deleted_messages(self=None, def on_deleted_messages(
filters=None, self=None,
group: int = 0) -> callable: filters=None,
"""Use this decorator to automatically register a function for handling group: int = 0
deleted messages. This does the same thing as :meth:`add_handler` using the ) -> callable:
:class:`DeletedMessagesHandler`. """Use this decorator to automatically register a function for handling deleted messages.
This does the same thing as :meth:`add_handler` using the :class:`DeletedMessagesHandler`.
.. note::
This decorator will wrap your defined function in a tuple consisting of *(Handler, group)*.
To reference your own function after it has been decorated, you need to access
*my_function[0].callback*, that is, the *callback* field of Handler object which is the the
first element in the tuple.
Args: Args:
filters (:obj:`Filters <pyrogram.Filters>`): filters (:obj:`Filters <pyrogram.Filters>`):

View File

@ -23,9 +23,8 @@ from ...ext import BaseClient
class OnDisconnect(BaseClient): class OnDisconnect(BaseClient):
def on_disconnect(self=None) -> callable: def on_disconnect(self=None) -> callable:
"""Use this decorator to automatically register a function for handling """Use this decorator to automatically register a function for handling disconnections.
disconnections. This does the same thing as :meth:`add_handler` using the This does the same thing as :meth:`add_handler` using the :class:`DisconnectHandler`.
:class:`DisconnectHandler`.
""" """
def decorator(func: callable) -> Handler: def decorator(func: callable) -> Handler:

View File

@ -0,0 +1,59 @@
# 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 typing import Tuple
import pyrogram
from pyrogram.client.filters.filter import Filter
from pyrogram.client.handlers.handler import Handler
from ...ext import BaseClient
class OnInlineQuery(BaseClient):
def on_inline_query(
self=None,
filters=None,
group: int = 0
) -> callable:
"""Use this decorator to automatically register a function for handling inline queries.
This does the same thing as :meth:`add_handler` using the :class:`InlineQueryHandler`.
Args:
filters (:obj:`Filters <pyrogram.Filters>`):
Pass one or more filters to allow only a subset of inline queries to be passed
in your function.
group (``int``, *optional*):
The group identifier, defaults to 0.
"""
def decorator(func: callable) -> Tuple[Handler, int]:
if isinstance(func, tuple):
func = func[0].callback
handler = pyrogram.InlineQueryHandler(func, filters)
if isinstance(self, Filter):
return pyrogram.InlineQueryHandler(func, self), group if filters is None else filters
if self is not None:
self.add_handler(handler, group)
return handler, group
return decorator

View File

@ -25,19 +25,13 @@ from ...ext import BaseClient
class OnMessage(BaseClient): class OnMessage(BaseClient):
def on_message(self=None, def on_message(
filters=None, self=None,
group: int = 0) -> callable: filters=None,
"""Use this decorator to automatically register a function for handling group: int = 0
messages. This does the same thing as :meth:`add_handler` using the ) -> callable:
:class:`MessageHandler`. """Use this decorator to automatically register a function for handling messages.
This does the same thing as :meth:`add_handler` using the :class:`MessageHandler`.
.. note::
This decorator will wrap your defined function in a tuple consisting of *(Handler, group)*.
To reference your own function after it has been decorated, you need to access
*my_function[0].callback*, that is, the *callback* field of Handler object which is the the
first element in the tuple.
Args: Args:
filters (:obj:`Filters <pyrogram.Filters>`): filters (:obj:`Filters <pyrogram.Filters>`):

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