mirror of
https://github.com/pyrogram/pyrogram
synced 2025-08-28 04:48:06 +00:00
Merge branch 'develop' into develop
This commit is contained in:
commit
233e0920a2
25
README.rst
25
README.rst
@ -17,8 +17,8 @@ Pyrogram
|
|||||||
|
|
||||||
app.run()
|
app.run()
|
||||||
|
|
||||||
**Pyrogram** is a brand new Telegram_ Client Library written from the ground up in Python and C. It can be used for building
|
**Pyrogram** is a brand new Telegram_ Client Library written from the ground up in Python and C. It can be used for
|
||||||
custom Telegram applications that interact with the MTProto API as both User and Bot.
|
building custom Telegram applications that interact with the MTProto API as both User and Bot.
|
||||||
|
|
||||||
Features
|
Features
|
||||||
--------
|
--------
|
||||||
@ -26,7 +26,7 @@ Features
|
|||||||
- **Easy to use**: You can easily install Pyrogram using pip and start building your app right away.
|
- **Easy to use**: You can easily install Pyrogram using pip and start building your app right away.
|
||||||
- **High-level**: The low-level details of MTProto are abstracted and automatically handled.
|
- **High-level**: The low-level details of MTProto are abstracted and automatically handled.
|
||||||
- **Fast**: Crypto parts are boosted up by TgCrypto_, a high-performance library written in pure C.
|
- **Fast**: Crypto parts are boosted up by TgCrypto_, a high-performance library written in pure C.
|
||||||
- **Updated** to the latest Telegram API version, currently Layer 81 on top of MTProto 2.0.
|
- **Updated** to the latest Telegram API version, currently Layer 91 on top of MTProto 2.0.
|
||||||
- **Documented**: The Pyrogram API is well documented and resembles the Telegram Bot API.
|
- **Documented**: The Pyrogram API is well documented and resembles the Telegram Bot API.
|
||||||
- **Full API**, allowing to execute any advanced action an official client is able to do, and more.
|
- **Full API**, allowing to execute any advanced action an official client is able to do, and more.
|
||||||
|
|
||||||
@ -78,14 +78,13 @@ Copyright & License
|
|||||||
|
|
||||||
<h1 align="center">
|
<h1 align="center">
|
||||||
<a href="https://github.com/pyrogram/pyrogram">
|
<a href="https://github.com/pyrogram/pyrogram">
|
||||||
<div><img src="https://media.pyrogram.ml/images/icon.png" alt="Pyrogram Icon"></div>
|
<div><img src="https://raw.githubusercontent.com/pyrogram/logos/master/logos/pyrogram_logo2.png" alt="Pyrogram Logo"></div>
|
||||||
<div><img src="https://media.pyrogram.ml/images/label.png" alt="Pyrogram Label"></div>
|
|
||||||
</a>
|
</a>
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<b>Telegram MTProto API Client Library for Python</b>
|
<b>Telegram MTProto API Client Library for Python</b>
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
<a href="https://github.com/pyrogram/pyrogram/releases/latest">
|
<a href="https://github.com/pyrogram/pyrogram/releases/latest">
|
||||||
Download
|
Download
|
||||||
@ -98,27 +97,27 @@ Copyright & License
|
|||||||
<a href="https://t.me/PyrogramChat">
|
<a href="https://t.me/PyrogramChat">
|
||||||
Community
|
Community
|
||||||
</a>
|
</a>
|
||||||
<br><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/SCHEME-LAYER%2081-eda738.svg?longCache=true&style=for-the-badge&colorA=262b30"
|
<img src="https://img.shields.io/badge/schema-layer%2091-eda738.svg?longCache=true&colorA=262b30"
|
||||||
alt="Scheme Layer">
|
alt="Schema Layer">
|
||||||
</a>
|
</a>
|
||||||
<a href="https://github.com/pyrogram/tgcrypto">
|
<a href="https://github.com/pyrogram/tgcrypto">
|
||||||
<img src="https://img.shields.io/badge/TGCRYPTO-V1.0.4-eda738.svg?longCache=true&style=for-the-badge&colorA=262b30"
|
<img src="https://img.shields.io/badge/tgcrypto-v1.1.1-eda738.svg?longCache=true&colorA=262b30"
|
||||||
alt="TgCrypto">
|
alt="TgCrypto">
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
.. |logo| image:: https://pyrogram.ml/images/logo.png
|
.. |logo| image:: https://raw.githubusercontent.com/pyrogram/logos/master/logos/pyrogram_logo2.png
|
||||||
:target: https://pyrogram.ml
|
:target: https://pyrogram.ml
|
||||||
:alt: Pyrogram
|
:alt: Pyrogram
|
||||||
|
|
||||||
.. |description| replace:: **Telegram MTProto API Client Library for Python**
|
.. |description| replace:: **Telegram MTProto API Client Library for Python**
|
||||||
|
|
||||||
.. |scheme| image:: "https://img.shields.io/badge/SCHEME-LAYER%2081-eda738.svg?longCache=true&style=for-the-badge&colorA=262b30"
|
.. |scheme| image:: "https://img.shields.io/badge/schema-layer%2091-eda738.svg?longCache=true&colorA=262b30"
|
||||||
:target: compiler/api/source/main_api.tl
|
:target: compiler/api/source/main_api.tl
|
||||||
:alt: Scheme Layer
|
:alt: Scheme Layer
|
||||||
|
|
||||||
.. |tgcrypto| image:: "https://img.shields.io/badge/TGCRYPTO-V1.0.4-eda738.svg?longCache=true&style=for-the-badge&colorA=262b30"
|
.. |tgcrypto| image:: "https://img.shields.io/badge/tgcrypto-v1.1.1-eda738.svg?longCache=true&colorA=262b30"
|
||||||
:target: https://github.com/pyrogram/tgcrypto
|
:target: https://github.com/pyrogram/tgcrypto
|
||||||
:alt: TgCrypto
|
:alt: TgCrypto
|
||||||
|
@ -172,9 +172,8 @@ def start():
|
|||||||
|
|
||||||
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:
|
||||||
open("{}/source/pyrogram.tl".format(HOME), encoding="utf-8") as pyrogram:
|
schema = (auth.read() + system.read() + api.read()).splitlines()
|
||||||
schema = (auth.read() + system.read() + api.read() + pyrogram.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:
|
||||||
mtproto_template = f.read()
|
mtproto_template = f.read()
|
||||||
@ -476,35 +475,6 @@ def start():
|
|||||||
f.write("\n 0x3072cfa1: \"pyrogram.api.core.GzipPacked\",")
|
f.write("\n 0x3072cfa1: \"pyrogram.api.core.GzipPacked\",")
|
||||||
f.write("\n 0x5bb8e511: \"pyrogram.api.core.Message\",")
|
f.write("\n 0x5bb8e511: \"pyrogram.api.core.Message\",")
|
||||||
|
|
||||||
f.write("\n 0xb0700000: \"pyrogram.client.types.Update\",")
|
|
||||||
f.write("\n 0xb0700001: \"pyrogram.client.types.User\",")
|
|
||||||
f.write("\n 0xb0700002: \"pyrogram.client.types.Chat\",")
|
|
||||||
f.write("\n 0xb0700003: \"pyrogram.client.types.Message\",")
|
|
||||||
f.write("\n 0xb0700004: \"pyrogram.client.types.MessageEntity\",")
|
|
||||||
f.write("\n 0xb0700005: \"pyrogram.client.types.PhotoSize\",")
|
|
||||||
f.write("\n 0xb0700006: \"pyrogram.client.types.Audio\",")
|
|
||||||
f.write("\n 0xb0700007: \"pyrogram.client.types.Document\",")
|
|
||||||
f.write("\n 0xb0700008: \"pyrogram.client.types.Video\",")
|
|
||||||
f.write("\n 0xb0700009: \"pyrogram.client.types.Voice\",")
|
|
||||||
f.write("\n 0xb0700010: \"pyrogram.client.types.VideoNote\",")
|
|
||||||
f.write("\n 0xb0700011: \"pyrogram.client.types.Contact\",")
|
|
||||||
f.write("\n 0xb0700012: \"pyrogram.client.types.Location\",")
|
|
||||||
f.write("\n 0xb0700013: \"pyrogram.client.types.Venue\",")
|
|
||||||
f.write("\n 0xb0700014: \"pyrogram.client.types.UserProfilePhotos\",")
|
|
||||||
f.write("\n 0xb0700015: \"pyrogram.client.types.ChatPhoto\",")
|
|
||||||
f.write("\n 0xb0700016: \"pyrogram.client.types.ChatMember\",")
|
|
||||||
f.write("\n 0xb0700017: \"pyrogram.client.types.Sticker\",")
|
|
||||||
f.write("\n 0xb0700018: \"pyrogram.client.types.reply_markup.ForceReply\",")
|
|
||||||
f.write("\n 0xb0700019: \"pyrogram.client.types.reply_markup.InlineKeyboardButton\",")
|
|
||||||
f.write("\n 0xb0700020: \"pyrogram.client.types.reply_markup.InlineKeyboardMarkup\",")
|
|
||||||
f.write("\n 0xb0700021: \"pyrogram.client.types.reply_markup.KeyboardButton\",")
|
|
||||||
f.write("\n 0xb0700022: \"pyrogram.client.types.reply_markup.ReplyKeyboardMarkup\",")
|
|
||||||
f.write("\n 0xb0700023: \"pyrogram.client.types.reply_markup.ReplyKeyboardRemove\",")
|
|
||||||
f.write("\n 0xb0700024: \"pyrogram.client.types.CallbackQuery\",")
|
|
||||||
f.write("\n 0xb0700025: \"pyrogram.client.types.GIF\",")
|
|
||||||
f.write("\n 0xb0700026: \"pyrogram.client.types.Messages\",")
|
|
||||||
f.write("\n 0xb0700027: \"pyrogram.client.types.Photo\",")
|
|
||||||
|
|
||||||
f.write("\n}\n")
|
f.write("\n}\n")
|
||||||
|
|
||||||
for k, v in namespaces.items():
|
for k, v in namespaces.items():
|
||||||
|
@ -36,7 +36,7 @@ inputMediaEmpty#9664f57f = InputMedia;
|
|||||||
inputMediaUploadedPhoto#1e287d04 flags:# file:InputFile stickers:flags.0?Vector<InputDocument> ttl_seconds:flags.1?int = InputMedia;
|
inputMediaUploadedPhoto#1e287d04 flags:# file:InputFile stickers:flags.0?Vector<InputDocument> ttl_seconds:flags.1?int = InputMedia;
|
||||||
inputMediaPhoto#b3ba0635 flags:# id:InputPhoto ttl_seconds:flags.0?int = InputMedia;
|
inputMediaPhoto#b3ba0635 flags:# id:InputPhoto ttl_seconds:flags.0?int = InputMedia;
|
||||||
inputMediaGeoPoint#f9c44144 geo_point:InputGeoPoint = InputMedia;
|
inputMediaGeoPoint#f9c44144 geo_point:InputGeoPoint = InputMedia;
|
||||||
inputMediaContact#a6e45987 phone_number:string first_name:string last_name:string = InputMedia;
|
inputMediaContact#f8ab7dfb phone_number:string first_name:string last_name:string vcard:string = InputMedia;
|
||||||
inputMediaUploadedDocument#5b38c6c1 flags:# nosound_video:flags.3?true file:InputFile thumb:flags.2?InputFile mime_type:string attributes:Vector<DocumentAttribute> stickers:flags.0?Vector<InputDocument> ttl_seconds:flags.1?int = InputMedia;
|
inputMediaUploadedDocument#5b38c6c1 flags:# nosound_video:flags.3?true file:InputFile thumb:flags.2?InputFile mime_type:string attributes:Vector<DocumentAttribute> stickers:flags.0?Vector<InputDocument> ttl_seconds:flags.1?int = InputMedia;
|
||||||
inputMediaDocument#23ab23d2 flags:# id:InputDocument ttl_seconds:flags.0?int = InputMedia;
|
inputMediaDocument#23ab23d2 flags:# id:InputDocument ttl_seconds:flags.0?int = InputMedia;
|
||||||
inputMediaVenue#c13d1c11 geo_point:InputGeoPoint title:string address:string provider:string venue_id:string venue_type:string = InputMedia;
|
inputMediaVenue#c13d1c11 geo_point:InputGeoPoint title:string address:string provider:string venue_id:string venue_type:string = InputMedia;
|
||||||
@ -45,7 +45,8 @@ inputMediaPhotoExternal#e5bbfe1a flags:# url:string ttl_seconds:flags.0?int = In
|
|||||||
inputMediaDocumentExternal#fb52dc99 flags:# url:string ttl_seconds:flags.0?int = InputMedia;
|
inputMediaDocumentExternal#fb52dc99 flags:# url:string ttl_seconds:flags.0?int = InputMedia;
|
||||||
inputMediaGame#d33f43f3 id:InputGame = InputMedia;
|
inputMediaGame#d33f43f3 id:InputGame = InputMedia;
|
||||||
inputMediaInvoice#f4e096c3 flags:# title:string description:string photo:flags.0?InputWebDocument invoice:Invoice payload:bytes provider:string provider_data:DataJSON start_param:string = InputMedia;
|
inputMediaInvoice#f4e096c3 flags:# title:string description:string photo:flags.0?InputWebDocument invoice:Invoice payload:bytes provider:string provider_data:DataJSON start_param:string = InputMedia;
|
||||||
inputMediaGeoLive#7b1a118f geo_point:InputGeoPoint period:int = InputMedia;
|
inputMediaGeoLive#ce4e82fd flags:# stopped:flags.0?true geo_point:InputGeoPoint period:flags.1?int = InputMedia;
|
||||||
|
inputMediaPoll#6b3765b poll:Poll = InputMedia;
|
||||||
|
|
||||||
inputChatPhotoEmpty#1ca48f57 = InputChatPhoto;
|
inputChatPhotoEmpty#1ca48f57 = InputChatPhoto;
|
||||||
inputChatUploadedPhoto#927c55b4 file:InputFile = InputChatPhoto;
|
inputChatUploadedPhoto#927c55b4 file:InputFile = InputChatPhoto;
|
||||||
@ -55,14 +56,13 @@ inputGeoPointEmpty#e4c123d6 = InputGeoPoint;
|
|||||||
inputGeoPoint#f3b7acc9 lat:double long:double = InputGeoPoint;
|
inputGeoPoint#f3b7acc9 lat:double long:double = InputGeoPoint;
|
||||||
|
|
||||||
inputPhotoEmpty#1cd7bf0d = InputPhoto;
|
inputPhotoEmpty#1cd7bf0d = InputPhoto;
|
||||||
inputPhoto#fb95c6c4 id:long access_hash:long = InputPhoto;
|
inputPhoto#3bb3b94a id:long access_hash:long file_reference:bytes = InputPhoto;
|
||||||
|
|
||||||
inputFileLocation#14637196 volume_id:long local_id:int secret:long = InputFileLocation;
|
inputFileLocation#dfdaabe1 volume_id:long local_id:int secret:long file_reference:bytes = InputFileLocation;
|
||||||
inputEncryptedFileLocation#f5235d55 id:long access_hash:long = InputFileLocation;
|
inputEncryptedFileLocation#f5235d55 id:long access_hash:long = InputFileLocation;
|
||||||
inputDocumentFileLocation#430f0724 id:long access_hash:long version:int = InputFileLocation;
|
inputDocumentFileLocation#196683d9 id:long access_hash:long file_reference:bytes = InputFileLocation;
|
||||||
inputSecureFileLocation#cbc7ee28 id:long access_hash:long = InputFileLocation;
|
inputSecureFileLocation#cbc7ee28 id:long access_hash:long = InputFileLocation;
|
||||||
|
inputTakeoutFileLocation#29be5899 = InputFileLocation;
|
||||||
inputAppEvent#770656a8 time:double type:string peer:long data:string = InputAppEvent;
|
|
||||||
|
|
||||||
peerUser#9db1bc6d user_id:int = Peer;
|
peerUser#9db1bc6d user_id:int = Peer;
|
||||||
peerChat#bad0e5bb chat_id:int = Peer;
|
peerChat#bad0e5bb chat_id:int = Peer;
|
||||||
@ -80,7 +80,7 @@ storage.fileMp4#b3cea0e4 = storage.FileType;
|
|||||||
storage.fileWebp#1081464c = storage.FileType;
|
storage.fileWebp#1081464c = storage.FileType;
|
||||||
|
|
||||||
fileLocationUnavailable#7c596b46 volume_id:long local_id:int secret:long = FileLocation;
|
fileLocationUnavailable#7c596b46 volume_id:long local_id:int secret:long = FileLocation;
|
||||||
fileLocation#53d69076 dc_id:int volume_id:long local_id:int secret:long = FileLocation;
|
fileLocation#91d11eb dc_id:int volume_id:long local_id:int secret:long file_reference:bytes = FileLocation;
|
||||||
|
|
||||||
userEmpty#200250ba id:int = User;
|
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 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;
|
||||||
@ -101,8 +101,8 @@ chatForbidden#7328bdb id:int title:string = Chat;
|
|||||||
channel#c88974ac flags:# creator:flags.0?true left:flags.2?true editor:flags.3?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true democracy:flags.10?true signatures:flags.11?true min:flags.12?true id:int access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?string admin_rights:flags.14?ChannelAdminRights banned_rights:flags.15?ChannelBannedRights participants_count:flags.17?int = Chat;
|
channel#c88974ac flags:# creator:flags.0?true left:flags.2?true editor:flags.3?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true democracy:flags.10?true signatures:flags.11?true min:flags.12?true id:int access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?string admin_rights:flags.14?ChannelAdminRights banned_rights:flags.15?ChannelBannedRights participants_count:flags.17?int = Chat;
|
||||||
channelForbidden#289da732 flags:# broadcast:flags.5?true megagroup:flags.8?true id:int access_hash:long title:string until_date:flags.16?int = Chat;
|
channelForbidden#289da732 flags:# broadcast:flags.5?true megagroup:flags.8?true id:int access_hash:long title:string until_date:flags.16?int = Chat;
|
||||||
|
|
||||||
chatFull#2e02a614 id:int participants:ChatParticipants chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector<BotInfo> = ChatFull;
|
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;
|
||||||
channelFull#76af5481 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector<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;
|
||||||
chatParticipantCreator#da13538a user_id:int = ChatParticipant;
|
chatParticipantCreator#da13538a user_id:int = ChatParticipant;
|
||||||
@ -115,13 +115,13 @@ chatPhotoEmpty#37c1011c = ChatPhoto;
|
|||||||
chatPhoto#6153276a photo_small:FileLocation photo_big:FileLocation = ChatPhoto;
|
chatPhoto#6153276a photo_small:FileLocation photo_big:FileLocation = ChatPhoto;
|
||||||
|
|
||||||
messageEmpty#83e5de54 id:int = Message;
|
messageEmpty#83e5de54 id:int = Message;
|
||||||
message#44f9b43d flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true id:int from_id:flags.8?int to_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to_msg_id:flags.3?int date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long = Message;
|
message#44f9b43d flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true id:int from_id:flags.8?int to_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to_msg_id:flags.3?int date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long = Message;
|
||||||
messageService#9e19a1f6 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true id:int from_id:flags.8?int to_id:Peer reply_to_msg_id:flags.3?int date:int action:MessageAction = Message;
|
messageService#9e19a1f6 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true id:int from_id:flags.8?int to_id:Peer reply_to_msg_id:flags.3?int date:int action:MessageAction = Message;
|
||||||
|
|
||||||
messageMediaEmpty#3ded6320 = MessageMedia;
|
messageMediaEmpty#3ded6320 = MessageMedia;
|
||||||
messageMediaPhoto#695150d7 flags:# photo:flags.0?Photo ttl_seconds:flags.2?int = MessageMedia;
|
messageMediaPhoto#695150d7 flags:# photo:flags.0?Photo ttl_seconds:flags.2?int = MessageMedia;
|
||||||
messageMediaGeo#56e0d474 geo:GeoPoint = MessageMedia;
|
messageMediaGeo#56e0d474 geo:GeoPoint = MessageMedia;
|
||||||
messageMediaContact#5e7d2f39 phone_number:string first_name:string last_name:string user_id:int = MessageMedia;
|
messageMediaContact#cbf24940 phone_number:string first_name:string last_name:string vcard:string user_id:int = MessageMedia;
|
||||||
messageMediaUnsupported#9f84f49e = MessageMedia;
|
messageMediaUnsupported#9f84f49e = MessageMedia;
|
||||||
messageMediaDocument#9cb070d7 flags:# document:flags.0?Document ttl_seconds:flags.2?int = MessageMedia;
|
messageMediaDocument#9cb070d7 flags:# document:flags.0?Document ttl_seconds:flags.2?int = MessageMedia;
|
||||||
messageMediaWebPage#a32dd600 webpage:WebPage = MessageMedia;
|
messageMediaWebPage#a32dd600 webpage:WebPage = MessageMedia;
|
||||||
@ -129,6 +129,7 @@ messageMediaVenue#2ec0533f geo:GeoPoint title:string address:string provider:str
|
|||||||
messageMediaGame#fdb19008 game:Game = MessageMedia;
|
messageMediaGame#fdb19008 game:Game = MessageMedia;
|
||||||
messageMediaInvoice#84551347 flags:# shipping_address_requested:flags.1?true test:flags.3?true title:string description:string photo:flags.0?WebDocument receipt_msg_id:flags.2?int currency:string total_amount:long start_param:string = MessageMedia;
|
messageMediaInvoice#84551347 flags:# shipping_address_requested:flags.1?true test:flags.3?true title:string description:string photo:flags.0?WebDocument receipt_msg_id:flags.2?int currency:string total_amount:long start_param:string = MessageMedia;
|
||||||
messageMediaGeoLive#7c3c2609 geo:GeoPoint period:int = MessageMedia;
|
messageMediaGeoLive#7c3c2609 geo:GeoPoint period:int = MessageMedia;
|
||||||
|
messageMediaPoll#4bd6e798 poll:Poll results:PollResults = MessageMedia;
|
||||||
|
|
||||||
messageActionEmpty#b6aef7b0 = MessageAction;
|
messageActionEmpty#b6aef7b0 = MessageAction;
|
||||||
messageActionChatCreate#a6638b9a title:string users:Vector<int> = MessageAction;
|
messageActionChatCreate#a6638b9a title:string users:Vector<int> = MessageAction;
|
||||||
@ -152,18 +153,19 @@ messageActionCustomAction#fae69f56 message:string = MessageAction;
|
|||||||
messageActionBotAllowed#abe9affe domain:string = MessageAction;
|
messageActionBotAllowed#abe9affe domain:string = MessageAction;
|
||||||
messageActionSecureValuesSentMe#1b287353 values:Vector<SecureValue> credentials:SecureCredentialsEncrypted = MessageAction;
|
messageActionSecureValuesSentMe#1b287353 values:Vector<SecureValue> credentials:SecureCredentialsEncrypted = MessageAction;
|
||||||
messageActionSecureValuesSent#d95c6154 types:Vector<SecureValueType> = MessageAction;
|
messageActionSecureValuesSent#d95c6154 types:Vector<SecureValueType> = MessageAction;
|
||||||
|
messageActionContactSignUp#f3f25f76 = MessageAction;
|
||||||
|
|
||||||
dialog#e4def5db flags:# pinned:flags.2?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage = Dialog;
|
dialog#e4def5db flags:# pinned:flags.2?true unread_mark:flags.3?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage = Dialog;
|
||||||
|
|
||||||
photoEmpty#2331b22d id:long = Photo;
|
photoEmpty#2331b22d id:long = Photo;
|
||||||
photo#9288dd29 flags:# has_stickers:flags.0?true id:long access_hash:long date:int sizes:Vector<PhotoSize> = Photo;
|
photo#9c477dd8 flags:# has_stickers:flags.0?true id:long access_hash:long file_reference:bytes date:int sizes:Vector<PhotoSize> = Photo;
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
geoPointEmpty#1117dd5f = GeoPoint;
|
geoPointEmpty#1117dd5f = GeoPoint;
|
||||||
geoPoint#2049d70c long:double lat:double = GeoPoint;
|
geoPoint#296f104 long:double lat:double access_hash:long = GeoPoint;
|
||||||
|
|
||||||
auth.checkedPhone#811ea28e phone_registered:Bool = auth.CheckedPhone;
|
auth.checkedPhone#811ea28e phone_registered:Bool = auth.CheckedPhone;
|
||||||
|
|
||||||
@ -176,6 +178,7 @@ auth.exportedAuthorization#df969c2d id:int bytes:bytes = auth.ExportedAuthorizat
|
|||||||
inputNotifyPeer#b8bc5b0c peer:InputPeer = InputNotifyPeer;
|
inputNotifyPeer#b8bc5b0c peer:InputPeer = InputNotifyPeer;
|
||||||
inputNotifyUsers#193b4417 = InputNotifyPeer;
|
inputNotifyUsers#193b4417 = InputNotifyPeer;
|
||||||
inputNotifyChats#4a95e84e = InputNotifyPeer;
|
inputNotifyChats#4a95e84e = InputNotifyPeer;
|
||||||
|
inputNotifyBroadcasts#b1db7c7e = InputNotifyPeer;
|
||||||
|
|
||||||
inputPeerNotifySettings#9c3d198e flags:# show_previews:flags.0?Bool silent:flags.1?Bool mute_until:flags.2?int sound:flags.3?string = InputPeerNotifySettings;
|
inputPeerNotifySettings#9c3d198e flags:# show_previews:flags.0?Bool silent:flags.1?Bool mute_until:flags.2?int sound:flags.3?string = InputPeerNotifySettings;
|
||||||
|
|
||||||
@ -189,9 +192,11 @@ wallPaperSolid#63117f24 id:int title:string bg_color:int color:int = WallPaper;
|
|||||||
inputReportReasonSpam#58dbcab8 = ReportReason;
|
inputReportReasonSpam#58dbcab8 = ReportReason;
|
||||||
inputReportReasonViolence#1e22c78d = ReportReason;
|
inputReportReasonViolence#1e22c78d = ReportReason;
|
||||||
inputReportReasonPornography#2e59d922 = ReportReason;
|
inputReportReasonPornography#2e59d922 = ReportReason;
|
||||||
|
inputReportReasonChildAbuse#adf44ee3 = ReportReason;
|
||||||
inputReportReasonOther#e1746d0a text:string = ReportReason;
|
inputReportReasonOther#e1746d0a text:string = ReportReason;
|
||||||
|
inputReportReasonCopyright#9b89f93a = ReportReason;
|
||||||
|
|
||||||
userFull#f220f3f flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true user:User about:flags.1?string link:contacts.Link profile_photo:flags.2?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo common_chats_count:int = UserFull;
|
userFull#8ea4a881 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true user:User about:flags.1?string link:contacts.Link profile_photo:flags.2?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int = UserFull;
|
||||||
|
|
||||||
contact#f911c994 user_id:int mutual:Bool = Contact;
|
contact#f911c994 user_id:int mutual:Bool = Contact;
|
||||||
|
|
||||||
@ -213,10 +218,11 @@ contacts.blockedSlice#900802a1 count:int blocked:Vector<ContactBlocked> users:Ve
|
|||||||
|
|
||||||
messages.dialogs#15ba6c40 dialogs:Vector<Dialog> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.Dialogs;
|
messages.dialogs#15ba6c40 dialogs:Vector<Dialog> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.Dialogs;
|
||||||
messages.dialogsSlice#71e094f3 count:int dialogs:Vector<Dialog> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.Dialogs;
|
messages.dialogsSlice#71e094f3 count:int dialogs:Vector<Dialog> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.Dialogs;
|
||||||
|
messages.dialogsNotModified#f0e3e596 count:int = messages.Dialogs;
|
||||||
|
|
||||||
messages.messages#8c718e87 messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.Messages;
|
messages.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#b446ae3 count:int messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.Messages;
|
||||||
messages.channelMessages#99262e37 flags:# 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;
|
||||||
|
|
||||||
messages.chats#64ff9fd5 chats:Vector<Chat> = messages.Chats;
|
messages.chats#64ff9fd5 chats:Vector<Chat> = messages.Chats;
|
||||||
@ -252,7 +258,6 @@ updateChatParticipants#7761198 participants:ChatParticipants = Update;
|
|||||||
updateUserStatus#1bfbd823 user_id:int status:UserStatus = Update;
|
updateUserStatus#1bfbd823 user_id:int status:UserStatus = Update;
|
||||||
updateUserName#a7332b73 user_id:int first_name:string last_name:string username:string = Update;
|
updateUserName#a7332b73 user_id:int first_name:string last_name:string username:string = Update;
|
||||||
updateUserPhoto#95313b0c user_id:int date:int photo:UserProfilePhoto previous:Bool = Update;
|
updateUserPhoto#95313b0c user_id:int date:int photo:UserProfilePhoto previous:Bool = Update;
|
||||||
updateContactRegistered#2575bbb9 user_id:int date:int = Update;
|
|
||||||
updateContactLink#9d2e67c5 user_id:int my_link:ContactLink foreign_link:ContactLink = Update;
|
updateContactLink#9d2e67c5 user_id:int my_link:ContactLink foreign_link:ContactLink = Update;
|
||||||
updateNewEncryptedMessage#12bcbd9a message:EncryptedMessage qts:int = Update;
|
updateNewEncryptedMessage#12bcbd9a message:EncryptedMessage qts:int = Update;
|
||||||
updateEncryptedChatTyping#1710f156 chat_id:int = Update;
|
updateEncryptedChatTyping#1710f156 chat_id:int = Update;
|
||||||
@ -303,12 +308,16 @@ updateBotWebhookJSONQuery#9b9240a6 query_id:long data:DataJSON timeout:int = Upd
|
|||||||
updateBotShippingQuery#e0cdc940 query_id:long user_id:int payload:bytes shipping_address:PostAddress = Update;
|
updateBotShippingQuery#e0cdc940 query_id:long user_id:int payload:bytes shipping_address:PostAddress = Update;
|
||||||
updateBotPrecheckoutQuery#5d2f3aa9 flags:# query_id:long user_id:int payload:bytes info:flags.0?PaymentRequestedInfo shipping_option_id:flags.1?string currency:string total_amount:long = Update;
|
updateBotPrecheckoutQuery#5d2f3aa9 flags:# query_id:long user_id:int payload:bytes info:flags.0?PaymentRequestedInfo shipping_option_id:flags.1?string currency:string total_amount:long = Update;
|
||||||
updatePhoneCall#ab0f6b1e phone_call:PhoneCall = Update;
|
updatePhoneCall#ab0f6b1e phone_call:PhoneCall = Update;
|
||||||
updateLangPackTooLong#10c2404b = Update;
|
updateLangPackTooLong#46560264 lang_code:string = Update;
|
||||||
updateLangPack#56022f4d difference:LangPackDifference = Update;
|
updateLangPack#56022f4d difference:LangPackDifference = Update;
|
||||||
updateFavedStickers#e511996d = Update;
|
updateFavedStickers#e511996d = Update;
|
||||||
updateChannelReadMessagesContents#89893b45 channel_id:int messages:Vector<int> = Update;
|
updateChannelReadMessagesContents#89893b45 channel_id:int messages:Vector<int> = Update;
|
||||||
updateContactsReset#7084a7be = Update;
|
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;
|
||||||
|
updateUserPinnedMessage#4c43da18 user_id:int id:int = Update;
|
||||||
|
updateChatPinnedMessage#22893b26 chat_id:int id:int = Update;
|
||||||
|
updateMessagePoll#aca1657b flags:# poll_id:long poll:flags.0?Poll results:PollResults = Update;
|
||||||
|
|
||||||
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
|
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
|
||||||
|
|
||||||
@ -335,11 +344,11 @@ upload.fileCdnRedirect#f18cda44 dc_id:int file_token:bytes encryption_key:bytes
|
|||||||
|
|
||||||
dcOption#18b7a10d flags:# ipv6:flags.0?true media_only:flags.1?true tcpo_only:flags.2?true cdn:flags.3?true static:flags.4?true id:int ip_address:string port:int secret:flags.10?bytes = DcOption;
|
dcOption#18b7a10d flags:# ipv6:flags.0?true media_only:flags.1?true tcpo_only:flags.2?true cdn:flags.3?true static:flags.4?true id:int ip_address:string port:int secret:flags.10?bytes = DcOption;
|
||||||
|
|
||||||
config#eb7bb160 flags:# phonecalls_enabled:flags.1?true default_p2p_contacts:flags.3?true preload_featured_stickers:flags.4?true ignore_phone_entities:flags.5?true revoke_pm_inbox:flags.6?true blocked_mode:flags.8?true date:int expires:int test_mode:Bool this_dc:int dc_options:Vector<DcOption> chat_size_max:int megagroup_size_max:int forwarded_count_max:int online_update_period_ms:int offline_blur_timeout_ms:int offline_idle_timeout_ms:int online_cloud_timeout_ms:int notify_cloud_delay_ms:int notify_default_delay_ms:int push_chat_period_ms:int push_chat_limit:int saved_gifs_limit:int edit_time_limit:int revoke_time_limit:int revoke_pm_time_limit:int rating_e_decay:int stickers_recent_limit:int stickers_faved_limit:int channels_read_media_period:int tmp_sessions:flags.0?int pinned_dialogs_count_max:int call_receive_timeout_ms:int call_ring_timeout_ms:int call_connect_timeout_ms:int call_packet_timeout_ms:int me_url_prefix:string autoupdate_url_prefix:flags.7?string suggested_lang_code:flags.2?string lang_pack_version:flags.2?int = Config;
|
config#e6ca25f6 flags:# phonecalls_enabled:flags.1?true default_p2p_contacts:flags.3?true preload_featured_stickers:flags.4?true ignore_phone_entities:flags.5?true revoke_pm_inbox:flags.6?true blocked_mode:flags.8?true pfs_enabled:flags.13?true date:int expires:int test_mode:Bool this_dc:int dc_options:Vector<DcOption> dc_txt_domain_name:string chat_size_max:int megagroup_size_max:int forwarded_count_max:int online_update_period_ms:int offline_blur_timeout_ms:int offline_idle_timeout_ms:int online_cloud_timeout_ms:int notify_cloud_delay_ms:int notify_default_delay_ms:int push_chat_period_ms:int push_chat_limit:int saved_gifs_limit:int edit_time_limit:int revoke_time_limit:int revoke_pm_time_limit:int rating_e_decay:int stickers_recent_limit:int stickers_faved_limit:int channels_read_media_period:int tmp_sessions:flags.0?int pinned_dialogs_count_max:int call_receive_timeout_ms:int call_ring_timeout_ms:int call_connect_timeout_ms:int call_packet_timeout_ms:int me_url_prefix:string autoupdate_url_prefix:flags.7?string gif_search_username:flags.9?string venue_search_username:flags.10?string img_search_username:flags.11?string static_maps_provider:flags.12?string caption_length_max:int message_length_max:int webfile_dc_id:int suggested_lang_code:flags.2?string lang_pack_version:flags.2?int base_lang_pack_version:flags.2?int = Config;
|
||||||
|
|
||||||
nearestDc#8e1a1775 country:string this_dc:int nearest_dc:int = NearestDc;
|
nearestDc#8e1a1775 country:string this_dc:int nearest_dc:int = NearestDc;
|
||||||
|
|
||||||
help.appUpdate#8987f311 id:int critical:Bool url:string text:string = help.AppUpdate;
|
help.appUpdate#1da7158f flags:# popup:flags.0?true id:int version:string text:string entities:Vector<MessageEntity> document:flags.1?Document url:flags.2?string = help.AppUpdate;
|
||||||
help.noAppUpdate#c45a6536 = help.AppUpdate;
|
help.noAppUpdate#c45a6536 = help.AppUpdate;
|
||||||
|
|
||||||
help.inviteText#18cb9f78 message:string = help.InviteText;
|
help.inviteText#18cb9f78 message:string = help.InviteText;
|
||||||
@ -370,16 +379,17 @@ messages.sentEncryptedMessage#560f8935 date:int = messages.SentEncryptedMessage;
|
|||||||
messages.sentEncryptedFile#9493ff32 date:int file:EncryptedFile = messages.SentEncryptedMessage;
|
messages.sentEncryptedFile#9493ff32 date:int file:EncryptedFile = messages.SentEncryptedMessage;
|
||||||
|
|
||||||
inputDocumentEmpty#72f0eaae = InputDocument;
|
inputDocumentEmpty#72f0eaae = InputDocument;
|
||||||
inputDocument#18798952 id:long access_hash:long = InputDocument;
|
inputDocument#1abfb575 id:long access_hash:long file_reference:bytes = InputDocument;
|
||||||
|
|
||||||
documentEmpty#36f8c871 id:long = Document;
|
documentEmpty#36f8c871 id:long = Document;
|
||||||
document#87232bc7 id:long access_hash:long date:int mime_type:string size:int thumb:PhotoSize dc_id:int version:int attributes:Vector<DocumentAttribute> = 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;
|
||||||
|
|
||||||
help.support#17c6b5f6 phone_number:string user:User = help.Support;
|
help.support#17c6b5f6 phone_number:string user:User = help.Support;
|
||||||
|
|
||||||
notifyPeer#9fd40bd8 peer:Peer = NotifyPeer;
|
notifyPeer#9fd40bd8 peer:Peer = NotifyPeer;
|
||||||
notifyUsers#b4c83b4c = NotifyPeer;
|
notifyUsers#b4c83b4c = NotifyPeer;
|
||||||
notifyChats#c007cec3 = NotifyPeer;
|
notifyChats#c007cec3 = NotifyPeer;
|
||||||
|
notifyBroadcasts#d612e8ef = NotifyPeer;
|
||||||
|
|
||||||
sendMessageTypingAction#16bf744e = SendMessageAction;
|
sendMessageTypingAction#16bf744e = SendMessageAction;
|
||||||
sendMessageCancelAction#fd5ec8f5 = SendMessageAction;
|
sendMessageCancelAction#fd5ec8f5 = SendMessageAction;
|
||||||
@ -400,10 +410,12 @@ contacts.found#b3134d9d my_results:Vector<Peer> results:Vector<Peer> chats:Vecto
|
|||||||
inputPrivacyKeyStatusTimestamp#4f96cb18 = InputPrivacyKey;
|
inputPrivacyKeyStatusTimestamp#4f96cb18 = InputPrivacyKey;
|
||||||
inputPrivacyKeyChatInvite#bdfb0426 = InputPrivacyKey;
|
inputPrivacyKeyChatInvite#bdfb0426 = InputPrivacyKey;
|
||||||
inputPrivacyKeyPhoneCall#fabadc5f = InputPrivacyKey;
|
inputPrivacyKeyPhoneCall#fabadc5f = InputPrivacyKey;
|
||||||
|
inputPrivacyKeyPhoneP2P#db9e70d2 = InputPrivacyKey;
|
||||||
|
|
||||||
privacyKeyStatusTimestamp#bc2eab30 = PrivacyKey;
|
privacyKeyStatusTimestamp#bc2eab30 = PrivacyKey;
|
||||||
privacyKeyChatInvite#500e6dfa = PrivacyKey;
|
privacyKeyChatInvite#500e6dfa = PrivacyKey;
|
||||||
privacyKeyPhoneCall#3d662b7b = PrivacyKey;
|
privacyKeyPhoneCall#3d662b7b = PrivacyKey;
|
||||||
|
privacyKeyPhoneP2P#39491cc8 = PrivacyKey;
|
||||||
|
|
||||||
inputPrivacyValueAllowContacts#d09e07b = InputPrivacyRule;
|
inputPrivacyValueAllowContacts#d09e07b = InputPrivacyRule;
|
||||||
inputPrivacyValueAllowAll#184b35ce = InputPrivacyRule;
|
inputPrivacyValueAllowAll#184b35ce = InputPrivacyRule;
|
||||||
@ -451,16 +463,15 @@ webPagePending#c586da1c id:long date:int = WebPage;
|
|||||||
webPage#5f07b4bc flags:# id:long url:string display_url:string hash:int type:flags.0?string site_name:flags.1?string title:flags.2?string description:flags.3?string photo:flags.4?Photo embed_url:flags.5?string embed_type:flags.5?string embed_width:flags.6?int embed_height:flags.6?int duration:flags.7?int author:flags.8?string document:flags.9?Document cached_page:flags.10?Page = WebPage;
|
webPage#5f07b4bc flags:# id:long url:string display_url:string hash:int type:flags.0?string site_name:flags.1?string title:flags.2?string description:flags.3?string photo:flags.4?Photo embed_url:flags.5?string embed_type:flags.5?string embed_width:flags.6?int embed_height:flags.6?int duration:flags.7?int author:flags.8?string document:flags.9?Document cached_page:flags.10?Page = WebPage;
|
||||||
webPageNotModified#85849473 = WebPage;
|
webPageNotModified#85849473 = WebPage;
|
||||||
|
|
||||||
authorization#7bf2e6f6 hash:long flags:int device_model:string platform:string system_version:string api_id:int app_name:string app_version:string date_created:int date_active:int ip:string country:string region:string = Authorization;
|
authorization#ad01d61d flags:# current:flags.0?true official_app:flags.1?true password_pending:flags.2?true hash:long device_model:string platform:string system_version:string api_id:int app_name:string app_version:string date_created:int date_active:int ip:string country:string region:string = Authorization;
|
||||||
|
|
||||||
account.authorizations#1250abde authorizations:Vector<Authorization> = account.Authorizations;
|
account.authorizations#1250abde authorizations:Vector<Authorization> = account.Authorizations;
|
||||||
|
|
||||||
account.noPassword#5ea182f6 new_salt:bytes new_secure_salt:bytes secure_random:bytes email_unconfirmed_pattern:string = account.Password;
|
account.password#ad2641f8 flags:# has_recovery:flags.0?true has_secure_values:flags.1?true has_password:flags.2?true current_algo:flags.2?PasswordKdfAlgo srp_B:flags.2?bytes srp_id:flags.2?long hint:flags.3?string email_unconfirmed_pattern:flags.4?string new_algo:PasswordKdfAlgo new_secure_algo:SecurePasswordKdfAlgo secure_random:bytes = account.Password;
|
||||||
account.password#ca39b447 flags:# has_recovery:flags.0?true has_secure_values:flags.1?true current_salt:bytes new_salt:bytes new_secure_salt:bytes secure_random:bytes hint:string email_unconfirmed_pattern:string = account.Password;
|
|
||||||
|
|
||||||
account.passwordSettings#7bd9c3f1 email:string secure_salt:bytes secure_secret:bytes secure_secret_id:long = account.PasswordSettings;
|
account.passwordSettings#9a5c33e5 flags:# email:flags.0?string secure_settings:flags.1?SecureSecretSettings = account.PasswordSettings;
|
||||||
|
|
||||||
account.passwordInputSettings#21ffa60d flags:# new_salt:flags.0?bytes new_password_hash:flags.0?bytes hint:flags.0?string email:flags.1?string new_secure_salt:flags.2?bytes new_secure_secret:flags.2?bytes new_secure_secret_id:flags.2?long = account.PasswordInputSettings;
|
account.passwordInputSettings#c23727c9 flags:# new_algo:flags.0?PasswordKdfAlgo new_password_hash:flags.0?bytes hint:flags.0?string email:flags.1?string new_secure_settings:flags.2?SecureSecretSettings = account.PasswordInputSettings;
|
||||||
|
|
||||||
auth.passwordRecovery#137948a5 email_pattern:string = auth.PasswordRecovery;
|
auth.passwordRecovery#137948a5 email_pattern:string = auth.PasswordRecovery;
|
||||||
|
|
||||||
@ -562,7 +573,7 @@ inputBotInlineMessageMediaAuto#3380c786 flags:# message:string entities:flags.1?
|
|||||||
inputBotInlineMessageText#3dcd7a87 flags:# no_webpage:flags.0?true message:string entities:flags.1?Vector<MessageEntity> reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
|
inputBotInlineMessageText#3dcd7a87 flags:# no_webpage:flags.0?true message:string entities:flags.1?Vector<MessageEntity> reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
|
||||||
inputBotInlineMessageMediaGeo#c1b15d65 flags:# geo_point:InputGeoPoint period:int reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
|
inputBotInlineMessageMediaGeo#c1b15d65 flags:# geo_point:InputGeoPoint period:int reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
|
||||||
inputBotInlineMessageMediaVenue#417bbf11 flags:# geo_point:InputGeoPoint title:string address:string provider:string venue_id:string venue_type:string reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
|
inputBotInlineMessageMediaVenue#417bbf11 flags:# geo_point:InputGeoPoint title:string address:string provider:string venue_id:string venue_type:string reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
|
||||||
inputBotInlineMessageMediaContact#2daf01a7 flags:# phone_number:string first_name:string last_name:string reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
|
inputBotInlineMessageMediaContact#a6edbffd flags:# phone_number:string first_name:string last_name:string vcard:string reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
|
||||||
inputBotInlineMessageGame#4b425864 flags:# reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
|
inputBotInlineMessageGame#4b425864 flags:# reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
|
||||||
|
|
||||||
inputBotInlineResult#88bf9319 flags:# id:string type:string title:flags.1?string description:flags.2?string url:flags.3?string thumb:flags.4?InputWebDocument content:flags.5?InputWebDocument send_message:InputBotInlineMessage = InputBotInlineResult;
|
inputBotInlineResult#88bf9319 flags:# id:string type:string title:flags.1?string description:flags.2?string url:flags.3?string thumb:flags.4?InputWebDocument content:flags.5?InputWebDocument send_message:InputBotInlineMessage = InputBotInlineResult;
|
||||||
@ -574,7 +585,7 @@ botInlineMessageMediaAuto#764cf810 flags:# message:string entities:flags.1?Vecto
|
|||||||
botInlineMessageText#8c7f65e2 flags:# no_webpage:flags.0?true message:string entities:flags.1?Vector<MessageEntity> reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
|
botInlineMessageText#8c7f65e2 flags:# no_webpage:flags.0?true message:string entities:flags.1?Vector<MessageEntity> reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
|
||||||
botInlineMessageMediaGeo#b722de65 flags:# geo:GeoPoint period:int reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
|
botInlineMessageMediaGeo#b722de65 flags:# geo:GeoPoint period:int reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
|
||||||
botInlineMessageMediaVenue#8a86659c flags:# geo:GeoPoint title:string address:string provider:string venue_id:string venue_type:string reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
|
botInlineMessageMediaVenue#8a86659c flags:# geo:GeoPoint title:string address:string provider:string venue_id:string venue_type:string reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
|
||||||
botInlineMessageMediaContact#35edb4d4 flags:# phone_number:string first_name:string last_name:string reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
|
botInlineMessageMediaContact#18d1cdc2 flags:# phone_number:string first_name:string last_name:string vcard:string reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
|
||||||
|
|
||||||
botInlineResult#11965f3a flags:# id:string type:string title:flags.1?string description:flags.2?string url:flags.3?string thumb:flags.4?WebDocument content:flags.5?WebDocument send_message:BotInlineMessage = BotInlineResult;
|
botInlineResult#11965f3a flags:# id:string type:string title:flags.1?string description:flags.2?string url:flags.3?string thumb:flags.4?WebDocument content:flags.5?WebDocument send_message:BotInlineMessage = BotInlineResult;
|
||||||
botInlineMediaResult#17db940b flags:# id:string type:string photo:flags.0?Photo document:flags.1?Document title:flags.2?string description:flags.3?string send_message:BotInlineMessage = BotInlineResult;
|
botInlineMediaResult#17db940b flags:# id:string type:string photo:flags.0?Photo document:flags.1?Document title:flags.2?string description:flags.3?string send_message:BotInlineMessage = BotInlineResult;
|
||||||
@ -617,8 +628,9 @@ topPeerCategoryPeers#fb834291 category:TopPeerCategory count:int peers:Vector<To
|
|||||||
|
|
||||||
contacts.topPeersNotModified#de266ef5 = contacts.TopPeers;
|
contacts.topPeersNotModified#de266ef5 = contacts.TopPeers;
|
||||||
contacts.topPeers#70b772a8 categories:Vector<TopPeerCategoryPeers> chats:Vector<Chat> users:Vector<User> = contacts.TopPeers;
|
contacts.topPeers#70b772a8 categories:Vector<TopPeerCategoryPeers> chats:Vector<Chat> users:Vector<User> = contacts.TopPeers;
|
||||||
|
contacts.topPeersDisabled#b52c939d = contacts.TopPeers;
|
||||||
|
|
||||||
draftMessageEmpty#ba4baec5 = DraftMessage;
|
draftMessageEmpty#1b0c841a flags:# date:flags.0?int = DraftMessage;
|
||||||
draftMessage#fd8e711f flags:# no_webpage:flags.1?true reply_to_msg_id:flags.0?int message:string entities:flags.3?Vector<MessageEntity> date:int = DraftMessage;
|
draftMessage#fd8e711f flags:# no_webpage:flags.1?true reply_to_msg_id:flags.0?int message:string entities:flags.3?Vector<MessageEntity> date:int = DraftMessage;
|
||||||
|
|
||||||
messages.featuredStickersNotModified#4ede3cf = messages.FeaturedStickers;
|
messages.featuredStickersNotModified#4ede3cf = messages.FeaturedStickers;
|
||||||
@ -659,6 +671,12 @@ textFixed#6c3f19b9 text:RichText = RichText;
|
|||||||
textUrl#3c2884c1 text:RichText url:string webpage_id:long = RichText;
|
textUrl#3c2884c1 text:RichText url:string webpage_id:long = RichText;
|
||||||
textEmail#de5a0dd6 text:RichText email:string = RichText;
|
textEmail#de5a0dd6 text:RichText email:string = RichText;
|
||||||
textConcat#7e6260d7 texts:Vector<RichText> = RichText;
|
textConcat#7e6260d7 texts:Vector<RichText> = RichText;
|
||||||
|
textSubscript#ed6a8504 text:RichText = RichText;
|
||||||
|
textSuperscript#c7fb5e01 text:RichText = RichText;
|
||||||
|
textMarked#34b8621 text:RichText = RichText;
|
||||||
|
textPhone#1ccb966a text:RichText phone:string = RichText;
|
||||||
|
textImage#81ccf4f document_id:long w:int h:int = RichText;
|
||||||
|
textAnchor#35553762 text:RichText name:string = RichText;
|
||||||
|
|
||||||
pageBlockUnsupported#13567e8a = PageBlock;
|
pageBlockUnsupported#13567e8a = PageBlock;
|
||||||
pageBlockTitle#70abc3fd text:RichText = PageBlock;
|
pageBlockTitle#70abc3fd text:RichText = PageBlock;
|
||||||
@ -671,21 +689,24 @@ pageBlockPreformatted#c070d93e text:RichText language:string = PageBlock;
|
|||||||
pageBlockFooter#48870999 text:RichText = PageBlock;
|
pageBlockFooter#48870999 text:RichText = PageBlock;
|
||||||
pageBlockDivider#db20b188 = PageBlock;
|
pageBlockDivider#db20b188 = PageBlock;
|
||||||
pageBlockAnchor#ce0d37b0 name:string = PageBlock;
|
pageBlockAnchor#ce0d37b0 name:string = PageBlock;
|
||||||
pageBlockList#3a58c7f4 ordered:Bool items:Vector<RichText> = PageBlock;
|
pageBlockList#e4e88011 items:Vector<PageListItem> = PageBlock;
|
||||||
pageBlockBlockquote#263d7c26 text:RichText caption:RichText = PageBlock;
|
pageBlockBlockquote#263d7c26 text:RichText caption:RichText = PageBlock;
|
||||||
pageBlockPullquote#4f4456d3 text:RichText caption:RichText = PageBlock;
|
pageBlockPullquote#4f4456d3 text:RichText caption:RichText = PageBlock;
|
||||||
pageBlockPhoto#e9c69982 photo_id:long caption:RichText = PageBlock;
|
pageBlockPhoto#1759c560 flags:# photo_id:long caption:PageCaption url:flags.0?string webpage_id:flags.0?long = PageBlock;
|
||||||
pageBlockVideo#d9d71866 flags:# autoplay:flags.0?true loop:flags.1?true video_id:long caption:RichText = PageBlock;
|
pageBlockVideo#7c8fe7b6 flags:# autoplay:flags.0?true loop:flags.1?true video_id:long caption:PageCaption = PageBlock;
|
||||||
pageBlockCover#39f23300 cover:PageBlock = PageBlock;
|
pageBlockCover#39f23300 cover:PageBlock = PageBlock;
|
||||||
pageBlockEmbed#cde200d1 flags:# full_width:flags.0?true allow_scrolling:flags.3?true url:flags.1?string html:flags.2?string poster_photo_id:flags.4?long w:int h:int caption:RichText = PageBlock;
|
pageBlockEmbed#a8718dc5 flags:# full_width:flags.0?true allow_scrolling:flags.3?true url:flags.1?string html:flags.2?string poster_photo_id:flags.4?long w:flags.5?int h:flags.5?int caption:PageCaption = PageBlock;
|
||||||
pageBlockEmbedPost#292c7be9 url:string webpage_id:long author_photo_id:long author:string date:int blocks:Vector<PageBlock> caption:RichText = PageBlock;
|
pageBlockEmbedPost#f259a80b url:string webpage_id:long author_photo_id:long author:string date:int blocks:Vector<PageBlock> caption:PageCaption = PageBlock;
|
||||||
pageBlockCollage#8b31c4f items:Vector<PageBlock> caption:RichText = PageBlock;
|
pageBlockCollage#65a0fa4d items:Vector<PageBlock> caption:PageCaption = PageBlock;
|
||||||
pageBlockSlideshow#130c8963 items:Vector<PageBlock> caption:RichText = PageBlock;
|
pageBlockSlideshow#31f9590 items:Vector<PageBlock> caption:PageCaption = PageBlock;
|
||||||
pageBlockChannel#ef1751b5 channel:Chat = PageBlock;
|
pageBlockChannel#ef1751b5 channel:Chat = PageBlock;
|
||||||
pageBlockAudio#31b81a7f audio_id:long caption:RichText = PageBlock;
|
pageBlockAudio#804361ea audio_id:long caption:PageCaption = PageBlock;
|
||||||
|
pageBlockKicker#1e148390 text:RichText = PageBlock;
|
||||||
pagePart#8e3f9ebe blocks:Vector<PageBlock> photos:Vector<Photo> documents:Vector<Document> = Page;
|
pageBlockTable#bf4dea82 flags:# bordered:flags.0?true striped:flags.1?true title:RichText rows:Vector<PageTableRow> = PageBlock;
|
||||||
pageFull#556ec7aa blocks:Vector<PageBlock> photos:Vector<Photo> documents:Vector<Document> = Page;
|
pageBlockOrderedList#9a8ae1e1 items:Vector<PageListOrderedItem> = PageBlock;
|
||||||
|
pageBlockDetails#76768bed flags:# open:flags.0?true blocks:Vector<PageBlock> title:RichText = PageBlock;
|
||||||
|
pageBlockRelatedArticles#16115a96 title:RichText articles:Vector<PageRelatedArticle> = PageBlock;
|
||||||
|
pageBlockMap#a44f3ef6 geo:GeoPoint zoom:int w:int h:int caption:PageCaption = PageBlock;
|
||||||
|
|
||||||
phoneCallDiscardReasonMissed#85e42301 = PhoneCallDiscardReason;
|
phoneCallDiscardReasonMissed#85e42301 = PhoneCallDiscardReason;
|
||||||
phoneCallDiscardReasonDisconnect#e095c1a0 = PhoneCallDiscardReason;
|
phoneCallDiscardReasonDisconnect#e095c1a0 = PhoneCallDiscardReason;
|
||||||
@ -706,14 +727,13 @@ paymentRequestedInfo#909c3f94 flags:# name:flags.0?string phone:flags.1?string e
|
|||||||
|
|
||||||
paymentSavedCredentialsCard#cdc27a1f id:string title:string = PaymentSavedCredentials;
|
paymentSavedCredentialsCard#cdc27a1f id:string title:string = PaymentSavedCredentials;
|
||||||
|
|
||||||
webDocument#c61acbd8 url:string access_hash:long size:int mime_type:string attributes:Vector<DocumentAttribute> dc_id:int = WebDocument;
|
webDocument#1c570ed1 url:string access_hash:long size:int mime_type:string attributes:Vector<DocumentAttribute> = WebDocument;
|
||||||
webDocumentNoProxy#f9c8bcc6 url:string size:int mime_type:string attributes:Vector<DocumentAttribute> = WebDocument;
|
webDocumentNoProxy#f9c8bcc6 url:string size:int mime_type:string attributes:Vector<DocumentAttribute> = WebDocument;
|
||||||
|
|
||||||
inputWebDocument#9bed434d url:string size:int mime_type:string attributes:Vector<DocumentAttribute> = InputWebDocument;
|
inputWebDocument#9bed434d url:string size:int mime_type:string attributes:Vector<DocumentAttribute> = InputWebDocument;
|
||||||
|
|
||||||
inputWebFileLocation#c239d686 url:string access_hash:long = InputWebFileLocation;
|
inputWebFileLocation#c239d686 url:string access_hash:long = InputWebFileLocation;
|
||||||
inputWebFileGeoPointLocation#66275a62 geo_point:InputGeoPoint w:int h:int zoom:int scale:int = InputWebFileLocation;
|
inputWebFileGeoPointLocation#9f2221c9 geo_point:InputGeoPoint access_hash:long w:int h:int zoom:int scale:int = InputWebFileLocation;
|
||||||
inputWebFileGeoMessageLocation#553f32eb peer:InputPeer msg_id:int w:int h:int zoom:int scale:int = InputWebFileLocation;
|
|
||||||
|
|
||||||
upload.webFile#21e753bc size:int mime_type:string file_type:storage.FileType mtime:int bytes:bytes = upload.WebFile;
|
upload.webFile#21e753bc size:int mime_type:string file_type:storage.FileType mtime:int bytes:bytes = upload.WebFile;
|
||||||
|
|
||||||
@ -745,7 +765,7 @@ phoneCallEmpty#5366c915 id:long = PhoneCall;
|
|||||||
phoneCallWaiting#1b8f4ad1 flags:# id:long access_hash:long date:int admin_id:int participant_id:int protocol:PhoneCallProtocol receive_date:flags.0?int = PhoneCall;
|
phoneCallWaiting#1b8f4ad1 flags:# id:long access_hash:long date:int admin_id:int participant_id:int protocol:PhoneCallProtocol receive_date:flags.0?int = PhoneCall;
|
||||||
phoneCallRequested#83761ce4 id:long access_hash:long date:int admin_id:int participant_id:int g_a_hash:bytes protocol:PhoneCallProtocol = PhoneCall;
|
phoneCallRequested#83761ce4 id:long access_hash:long date:int admin_id:int participant_id:int g_a_hash:bytes protocol:PhoneCallProtocol = PhoneCall;
|
||||||
phoneCallAccepted#6d003d3f id:long access_hash:long date:int admin_id:int participant_id:int g_b:bytes protocol:PhoneCallProtocol = PhoneCall;
|
phoneCallAccepted#6d003d3f id:long access_hash:long date:int admin_id:int participant_id:int g_b:bytes protocol:PhoneCallProtocol = PhoneCall;
|
||||||
phoneCall#ffe6ab67 id:long access_hash:long date:int admin_id:int participant_id:int g_a_or_b:bytes key_fingerprint:long protocol:PhoneCallProtocol connection:PhoneConnection alternative_connections:Vector<PhoneConnection> start_date:int = PhoneCall;
|
phoneCall#e6f9ddf3 flags:# p2p_allowed:flags.5?true id:long access_hash:long date:int admin_id:int participant_id:int g_a_or_b:bytes key_fingerprint:long protocol:PhoneCallProtocol connection:PhoneConnection alternative_connections:Vector<PhoneConnection> start_date:int = PhoneCall;
|
||||||
phoneCallDiscarded#50ca4de1 flags:# need_rating:flags.2?true need_debug:flags.3?true id:long reason:flags.0?PhoneCallDiscardReason duration:flags.1?int = PhoneCall;
|
phoneCallDiscarded#50ca4de1 flags:# need_rating:flags.2?true need_debug:flags.3?true id:long reason:flags.0?PhoneCallDiscardReason duration:flags.1?int = PhoneCall;
|
||||||
|
|
||||||
phoneConnection#9d4c17c0 id:long ip:string ipv6:string port:int peer_tag:bytes = PhoneConnection;
|
phoneConnection#9d4c17c0 id:long ip:string ipv6:string port:int peer_tag:bytes = PhoneConnection;
|
||||||
@ -767,7 +787,7 @@ langPackStringDeleted#2979eeb2 key:string = LangPackString;
|
|||||||
|
|
||||||
langPackDifference#f385c1f6 lang_code:string from_version:int version:int strings:Vector<LangPackString> = LangPackDifference;
|
langPackDifference#f385c1f6 lang_code:string from_version:int version:int strings:Vector<LangPackString> = LangPackDifference;
|
||||||
|
|
||||||
langPackLanguage#117698f1 name:string native_name:string lang_code:string = LangPackLanguage;
|
langPackLanguage#eeca5ce3 flags:# official:flags.0?true rtl:flags.2?true beta:flags.3?true name:string native_name:string lang_code:string base_lang_code:flags.1?string plural_code:string strings_count:int translated_count:int translations_url:string = LangPackLanguage;
|
||||||
|
|
||||||
channelAdminRights#5d7ceba5 flags:# change_info:flags.0?true post_messages:flags.1?true edit_messages:flags.2?true delete_messages:flags.3?true ban_users:flags.4?true invite_users:flags.5?true invite_link:flags.6?true pin_messages:flags.7?true add_admins:flags.9?true manage_call:flags.10?true = ChannelAdminRights;
|
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;
|
||||||
|
|
||||||
@ -861,9 +881,9 @@ secureValueTypeTemporaryRegistration#ea02ec33 = SecureValueType;
|
|||||||
secureValueTypePhone#b320aadb = SecureValueType;
|
secureValueTypePhone#b320aadb = SecureValueType;
|
||||||
secureValueTypeEmail#8e3ca7ee = SecureValueType;
|
secureValueTypeEmail#8e3ca7ee = SecureValueType;
|
||||||
|
|
||||||
secureValue#b4b4b699 flags:# type:SecureValueType data:flags.0?SecureData front_side:flags.1?SecureFile reverse_side:flags.2?SecureFile selfie:flags.3?SecureFile files:flags.4?Vector<SecureFile> plain_data:flags.5?SecurePlainData hash:bytes = SecureValue;
|
secureValue#187fa0ca flags:# type:SecureValueType data:flags.0?SecureData front_side:flags.1?SecureFile reverse_side:flags.2?SecureFile selfie:flags.3?SecureFile translation:flags.6?Vector<SecureFile> files:flags.4?Vector<SecureFile> plain_data:flags.5?SecurePlainData hash:bytes = SecureValue;
|
||||||
|
|
||||||
inputSecureValue#67872e8 flags:# type:SecureValueType data:flags.0?SecureData front_side:flags.1?InputSecureFile reverse_side:flags.2?InputSecureFile selfie:flags.3?InputSecureFile files:flags.4?Vector<InputSecureFile> plain_data:flags.5?SecurePlainData = InputSecureValue;
|
inputSecureValue#db21d0a7 flags:# type:SecureValueType data:flags.0?SecureData front_side:flags.1?InputSecureFile reverse_side:flags.2?InputSecureFile selfie:flags.3?InputSecureFile translation:flags.6?Vector<InputSecureFile> files:flags.4?Vector<InputSecureFile> plain_data:flags.5?SecurePlainData = InputSecureValue;
|
||||||
|
|
||||||
secureValueHash#ed1ecdb0 type:SecureValueType hash:bytes = SecureValueHash;
|
secureValueHash#ed1ecdb0 type:SecureValueType hash:bytes = SecureValueHash;
|
||||||
|
|
||||||
@ -873,16 +893,85 @@ secureValueErrorReverseSide#868a2aa5 type:SecureValueType file_hash:bytes text:s
|
|||||||
secureValueErrorSelfie#e537ced6 type:SecureValueType file_hash:bytes text:string = SecureValueError;
|
secureValueErrorSelfie#e537ced6 type:SecureValueType file_hash:bytes text:string = SecureValueError;
|
||||||
secureValueErrorFile#7a700873 type:SecureValueType file_hash:bytes text:string = SecureValueError;
|
secureValueErrorFile#7a700873 type:SecureValueType file_hash:bytes text:string = SecureValueError;
|
||||||
secureValueErrorFiles#666220e9 type:SecureValueType file_hash:Vector<bytes> text:string = SecureValueError;
|
secureValueErrorFiles#666220e9 type:SecureValueType file_hash:Vector<bytes> text:string = SecureValueError;
|
||||||
|
secureValueError#869d758f type:SecureValueType hash:bytes text:string = SecureValueError;
|
||||||
|
secureValueErrorTranslationFile#a1144770 type:SecureValueType file_hash:bytes text:string = SecureValueError;
|
||||||
|
secureValueErrorTranslationFiles#34636dd8 type:SecureValueType file_hash:Vector<bytes> text:string = SecureValueError;
|
||||||
|
|
||||||
secureCredentialsEncrypted#33f0ea47 data:bytes hash:bytes secret:bytes = SecureCredentialsEncrypted;
|
secureCredentialsEncrypted#33f0ea47 data:bytes hash:bytes secret:bytes = SecureCredentialsEncrypted;
|
||||||
|
|
||||||
account.authorizationForm#cb976d53 flags:# selfie_required:flags.1?true required_types:Vector<SecureValueType> values:Vector<SecureValue> errors:Vector<SecureValueError> users:Vector<User> privacy_policy_url:flags.0?string = account.AuthorizationForm;
|
account.authorizationForm#ad2e1cd8 flags:# required_types:Vector<SecureRequiredType> values:Vector<SecureValue> errors:Vector<SecureValueError> users:Vector<User> privacy_policy_url:flags.0?string = account.AuthorizationForm;
|
||||||
|
|
||||||
account.sentEmailCode#811f854f email_pattern:string length:int = account.SentEmailCode;
|
account.sentEmailCode#811f854f email_pattern:string length:int = account.SentEmailCode;
|
||||||
|
|
||||||
help.deepLinkInfoEmpty#66afa166 = help.DeepLinkInfo;
|
help.deepLinkInfoEmpty#66afa166 = help.DeepLinkInfo;
|
||||||
help.deepLinkInfo#6a4ee832 flags:# update_app:flags.0?true message:string entities:flags.1?Vector<MessageEntity> = help.DeepLinkInfo;
|
help.deepLinkInfo#6a4ee832 flags:# update_app:flags.0?true message:string entities:flags.1?Vector<MessageEntity> = help.DeepLinkInfo;
|
||||||
|
|
||||||
|
savedPhoneContact#1142bd56 phone:string first_name:string last_name:string date:int = SavedContact;
|
||||||
|
|
||||||
|
account.takeout#4dba4501 id:long = account.Takeout;
|
||||||
|
|
||||||
|
passwordKdfAlgoUnknown#d45ab096 = PasswordKdfAlgo;
|
||||||
|
passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow#3a912d4a salt1:bytes salt2:bytes g:int p:bytes = PasswordKdfAlgo;
|
||||||
|
|
||||||
|
securePasswordKdfAlgoUnknown#4a8537 = SecurePasswordKdfAlgo;
|
||||||
|
securePasswordKdfAlgoPBKDF2HMACSHA512iter100000#bbf2dda0 salt:bytes = SecurePasswordKdfAlgo;
|
||||||
|
securePasswordKdfAlgoSHA512#86471d92 salt:bytes = SecurePasswordKdfAlgo;
|
||||||
|
|
||||||
|
secureSecretSettings#1527bcac secure_algo:SecurePasswordKdfAlgo secure_secret:bytes secure_secret_id:long = SecureSecretSettings;
|
||||||
|
|
||||||
|
inputCheckPasswordEmpty#9880f658 = InputCheckPasswordSRP;
|
||||||
|
inputCheckPasswordSRP#d27ff082 srp_id:long A:bytes M1:bytes = InputCheckPasswordSRP;
|
||||||
|
|
||||||
|
secureRequiredType#829d99da flags:# native_names:flags.0?true selfie_required:flags.1?true translation_required:flags.2?true type:SecureValueType = SecureRequiredType;
|
||||||
|
secureRequiredTypeOneOf#27477b4 types:Vector<SecureRequiredType> = SecureRequiredType;
|
||||||
|
|
||||||
|
help.passportConfigNotModified#bfb9f457 = help.PassportConfig;
|
||||||
|
help.passportConfig#a098d6af hash:int countries_langs:DataJSON = help.PassportConfig;
|
||||||
|
|
||||||
|
inputAppEvent#1d1b1245 time:double type:string peer:long data:JSONValue = InputAppEvent;
|
||||||
|
|
||||||
|
jsonObjectValue#c0de1bd9 key:string value:JSONValue = JSONObjectValue;
|
||||||
|
|
||||||
|
jsonNull#3f6d7b68 = JSONValue;
|
||||||
|
jsonBool#c7345e6a value:Bool = JSONValue;
|
||||||
|
jsonNumber#2be0dfa4 value:double = JSONValue;
|
||||||
|
jsonString#b71e767a value:string = JSONValue;
|
||||||
|
jsonArray#f7444763 value:Vector<JSONValue> = JSONValue;
|
||||||
|
jsonObject#99c1d49d value:Vector<JSONObjectValue> = JSONValue;
|
||||||
|
|
||||||
|
pageTableCell#34566b6a flags:# header:flags.0?true align_center:flags.3?true align_right:flags.4?true valign_middle:flags.5?true valign_bottom:flags.6?true text:flags.7?RichText colspan:flags.1?int rowspan:flags.2?int = PageTableCell;
|
||||||
|
|
||||||
|
pageTableRow#e0c0c5e5 cells:Vector<PageTableCell> = PageTableRow;
|
||||||
|
|
||||||
|
pageCaption#6f747657 text:RichText credit:RichText = PageCaption;
|
||||||
|
|
||||||
|
pageListItemText#b92fb6cd text:RichText = PageListItem;
|
||||||
|
pageListItemBlocks#25e073fc blocks:Vector<PageBlock> = PageListItem;
|
||||||
|
|
||||||
|
pageListOrderedItemText#5e068047 num:string text:RichText = PageListOrderedItem;
|
||||||
|
pageListOrderedItemBlocks#98dd8936 num:string blocks:Vector<PageBlock> = PageListOrderedItem;
|
||||||
|
|
||||||
|
pageRelatedArticle#b390dc08 flags:# url:string webpage_id:long title:flags.0?string description:flags.1?string photo_id:flags.2?long author:flags.3?string published_date:flags.4?int = PageRelatedArticle;
|
||||||
|
|
||||||
|
page#ae891bec flags:# part:flags.0?true rtl:flags.1?true v2:flags.2?true url:string blocks:Vector<PageBlock> photos:Vector<Photo> documents:Vector<Document> = Page;
|
||||||
|
|
||||||
|
help.supportName#8c05f1c9 name:string = help.SupportName;
|
||||||
|
|
||||||
|
help.userInfoEmpty#f3ae2eed = help.UserInfo;
|
||||||
|
help.userInfo#1eb3758 message:string entities:Vector<MessageEntity> author:string date:int = help.UserInfo;
|
||||||
|
|
||||||
|
pollAnswer#6ca9c2e9 text:string option:bytes = PollAnswer;
|
||||||
|
|
||||||
|
poll#d5529d06 id:long flags:# closed:flags.0?true question:string answers:Vector<PollAnswer> = Poll;
|
||||||
|
|
||||||
|
pollAnswerVoters#3b6ddad2 flags:# chosen:flags.0?true option:bytes voters:int = PollAnswerVoters;
|
||||||
|
|
||||||
|
pollResults#5755785a flags:# min:flags.0?true results:flags.1?Vector<PollAnswerVoters> total_voters:flags.2?int = PollResults;
|
||||||
|
|
||||||
|
chatOnlines#f041e250 onlines:int = ChatOnlines;
|
||||||
|
|
||||||
|
statsURL#47a971e0 url:string = StatsURL;
|
||||||
|
|
||||||
---functions---
|
---functions---
|
||||||
|
|
||||||
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
|
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
|
||||||
@ -890,18 +979,19 @@ invokeAfterMsgs#3dc4b4f0 {X:Type} msg_ids:Vector<long> query:!X = X;
|
|||||||
initConnection#785188b8 {X:Type} flags:# api_id:int device_model:string system_version:string app_version:string system_lang_code:string lang_pack:string lang_code:string proxy:flags.0?InputClientProxy query:!X = X;
|
initConnection#785188b8 {X:Type} flags:# api_id:int device_model:string system_version:string app_version:string system_lang_code:string lang_pack:string lang_code:string proxy:flags.0?InputClientProxy query:!X = X;
|
||||||
invokeWithLayer#da9b0d0d {X:Type} layer:int query:!X = X;
|
invokeWithLayer#da9b0d0d {X:Type} layer:int query:!X = X;
|
||||||
invokeWithoutUpdates#bf9459b7 {X:Type} query:!X = X;
|
invokeWithoutUpdates#bf9459b7 {X:Type} query:!X = X;
|
||||||
|
invokeWithMessagesRange#365275f2 {X:Type} range:MessageRange query:!X = X;
|
||||||
|
invokeWithTakeout#aca9fd2e {X:Type} takeout_id:long query:!X = X;
|
||||||
|
|
||||||
auth.sendCode#86aef0ec flags:# allow_flashcall:flags.0?true phone_number:string current_number:flags.0?Bool api_id:int api_hash:string = auth.SentCode;
|
auth.sendCode#86aef0ec flags:# allow_flashcall:flags.0?true phone_number:string current_number:flags.0?Bool api_id:int api_hash:string = auth.SentCode;
|
||||||
auth.signUp#1b067634 phone_number:string phone_code_hash:string phone_code:string first_name:string last_name:string = auth.Authorization;
|
auth.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;
|
||||||
auth.resetAuthorizations#9fab0d1a = Bool;
|
auth.resetAuthorizations#9fab0d1a = Bool;
|
||||||
auth.sendInvites#771c1d97 phone_numbers:Vector<string> message:string = Bool;
|
|
||||||
auth.exportAuthorization#e5bfffcd dc_id:int = auth.ExportedAuthorization;
|
auth.exportAuthorization#e5bfffcd dc_id:int = auth.ExportedAuthorization;
|
||||||
auth.importAuthorization#e3ef9613 id:int bytes:bytes = auth.Authorization;
|
auth.importAuthorization#e3ef9613 id:int bytes:bytes = auth.Authorization;
|
||||||
auth.bindTempAuthKey#cdd42a05 perm_auth_key_id:long nonce:long expires_at:int encrypted_message:bytes = Bool;
|
auth.bindTempAuthKey#cdd42a05 perm_auth_key_id:long nonce:long expires_at:int encrypted_message:bytes = Bool;
|
||||||
auth.importBotAuthorization#67a3ff2c flags:int api_id:int api_hash:string bot_auth_token:string = auth.Authorization;
|
auth.importBotAuthorization#67a3ff2c flags:int api_id:int api_hash:string bot_auth_token:string = auth.Authorization;
|
||||||
auth.checkPassword#a63011e password_hash:bytes = auth.Authorization;
|
auth.checkPassword#d18b4d16 password:InputCheckPasswordSRP = auth.Authorization;
|
||||||
auth.requestPasswordRecovery#d897bc66 = auth.PasswordRecovery;
|
auth.requestPasswordRecovery#d897bc66 = auth.PasswordRecovery;
|
||||||
auth.recoverPassword#4ea56e92 code:string = auth.Authorization;
|
auth.recoverPassword#4ea56e92 code:string = auth.Authorization;
|
||||||
auth.resendCode#3ef1a9bf phone_number:string phone_code_hash:string = auth.SentCode;
|
auth.resendCode#3ef1a9bf phone_number:string phone_code_hash:string = auth.SentCode;
|
||||||
@ -930,11 +1020,11 @@ account.updateDeviceLocked#38df3532 period:int = Bool;
|
|||||||
account.getAuthorizations#e320c158 = account.Authorizations;
|
account.getAuthorizations#e320c158 = account.Authorizations;
|
||||||
account.resetAuthorization#df77f3bc hash:long = Bool;
|
account.resetAuthorization#df77f3bc hash:long = Bool;
|
||||||
account.getPassword#548a30f5 = account.Password;
|
account.getPassword#548a30f5 = account.Password;
|
||||||
account.getPasswordSettings#bc8d11bb current_password_hash:bytes = account.PasswordSettings;
|
account.getPasswordSettings#9cd4eaf9 password:InputCheckPasswordSRP = account.PasswordSettings;
|
||||||
account.updatePasswordSettings#fa7c4b86 current_password_hash:bytes 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#1516d7bd flags:# allow_flashcall:flags.0?true hash:string current_number:flags.0?Bool = auth.SentCode;
|
||||||
account.confirmPhone#5f2178c3 phone_code_hash:string phone_code:string = Bool;
|
account.confirmPhone#5f2178c3 phone_code_hash:string phone_code:string = Bool;
|
||||||
account.getTmpPassword#4a82327e password_hash:bytes period:int = account.TmpPassword;
|
account.getTmpPassword#449e0b51 password:InputCheckPasswordSRP period:int = account.TmpPassword;
|
||||||
account.getWebAuthorizations#182e6d6f = account.WebAuthorizations;
|
account.getWebAuthorizations#182e6d6f = account.WebAuthorizations;
|
||||||
account.resetWebAuthorization#2d01b9ef hash:long = Bool;
|
account.resetWebAuthorization#2d01b9ef hash:long = Bool;
|
||||||
account.resetWebAuthorizations#682d2594 = Bool;
|
account.resetWebAuthorizations#682d2594 = Bool;
|
||||||
@ -948,29 +1038,39 @@ account.sendVerifyPhoneCode#823380b4 flags:# allow_flashcall:flags.0?true phone_
|
|||||||
account.verifyPhone#4dd3a7f6 phone_number:string phone_code_hash:string phone_code:string = Bool;
|
account.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;
|
||||||
|
account.initTakeoutSession#f05b4804 flags:# contacts:flags.0?true message_users:flags.1?true message_chats:flags.2?true message_megagroups:flags.3?true message_channels:flags.4?true files:flags.5?true file_max_size:flags.5?int = account.Takeout;
|
||||||
|
account.finishTakeoutSession#1d2652ee flags:# success:flags.0?true = Bool;
|
||||||
|
account.confirmPasswordEmail#8fdf1920 code:string = Bool;
|
||||||
|
account.resendPasswordEmail#7a7f2a15 = Bool;
|
||||||
|
account.cancelPasswordEmail#c1cbd5b6 = Bool;
|
||||||
|
account.getContactSignUpNotification#9f07c728 = Bool;
|
||||||
|
account.setContactSignUpNotification#cff43f61 silent:Bool = Bool;
|
||||||
|
account.getNotifyExceptions#53577479 flags:# compare_sound:flags.1?true peer:flags.0?InputNotifyPeer = Updates;
|
||||||
|
|
||||||
users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
|
users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
|
||||||
users.getFullUser#ca30a5b1 id:InputUser = UserFull;
|
users.getFullUser#ca30a5b1 id:InputUser = UserFull;
|
||||||
users.setSecureValueErrors#90c894b5 id:InputUser errors:Vector<SecureValueError> = Bool;
|
users.setSecureValueErrors#90c894b5 id:InputUser errors:Vector<SecureValueError> = Bool;
|
||||||
|
|
||||||
|
contacts.getContactIDs#2caa4a42 hash:int = Vector<int>;
|
||||||
contacts.getStatuses#c4a353ee = Vector<ContactStatus>;
|
contacts.getStatuses#c4a353ee = Vector<ContactStatus>;
|
||||||
contacts.getContacts#c023849f hash:int = contacts.Contacts;
|
contacts.getContacts#c023849f hash:int = contacts.Contacts;
|
||||||
contacts.importContacts#2c800be5 contacts:Vector<InputContact> = contacts.ImportedContacts;
|
contacts.importContacts#2c800be5 contacts:Vector<InputContact> = contacts.ImportedContacts;
|
||||||
contacts.deleteContact#8e953744 id:InputUser = contacts.Link;
|
contacts.deleteContact#8e953744 id:InputUser = contacts.Link;
|
||||||
contacts.deleteContacts#59ab389e id:Vector<InputUser> = Bool;
|
contacts.deleteContacts#59ab389e id:Vector<InputUser> = Bool;
|
||||||
|
contacts.deleteByPhones#1013fd9e phones:Vector<string> = Bool;
|
||||||
contacts.block#332b49fc id:InputUser = Bool;
|
contacts.block#332b49fc id:InputUser = Bool;
|
||||||
contacts.unblock#e54100bd id:InputUser = Bool;
|
contacts.unblock#e54100bd id:InputUser = Bool;
|
||||||
contacts.getBlocked#f57c350f offset:int limit:int = contacts.Blocked;
|
contacts.getBlocked#f57c350f offset:int limit:int = contacts.Blocked;
|
||||||
contacts.exportCard#84e53737 = Vector<int>;
|
|
||||||
contacts.importCard#4fe196fe export_card:Vector<int> = User;
|
|
||||||
contacts.search#11f812d8 q:string limit:int = contacts.Found;
|
contacts.search#11f812d8 q:string limit:int = contacts.Found;
|
||||||
contacts.resolveUsername#f93ccba3 username:string = contacts.ResolvedPeer;
|
contacts.resolveUsername#f93ccba3 username:string = contacts.ResolvedPeer;
|
||||||
contacts.getTopPeers#d4982db5 flags:# correspondents:flags.0?true bots_pm:flags.1?true bots_inline:flags.2?true phone_calls:flags.3?true groups:flags.10?true channels:flags.15?true offset:int limit:int hash:int = contacts.TopPeers;
|
contacts.getTopPeers#d4982db5 flags:# correspondents:flags.0?true bots_pm:flags.1?true bots_inline:flags.2?true phone_calls:flags.3?true groups:flags.10?true channels:flags.15?true offset:int limit:int hash:int = contacts.TopPeers;
|
||||||
contacts.resetTopPeerRating#1ae373ac category:TopPeerCategory peer:InputPeer = Bool;
|
contacts.resetTopPeerRating#1ae373ac category:TopPeerCategory peer:InputPeer = Bool;
|
||||||
contacts.resetSaved#879537f1 = Bool;
|
contacts.resetSaved#879537f1 = Bool;
|
||||||
|
contacts.getSaved#82f1e39f = Vector<SavedContact>;
|
||||||
|
contacts.toggleTopPeers#8514bdda enabled:Bool = Bool;
|
||||||
|
|
||||||
messages.getMessages#63c66506 id:Vector<InputMessage> = messages.Messages;
|
messages.getMessages#63c66506 id:Vector<InputMessage> = messages.Messages;
|
||||||
messages.getDialogs#191ba9c5 flags:# exclude_pinned:flags.0?true offset_date:int offset_id:int offset_peer:InputPeer limit:int = messages.Dialogs;
|
messages.getDialogs#b098aee6 flags:# exclude_pinned:flags.0?true offset_date:int offset_id:int offset_peer:InputPeer limit:int hash:int = messages.Dialogs;
|
||||||
messages.getHistory#dcbb8260 peer:InputPeer offset_id:int offset_date:int add_offset:int limit:int max_id:int min_id:int hash:int = messages.Messages;
|
messages.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;
|
||||||
@ -1026,10 +1126,10 @@ messages.getSavedGifs#83bf3d52 hash:int = messages.SavedGifs;
|
|||||||
messages.saveGif#327a30cb id:InputDocument unsave:Bool = Bool;
|
messages.saveGif#327a30cb id:InputDocument unsave:Bool = Bool;
|
||||||
messages.getInlineBotResults#514e999d flags:# bot:InputUser peer:InputPeer geo_point:flags.0?InputGeoPoint query:string offset:string = messages.BotResults;
|
messages.getInlineBotResults#514e999d flags:# bot:InputUser peer:InputPeer geo_point:flags.0?InputGeoPoint query:string offset:string = messages.BotResults;
|
||||||
messages.setInlineBotResults#eb5ea206 flags:# gallery:flags.0?true private:flags.1?true query_id:long results:Vector<InputBotInlineResult> cache_time:int next_offset:flags.2?string switch_pm:flags.3?InlineBotSwitchPM = Bool;
|
messages.setInlineBotResults#eb5ea206 flags:# gallery:flags.0?true private:flags.1?true query_id:long results:Vector<InputBotInlineResult> cache_time:int next_offset:flags.2?string switch_pm:flags.3?InlineBotSwitchPM = Bool;
|
||||||
messages.sendInlineBotResult#b16e06fe flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int random_id:long query_id:long id:string = Updates;
|
messages.sendInlineBotResult#b16e06fe flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true hide_via:flags.11?true peer:InputPeer reply_to_msg_id:flags.0?int random_id:long query_id:long id:string = Updates;
|
||||||
messages.getMessageEditData#fda68d36 peer:InputPeer id:int = messages.MessageEditData;
|
messages.getMessageEditData#fda68d36 peer:InputPeer id:int = messages.MessageEditData;
|
||||||
messages.editMessage#c000e4c8 flags:# no_webpage:flags.1?true stop_geo_live:flags.12?true peer:InputPeer id:int message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> geo_point:flags.13?InputGeoPoint = Updates;
|
messages.editMessage#d116f31e flags:# no_webpage:flags.1?true peer:InputPeer id:int message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> = Updates;
|
||||||
messages.editInlineBotMessage#adc3e828 flags:# no_webpage:flags.1?true stop_geo_live:flags.12?true id:InputBotInlineMessageID message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> geo_point:flags.13?InputGeoPoint = Bool;
|
messages.editInlineBotMessage#83557dba flags:# no_webpage:flags.1?true id:InputBotInlineMessageID message:flags.11?string media:flags.14?InputMedia reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> = Bool;
|
||||||
messages.getBotCallbackAnswer#810a9fec flags:# game:flags.1?true peer:InputPeer msg_id:int data:flags.0?bytes = messages.BotCallbackAnswer;
|
messages.getBotCallbackAnswer#810a9fec flags:# game:flags.1?true peer:InputPeer msg_id:int data:flags.0?bytes = messages.BotCallbackAnswer;
|
||||||
messages.setBotCallbackAnswer#d58f130a flags:# alert:flags.1?true query_id:long message:flags.0?string url:flags.2?string cache_time:int = Bool;
|
messages.setBotCallbackAnswer#d58f130a flags:# alert:flags.1?true query_id:long message:flags.0?string url:flags.2?string cache_time:int = Bool;
|
||||||
messages.getPeerDialogs#e470bcfd peers:Vector<InputDialogPeer> = messages.PeerDialogs;
|
messages.getPeerDialogs#e470bcfd peers:Vector<InputDialogPeer> = messages.PeerDialogs;
|
||||||
@ -1065,6 +1165,15 @@ messages.getRecentLocations#bbc45b09 peer:InputPeer limit:int hash:int = message
|
|||||||
messages.sendMultiMedia#2095512f flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int multi_media:Vector<InputSingleMedia> = Updates;
|
messages.sendMultiMedia#2095512f flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int multi_media:Vector<InputSingleMedia> = Updates;
|
||||||
messages.uploadEncryptedFile#5057c497 peer:InputEncryptedChat file:InputEncryptedFile = EncryptedFile;
|
messages.uploadEncryptedFile#5057c497 peer:InputEncryptedChat file:InputEncryptedFile = EncryptedFile;
|
||||||
messages.searchStickerSets#c2b7d08b flags:# exclude_featured:flags.0?true q:string hash:int = messages.FoundStickerSets;
|
messages.searchStickerSets#c2b7d08b flags:# exclude_featured:flags.0?true q:string hash:int = messages.FoundStickerSets;
|
||||||
|
messages.getSplitRanges#1cff7e08 = Vector<MessageRange>;
|
||||||
|
messages.markDialogUnread#c286d98f flags:# unread:flags.0?true peer:InputDialogPeer = Bool;
|
||||||
|
messages.getDialogUnreadMarks#22e24e22 = Vector<DialogPeer>;
|
||||||
|
messages.clearAllDrafts#7e58ee9c = Bool;
|
||||||
|
messages.updatePinnedMessage#d2aaf7ec flags:# silent:flags.0?true peer:InputPeer id:int = Updates;
|
||||||
|
messages.sendVote#10ea6184 peer:InputPeer msg_id:int options:Vector<bytes> = Updates;
|
||||||
|
messages.getPollResults#73bb643b peer:InputPeer msg_id:int = Updates;
|
||||||
|
messages.getOnlines#6e2be050 peer:InputPeer = ChatOnlines;
|
||||||
|
messages.getStatsURL#83f6c0cd peer:InputPeer = StatsURL;
|
||||||
|
|
||||||
updates.getState#edd4882a = updates.State;
|
updates.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;
|
||||||
@ -1086,8 +1195,7 @@ upload.getFileHashes#c7025931 location:InputFileLocation offset:int = Vector<Fil
|
|||||||
|
|
||||||
help.getConfig#c4f9186b = Config;
|
help.getConfig#c4f9186b = Config;
|
||||||
help.getNearestDc#1fb33026 = NearestDc;
|
help.getNearestDc#1fb33026 = NearestDc;
|
||||||
help.getAppUpdate#ae2de196 = help.AppUpdate;
|
help.getAppUpdate#522d5a7d source:string = help.AppUpdate;
|
||||||
help.saveAppLog#6f02f748 events:Vector<InputAppEvent> = Bool;
|
|
||||||
help.getInviteText#4d392343 = help.InviteText;
|
help.getInviteText#4d392343 = help.InviteText;
|
||||||
help.getSupport#9cdf08cd = help.Support;
|
help.getSupport#9cdf08cd = help.Support;
|
||||||
help.getAppChangelog#9010ef6f prev_app_version:string = Updates;
|
help.getAppChangelog#9010ef6f prev_app_version:string = Updates;
|
||||||
@ -1098,6 +1206,12 @@ help.getProxyData#3d7758e1 = help.ProxyData;
|
|||||||
help.getTermsOfServiceUpdate#2ca51fd1 = help.TermsOfServiceUpdate;
|
help.getTermsOfServiceUpdate#2ca51fd1 = help.TermsOfServiceUpdate;
|
||||||
help.acceptTermsOfService#ee72f79a id:DataJSON = Bool;
|
help.acceptTermsOfService#ee72f79a id:DataJSON = Bool;
|
||||||
help.getDeepLinkInfo#3fedc75f path:string = help.DeepLinkInfo;
|
help.getDeepLinkInfo#3fedc75f path:string = help.DeepLinkInfo;
|
||||||
|
help.getAppConfig#98914110 = JSONValue;
|
||||||
|
help.saveAppLog#6f02f748 events:Vector<InputAppEvent> = Bool;
|
||||||
|
help.getPassportConfig#c661ad08 hash:int = help.PassportConfig;
|
||||||
|
help.getSupportName#d360e72c = help.SupportName;
|
||||||
|
help.getUserInfo#38a08d3 user_id:InputUser = help.UserInfo;
|
||||||
|
help.editUserInfo#66b91b70 user_id:InputUser message:string entities:Vector<MessageEntity> = help.UserInfo;
|
||||||
|
|
||||||
channels.readHistory#cc104937 channel:InputChannel max_id:int = Bool;
|
channels.readHistory#cc104937 channel:InputChannel max_id:int = Bool;
|
||||||
channels.deleteMessages#84c1fd4e channel:InputChannel id:Vector<int> = messages.AffectedMessages;
|
channels.deleteMessages#84c1fd4e channel:InputChannel id:Vector<int> = messages.AffectedMessages;
|
||||||
@ -1123,7 +1237,6 @@ channels.deleteChannel#c0111fe3 channel:InputChannel = Updates;
|
|||||||
channels.toggleInvites#49609307 channel:InputChannel enabled:Bool = 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.updatePinnedMessage#a72ded52 flags:# silent:flags.0?true channel:InputChannel id:int = 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#bfd915cd channel:InputChannel user_id:InputUser banned_rights:ChannelBannedRights = Updates;
|
||||||
channels.getAdminLog#33ddf480 flags:# channel:InputChannel q:string events_filter:flags.0?ChannelAdminLogEventsFilter admins:flags.1?Vector<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;
|
||||||
@ -1131,6 +1244,7 @@ channels.setStickers#ea8ca4f9 channel:InputChannel stickerset:InputStickerSet =
|
|||||||
channels.readMessageContents#eab5dc38 channel:InputChannel id:Vector<int> = Bool;
|
channels.readMessageContents#eab5dc38 channel:InputChannel id:Vector<int> = Bool;
|
||||||
channels.deleteHistory#af369d42 channel:InputChannel max_id:int = Bool;
|
channels.deleteHistory#af369d42 channel:InputChannel max_id:int = Bool;
|
||||||
channels.togglePreHistoryHidden#eabbb94c channel:InputChannel enabled:Bool = Updates;
|
channels.togglePreHistoryHidden#eabbb94c channel:InputChannel enabled:Bool = Updates;
|
||||||
|
channels.getLeftChannels#8341ecc0 offset:int = messages.Chats;
|
||||||
|
|
||||||
bots.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON;
|
bots.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON;
|
||||||
bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool;
|
bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool;
|
||||||
@ -1156,9 +1270,10 @@ phone.discardCall#78d413a6 peer:InputPhoneCall duration:int reason:PhoneCallDisc
|
|||||||
phone.setCallRating#1c536a34 peer:InputPhoneCall rating:int comment:string = Updates;
|
phone.setCallRating#1c536a34 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#9ab5c58e lang_code:string = LangPackDifference;
|
langpack.getLangPack#f2f2330a lang_pack:string lang_code:string = LangPackDifference;
|
||||||
langpack.getStrings#2e1ee318 lang_code:string keys:Vector<string> = Vector<LangPackString>;
|
langpack.getStrings#efea3803 lang_pack:string lang_code:string keys:Vector<string> = Vector<LangPackString>;
|
||||||
langpack.getDifference#b2e4d7d from_version:int = LangPackDifference;
|
langpack.getDifference#9d51e814 lang_code:string from_version:int = LangPackDifference;
|
||||||
langpack.getLanguages#800fd57d = Vector<LangPackLanguage>;
|
langpack.getLanguages#42c6978f lang_pack:string = Vector<LangPackLanguage>;
|
||||||
|
langpack.getLanguage#6a596502 lang_pack:string lang_code:string = LangPackLanguage;
|
||||||
|
|
||||||
// LAYER 81
|
// LAYER 91
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
// Pyrogram
|
|
||||||
|
|
||||||
---types---
|
|
||||||
|
|
||||||
//pyrogram.update#b0700000 flags:# update_id:int message:flags.0?Message edited_message:flags.1?Message channel_post:flags.2?Message edited_channel_post:flags.3?Message inline_query:flags.4?InlineQuery chosen_inline_result:flags.5?ChosenInlineResult callback_query:flags.6?CallbackQuery shipping_query:flags.7?ShippingQuery pre_checkout_query:flags.8?PreCheckoutQuery = pyrogram.Update;
|
|
||||||
//pyrogram.user#b0700001 flags:# id:int is_bot:Bool first_name:string last_name:flags.0?string username:flags.1?string language_code:flags.2?string phone_number:flags.3?string photo:flags.4?ChatPhoto = pyrogram.User;
|
|
||||||
//pyrogram.chat#b0700002 flags:# id:int type:string title:flags.0?string username:flags.1?string first_name:flags.2?string last_name:flags.3?string all_members_are_administrators:flags.4?Bool photo:flags.5?ChatPhoto description:flags.6?string invite_link:flags.7?string pinned_message:flags.8?Message sticker_set_name:flags.9?string can_set_sticker_set:flags.10?Bool = pyrogram.Chat;
|
|
||||||
//pyrogram.message#b0700003 flags:# message_id:int from_user:flags.0?User date:int chat:Chat forward_from:flags.1?User forward_from_chat:flags.2?Chat forward_from_message_id:flags.3?int forward_signature:flags.4?string forward_date:flags.5?int reply_to_message:flags.6?Message edit_date:flags.7?int media_group_id:flags.8?string author_signature:flags.9?string text:flags.10?string entities:flags.11?Vector<MessageEntity> caption_entities:flags.12?Vector<MessageEntity> audio:flags.13?Audio document:flags.14?Document game:flags.15?Game photo:flags.16?Vector<PhotoSize> sticker:flags.17?Sticker video:flags.18?Video voice:flags.19?Voice video_note:flags.20?VideoNote caption:flags.21?string contact:flags.22?Contact location:flags.23?Location venue:flags.24?Venue new_chat_members:flags.25?Vector<User> left_chat_member:flags.26?User new_chat_title:flags.27?string new_chat_photo:flags.28?Vector<PhotoSize> delete_chat_photo:flags.29?true group_chat_created:flags.30?true supergroup_chat_created:flags.31?true channel_chat_created:flags.32?true migrate_to_chat_id:flags.33?int migrate_from_chat_id:flags.34?int pinned_message:flags.35?Message invoice:flags.36?Invoice successful_payment:flags.37?SuccessfulPayment connected_website:flags.38?string views:flags.39?int via_bot:flags.40?User = pyrogram.Message;
|
|
||||||
//pyrogram.messageEntity#b0700004 flags:# type:string offset:int length:int url:flags.0?string user:flags.1?User = pyrogram.MessageEntity;
|
|
||||||
//pyrogram.photoSize#b0700005 flags:# file_id:string file_size:flags.0?int date:flags.1?int width:int height:int = pyrogram.PhotoSize;
|
|
||||||
//pyrogram.audio#b0700006 flags:# file_id:string thumb:flags.0?PhotoSize file_name:flags.1?string mime_type:flags.2?string file_size:flags.3?int date:flags.4?int duration:int performer:flags.5?string title:flags.6?string = pyrogram.Audio;
|
|
||||||
//pyrogram.document#b0700007 flags:# file_id:string thumb:flags.0?PhotoSize file_name:flags.1?string mime_type:flags.2?string file_size:flags.3?int date:flags.4?int = pyrogram.Document;
|
|
||||||
//pyrogram.video#b0700008 flags:# file_id:string thumb:flags.0?PhotoSize file_name:flags.1?string mime_type:flags.2?string file_size:flags.3?int date:flags.4?int width:int height:int duration:int = pyrogram.Video;
|
|
||||||
//pyrogram.voice#b0700009 flags:# file_id:string thumb:flags.0?PhotoSize file_name:flags.1?string mime_type:flags.2?string file_size:flags.3?int date:flags.4?int duration:int = pyrogram.Voice;
|
|
||||||
//pyrogram.videoNote#b0700010 flags:# file_id:string thumb:flags.0?PhotoSize file_name:flags.1?string mime_type:flags.2?string file_size:flags.3?int date:flags.4?int length:int duration:int = pyrogram.VideoNote;
|
|
||||||
//pyrogram.contact#b0700011 flags:# phone_number:string first_name:string last_name:flags.0?string user_id:flags.1?int = pyrogram.Contact;
|
|
||||||
//pyrogram.location#b0700012 longitude:double latitude:double = pyrogram.Location;
|
|
||||||
//pyrogram.venue#b0700013 flags:# location:Location title:string address:string foursquare_id:flags.0?string = pyrogram.Venue;
|
|
||||||
//pyrogram.userProfilePhotos#b0700014 total_count:int photos:Vector<Vector<PhotoSize>> = pyrogram.UserProfilePhotos;
|
|
||||||
//pyrogram.chatPhoto#b0700015 small_file_id:string big_file_id:string = pyrogram.ChatPhoto;
|
|
||||||
//pyrogram.chatMember#b0700016 flags:# user:User status:string until_date:flags.0?int can_be_edited:flags.1?Bool can_change_info:flags.2?Bool can_post_messages:flags.3?Bool can_edit_messages:flags.4?Bool can_delete_messages:flags.5?Bool can_invite_users:flags.6?Bool can_restrict_members:flags.7?Bool can_pin_messages:flags.8?Bool can_promote_members:flags.9?Bool can_send_messages:flags.10?Bool can_send_media_messages:flags.11?Bool can_send_other_messages:flags.12?Bool can_add_web_page_previews:flags.13?Bool = pyrogram.ChatMember;
|
|
||||||
//pyrogram.sticker#b0700017 flags:# file_id:string thumb:flags.0?PhotoSize file_name:flags.1?string mime_type:flags.2?string file_size:flags.3?int date:flags.4?int width:int height:int emoji:flags.5?string set_name:flags.6?string mask_position:flags.7?MaskPosition = pyrogram.Sticker;
|
|
@ -88,7 +88,7 @@ def generate(source_path, base):
|
|||||||
inner_path = base + "/" + k + "/index" + ".rst"
|
inner_path = base + "/" + k + "/index" + ".rst"
|
||||||
module = "pyrogram.api.{}.{}".format(base, k)
|
module = "pyrogram.api.{}.{}".format(base, k)
|
||||||
else:
|
else:
|
||||||
for i in list(all_entities)[::-1]:
|
for i in sorted(list(all_entities), reverse=True):
|
||||||
if i != base:
|
if i != base:
|
||||||
entities.insert(0, "{0}/index".format(i))
|
entities.insert(0, "{0}/index".format(i))
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ BOT_INLINE_DISABLED The inline feature of the bot is disabled
|
|||||||
INLINE_RESULT_EXPIRED The inline bot query expired
|
INLINE_RESULT_EXPIRED The inline bot query expired
|
||||||
INVITE_HASH_INVALID The invite link hash is invalid
|
INVITE_HASH_INVALID The invite link hash is invalid
|
||||||
USER_ALREADY_PARTICIPANT The user is already a participant of this chat
|
USER_ALREADY_PARTICIPANT The user is already a participant of this chat
|
||||||
TTL_MEDIA_INVALID This kind of media does not support self-destruction
|
TTL_MEDIA_INVALID The media does not support self-destruction
|
||||||
MAX_ID_INVALID The max_id parameter is invalid
|
MAX_ID_INVALID The max_id parameter is invalid
|
||||||
CHANNEL_INVALID The channel parameter is invalid
|
CHANNEL_INVALID The channel parameter is invalid
|
||||||
DC_ID_INVALID The dc_id parameter is invalid
|
DC_ID_INVALID The dc_id parameter is invalid
|
||||||
@ -59,7 +59,25 @@ LIMIT_INVALID The limit parameter is invalid
|
|||||||
OFFSET_INVALID The offset parameter is invalid
|
OFFSET_INVALID The offset parameter is invalid
|
||||||
EMAIL_INVALID The email provided is invalid
|
EMAIL_INVALID The email provided is invalid
|
||||||
USER_IS_BOT A bot cannot send messages to other bots or to itself
|
USER_IS_BOT A bot cannot send messages to other bots or to itself
|
||||||
WEBPAGE_CURL_FAILED Telegram could not fetch the provided URL
|
WEBPAGE_CURL_FAILED Telegram server could not fetch the provided URL
|
||||||
STICKERSET_INVALID The requested sticker set is invalid
|
STICKERSET_INVALID The requested sticker set is invalid
|
||||||
PEER_FLOOD The method can't be used because your account is limited
|
PEER_FLOOD The method can't be used because your account is limited
|
||||||
MEDIA_CAPTION_TOO_LONG The media caption is longer than 200 characters
|
MEDIA_CAPTION_TOO_LONG The media caption is longer than 200 characters
|
||||||
|
USER_NOT_MUTUAL_CONTACT The user is not a mutual contact
|
||||||
|
USER_CHANNELS_TOO_MUCH The user is already in too many channels or supergroups
|
||||||
|
API_ID_PUBLISHED_FLOOD You are using an API key that is limited on the server side
|
||||||
|
USER_NOT_PARTICIPANT The user is not a member of this chat
|
||||||
|
CHANNEL_PRIVATE The channel/supergroup is not accessible
|
||||||
|
MESSAGE_IDS_EMPTY The requested message doesn't exist
|
||||||
|
WEBPAGE_MEDIA_EMPTY The URL doesn't contain any valid media
|
||||||
|
QUERY_ID_INVALID The callback query id is invalid
|
||||||
|
MEDIA_EMPTY The media is invalid
|
||||||
|
USER_IS_BLOCKED The user blocked you
|
||||||
|
YOU_BLOCKED_USER You blocked this user
|
||||||
|
ADMINS_TOO_MUCH The chat has too many administrators
|
||||||
|
BOTS_TOO_MUCH The chat has too many bots
|
||||||
|
USER_ADMIN_INVALID The action requires admin privileges
|
||||||
|
INPUT_USER_DEACTIVATED The target user has been deactivated
|
||||||
|
PASSWORD_RECOVERY_NA The password recovery e-mail is not available
|
||||||
|
PASSWORD_EMPTY The password entered is empty
|
||||||
|
PHONE_NUMBER_FLOOD This number has tried to login too many times
|
|
5
compiler/error/source/403_FORBIDDEN.tsv
Normal file
5
compiler/error/source/403_FORBIDDEN.tsv
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
id message
|
||||||
|
CHAT_WRITE_FORBIDDEN You don't have rights to send messages in this chat
|
||||||
|
RIGHT_FORBIDDEN One or more admin rights can't be applied to this kind of chat (channel/supergroup)
|
||||||
|
CHAT_ADMIN_INVITE_REQUIRED You don't have rights to invite other users
|
||||||
|
MESSAGE_DELETE_FORBIDDEN You don't have rights to delete messages in this chat
|
|
@ -1,2 +1,3 @@
|
|||||||
id message
|
id message
|
||||||
AUTH_KEY_DUPLICATED Authorization error. You must log out and log in again with your phone number. We apologize for the inconvenience.
|
AUTH_KEY_DUPLICATED Authorization error - you must delete your session file and log in again with your phone number
|
||||||
|
FILEREF_UPGRADE_NEEDED The file reference has expired - you must obtain the original media message
|
|
@ -3,4 +3,6 @@ AUTH_RESTART User authorization has restarted
|
|||||||
RPC_CALL_FAIL Telegram is having internal problems. Please try again later
|
RPC_CALL_FAIL Telegram is having internal problems. Please try again later
|
||||||
RPC_MCGET_FAIL Telegram is having internal problems. Please try again later
|
RPC_MCGET_FAIL Telegram is having internal problems. Please try again later
|
||||||
PERSISTENT_TIMESTAMP_OUTDATED Telegram is having internal problems. Please try again later
|
PERSISTENT_TIMESTAMP_OUTDATED Telegram is having internal problems. Please try again later
|
||||||
HISTORY_GET_FAILED Telegram is having internal problems. Please try again later
|
HISTORY_GET_FAILED Telegram is having internal problems. Please try again later
|
||||||
|
REG_ID_GENERATE_FAILED Telegram is having internal problems. Please try again later
|
||||||
|
RANDOM_ID_DUPLICATE Telegram is having internal problems. Please try again later
|
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
# You can set these variables from the command line.
|
# You can set these variables from the command line.
|
||||||
SPHINXOPTS =
|
SPHINXOPTS =
|
||||||
SPHINXBUILD = ~/PycharmProjects/pyrogram/venv3.6/bin/sphinx-build
|
SPHINXBUILD = sphinx-build
|
||||||
SPHINXPROJ = Pyrogram
|
SPHINXPROJ = Pyrogram
|
||||||
SOURCEDIR = source
|
SOURCEDIR = source
|
||||||
BUILDDIR = build
|
BUILDDIR = build
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
# Minimal makefile for Sphinx documentation
|
|
||||||
#
|
|
||||||
|
|
||||||
# You can set these variables from the command line.
|
|
||||||
SPHINXOPTS =
|
|
||||||
SPHINXBUILD = sphinx-build
|
|
||||||
SPHINXPROJ = Pyrogram
|
|
||||||
SOURCEDIR = source
|
|
||||||
BUILDDIR = build
|
|
||||||
|
|
||||||
# Put it first so that "make" without argument is like "make help".
|
|
||||||
help:
|
|
||||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
|
||||||
|
|
||||||
.PHONY: help Makefile
|
|
||||||
|
|
||||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
|
||||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
|
||||||
%: Makefile
|
|
||||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
|
@ -5,8 +5,7 @@ Welcome to Pyrogram
|
|||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<a href="https://docs.pyrogram.ml">
|
<a href="https://docs.pyrogram.ml">
|
||||||
<div><img src="https://media.pyrogram.ml/images/icon.png" alt="Pyrogram Icon"></div>
|
<div><img src="_static/logo.png" alt="Pyrogram Logo"></div>
|
||||||
<div><img src="https://media.pyrogram.ml/images/label.png" alt="Pyrogram Label"></div>
|
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -24,13 +23,13 @@ Welcome to Pyrogram
|
|||||||
<a href="https://t.me/PyrogramChat">
|
<a href="https://t.me/PyrogramChat">
|
||||||
Community
|
Community
|
||||||
</a>
|
</a>
|
||||||
<br><br>
|
<br>
|
||||||
<a href="https://github.com/pyrogram/pyrogram/blob/master/compiler/api/source/main_api.tl">
|
<a href="https://github.com/pyrogram/pyrogram/blob/master/compiler/api/source/main_api.tl">
|
||||||
<img src="https://img.shields.io/badge/SCHEME-LAYER%2081-eda738.svg?longCache=true&style=for-the-badge&colorA=262b30"
|
<img src="https://img.shields.io/badge/schema-layer%2091-eda738.svg?longCache=true&colorA=262b30"
|
||||||
alt="Scheme Layer">
|
alt="Scheme Layer">
|
||||||
</a>
|
</a>
|
||||||
<a href="https://github.com/pyrogram/tgcrypto">
|
<a href="https://github.com/pyrogram/tgcrypto">
|
||||||
<img src="https://img.shields.io/badge/TGCRYPTO-V1.0.4-eda738.svg?longCache=true&style=for-the-badge&colorA=262b30"
|
<img src="https://img.shields.io/badge/tgcrypto-v1.1.1-eda738.svg?longCache=true&colorA=262b30"
|
||||||
alt="TgCrypto">
|
alt="TgCrypto">
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
@ -50,14 +49,14 @@ Welcome to Pyrogram
|
|||||||
app.run()
|
app.run()
|
||||||
|
|
||||||
Welcome to Pyrogram's Documentation! Here you can find resources for learning how to use the library.
|
Welcome to Pyrogram's Documentation! Here you can find resources for learning how to use the library.
|
||||||
Contents are organized by topic and can be accessed from the sidebar, or by following them one by one using the Next
|
Contents are organized into self-contained topics and can be accessed from the sidebar, or by following them in order
|
||||||
button at the end of each page. But first, here's a brief overview of what is this all about.
|
using the Next button at the end of each page. But first, here's a brief overview of what is this all about.
|
||||||
|
|
||||||
About
|
About
|
||||||
-----
|
-----
|
||||||
|
|
||||||
**Pyrogram** is a brand new Telegram_ Client Library written from the ground up in Python and C. It can be used for building
|
**Pyrogram** is a brand new Telegram_ Client Library written from the ground up in Python and C. It can be used for
|
||||||
custom Telegram applications that interact with the MTProto API as both User and Bot.
|
building custom Telegram applications that interact with the MTProto API as both User and Bot.
|
||||||
|
|
||||||
Features
|
Features
|
||||||
--------
|
--------
|
||||||
@ -65,7 +64,7 @@ Features
|
|||||||
- **Easy to use**: You can easily install Pyrogram using pip and start building your app right away.
|
- **Easy to use**: You can easily install Pyrogram using pip and start building your app right away.
|
||||||
- **High-level**: The low-level details of MTProto are abstracted and automatically handled.
|
- **High-level**: The low-level details of MTProto are abstracted and automatically handled.
|
||||||
- **Fast**: Crypto parts are boosted up by TgCrypto_, a high-performance library written in pure C.
|
- **Fast**: Crypto parts are boosted up by TgCrypto_, a high-performance library written in pure C.
|
||||||
- **Updated** to the latest Telegram API version, currently Layer 81 on top of MTProto 2.0.
|
- **Updated** to the latest Telegram API version, currently Layer 91 on top of MTProto 2.0.
|
||||||
- **Documented**: The Pyrogram API is well documented and resembles the Telegram Bot API.
|
- **Documented**: The Pyrogram API is well documented and resembles the Telegram Bot API.
|
||||||
- **Full API**, allowing to execute any advanced action an official client is able to do, and more.
|
- **Full API**, allowing to execute any advanced action an official client is able to do, and more.
|
||||||
|
|
||||||
@ -84,6 +83,8 @@ To get started, press the Next button.
|
|||||||
:caption: Resources
|
:caption: Resources
|
||||||
|
|
||||||
resources/UpdateHandling
|
resources/UpdateHandling
|
||||||
|
resources/UsingFilters
|
||||||
|
resources/SmartPlugins
|
||||||
resources/AutoAuthorization
|
resources/AutoAuthorization
|
||||||
resources/CustomizeSessions
|
resources/CustomizeSessions
|
||||||
resources/TgCrypto
|
resources/TgCrypto
|
||||||
@ -91,6 +92,8 @@ To get started, press the Next button.
|
|||||||
resources/SOCKS5Proxy
|
resources/SOCKS5Proxy
|
||||||
resources/BotsInteraction
|
resources/BotsInteraction
|
||||||
resources/ErrorHandling
|
resources/ErrorHandling
|
||||||
|
resources/TestServers
|
||||||
|
resources/Changelog
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:hidden:
|
:hidden:
|
||||||
|
@ -1,70 +1,140 @@
|
|||||||
Client
|
Client
|
||||||
======
|
======
|
||||||
|
|
||||||
.. currentmodule:::: pyrogram.Client
|
.. currentmodule:: pyrogram.Client
|
||||||
|
|
||||||
|
.. autoclass:: pyrogram.Client
|
||||||
|
|
||||||
|
Utilities
|
||||||
|
---------
|
||||||
|
|
||||||
|
.. autosummary::
|
||||||
|
:nosignatures:
|
||||||
|
|
||||||
|
start
|
||||||
|
stop
|
||||||
|
idle
|
||||||
|
run
|
||||||
|
add_handler
|
||||||
|
remove_handler
|
||||||
|
send
|
||||||
|
resolve_peer
|
||||||
|
download_media
|
||||||
|
|
||||||
|
Decorators
|
||||||
|
----------
|
||||||
|
|
||||||
|
.. autosummary::
|
||||||
|
:nosignatures:
|
||||||
|
|
||||||
|
on_message
|
||||||
|
on_callback_query
|
||||||
|
on_deleted_messages
|
||||||
|
on_user_status
|
||||||
|
on_disconnect
|
||||||
|
on_raw_update
|
||||||
|
|
||||||
|
Messages
|
||||||
|
--------
|
||||||
|
|
||||||
|
.. autosummary::
|
||||||
|
:nosignatures:
|
||||||
|
|
||||||
|
send_message
|
||||||
|
forward_messages
|
||||||
|
send_photo
|
||||||
|
send_audio
|
||||||
|
send_document
|
||||||
|
send_sticker
|
||||||
|
send_video
|
||||||
|
send_animation
|
||||||
|
send_voice
|
||||||
|
send_video_note
|
||||||
|
send_media_group
|
||||||
|
send_location
|
||||||
|
send_venue
|
||||||
|
send_contact
|
||||||
|
send_chat_action
|
||||||
|
edit_message_text
|
||||||
|
edit_message_caption
|
||||||
|
edit_message_reply_markup
|
||||||
|
edit_message_media
|
||||||
|
delete_messages
|
||||||
|
get_messages
|
||||||
|
get_history
|
||||||
|
send_poll
|
||||||
|
vote_poll
|
||||||
|
retract_vote
|
||||||
|
|
||||||
|
Chats
|
||||||
|
-----
|
||||||
|
|
||||||
|
.. autosummary::
|
||||||
|
:nosignatures:
|
||||||
|
|
||||||
|
join_chat
|
||||||
|
leave_chat
|
||||||
|
kick_chat_member
|
||||||
|
unban_chat_member
|
||||||
|
restrict_chat_member
|
||||||
|
promote_chat_member
|
||||||
|
export_chat_invite_link
|
||||||
|
set_chat_photo
|
||||||
|
delete_chat_photo
|
||||||
|
set_chat_title
|
||||||
|
set_chat_description
|
||||||
|
pin_chat_message
|
||||||
|
unpin_chat_message
|
||||||
|
get_chat
|
||||||
|
get_chat_member
|
||||||
|
get_chat_members
|
||||||
|
get_chat_members_count
|
||||||
|
get_dialogs
|
||||||
|
|
||||||
|
Users
|
||||||
|
-----
|
||||||
|
|
||||||
|
.. autosummary::
|
||||||
|
:nosignatures:
|
||||||
|
|
||||||
|
get_me
|
||||||
|
get_users
|
||||||
|
get_user_profile_photos
|
||||||
|
set_user_profile_photo
|
||||||
|
delete_user_profile_photos
|
||||||
|
|
||||||
|
Contacts
|
||||||
|
--------
|
||||||
|
|
||||||
|
.. autosummary::
|
||||||
|
:nosignatures:
|
||||||
|
|
||||||
|
add_contacts
|
||||||
|
get_contacts
|
||||||
|
delete_contacts
|
||||||
|
|
||||||
|
Password
|
||||||
|
--------
|
||||||
|
|
||||||
|
.. autosummary::
|
||||||
|
:nosignatures:
|
||||||
|
|
||||||
|
enable_cloud_password
|
||||||
|
change_cloud_password
|
||||||
|
remove_cloud_password
|
||||||
|
|
||||||
|
Bots
|
||||||
|
----
|
||||||
|
|
||||||
|
.. autosummary::
|
||||||
|
:nosignatures:
|
||||||
|
|
||||||
|
get_inline_bot_results
|
||||||
|
send_inline_bot_result
|
||||||
|
answer_callback_query
|
||||||
|
request_callback_answer
|
||||||
|
|
||||||
|
|
||||||
.. autoclass:: pyrogram.Client
|
.. autoclass:: pyrogram.Client
|
||||||
:inherited-members:
|
:inherited-members:
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
.. _available-methods:
|
|
||||||
|
|
||||||
**Available methods**
|
|
||||||
|
|
||||||
.. autosummary::
|
|
||||||
:nosignatures:
|
|
||||||
|
|
||||||
start
|
|
||||||
stop
|
|
||||||
idle
|
|
||||||
run
|
|
||||||
on_message
|
|
||||||
on_callback_query
|
|
||||||
on_raw_update
|
|
||||||
add_handler
|
|
||||||
remove_handler
|
|
||||||
send
|
|
||||||
resolve_peer
|
|
||||||
get_me
|
|
||||||
send_message
|
|
||||||
forward_messages
|
|
||||||
send_photo
|
|
||||||
send_audio
|
|
||||||
send_document
|
|
||||||
send_sticker
|
|
||||||
send_video
|
|
||||||
send_voice
|
|
||||||
send_video_note
|
|
||||||
send_media_group
|
|
||||||
send_location
|
|
||||||
send_venue
|
|
||||||
send_contact
|
|
||||||
send_chat_action
|
|
||||||
download_media
|
|
||||||
get_user_profile_photos
|
|
||||||
delete_profile_photos
|
|
||||||
edit_message_text
|
|
||||||
edit_message_caption
|
|
||||||
edit_message_reply_markup
|
|
||||||
delete_messages
|
|
||||||
join_chat
|
|
||||||
leave_chat
|
|
||||||
export_chat_invite_link
|
|
||||||
enable_cloud_password
|
|
||||||
change_cloud_password
|
|
||||||
remove_cloud_password
|
|
||||||
kick_chat_member
|
|
||||||
unban_chat_member
|
|
||||||
restrict_chat_member
|
|
||||||
promote_chat_member
|
|
||||||
add_contacts
|
|
||||||
get_contacts
|
|
||||||
delete_contacts
|
|
||||||
get_inline_bot_results
|
|
||||||
send_inline_bot_result
|
|
||||||
answer_callback_query
|
|
||||||
request_callback_answer
|
|
||||||
get_users
|
|
||||||
get_chat
|
|
||||||
get_messages
|
|
||||||
get_history
|
|
@ -1,5 +1,3 @@
|
|||||||
:tocdepth: 1
|
|
||||||
|
|
||||||
Error
|
Error
|
||||||
=====
|
=====
|
||||||
|
|
||||||
|
@ -3,4 +3,3 @@ Filters
|
|||||||
|
|
||||||
.. autoclass:: pyrogram.Filters
|
.. autoclass:: pyrogram.Filters
|
||||||
:members:
|
:members:
|
||||||
:undoc-members:
|
|
||||||
|
33
docs/source/pyrogram/Handlers.rst
Normal file
33
docs/source/pyrogram/Handlers.rst
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
Handlers
|
||||||
|
========
|
||||||
|
|
||||||
|
.. currentmodule:: pyrogram
|
||||||
|
|
||||||
|
.. autosummary::
|
||||||
|
:nosignatures:
|
||||||
|
|
||||||
|
MessageHandler
|
||||||
|
DeletedMessagesHandler
|
||||||
|
CallbackQueryHandler
|
||||||
|
UserStatusHandler
|
||||||
|
DisconnectHandler
|
||||||
|
RawUpdateHandler
|
||||||
|
|
||||||
|
.. autoclass:: MessageHandler
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: DeletedMessagesHandler
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: CallbackQueryHandler
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: UserStatusHandler
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: DisconnectHandler
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: RawUpdateHandler
|
||||||
|
:members:
|
||||||
|
|
200
docs/source/pyrogram/Types.rst
Normal file
200
docs/source/pyrogram/Types.rst
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
Types
|
||||||
|
=====
|
||||||
|
|
||||||
|
.. currentmodule:: pyrogram
|
||||||
|
|
||||||
|
Users & Chats
|
||||||
|
-------------
|
||||||
|
|
||||||
|
.. autosummary::
|
||||||
|
:nosignatures:
|
||||||
|
|
||||||
|
User
|
||||||
|
UserStatus
|
||||||
|
Chat
|
||||||
|
ChatPhoto
|
||||||
|
ChatMember
|
||||||
|
ChatMembers
|
||||||
|
Dialog
|
||||||
|
Dialogs
|
||||||
|
|
||||||
|
Messages & Media
|
||||||
|
----------------
|
||||||
|
|
||||||
|
.. autosummary::
|
||||||
|
:nosignatures:
|
||||||
|
|
||||||
|
Message
|
||||||
|
Messages
|
||||||
|
MessageEntity
|
||||||
|
Photo
|
||||||
|
PhotoSize
|
||||||
|
UserProfilePhotos
|
||||||
|
Audio
|
||||||
|
Document
|
||||||
|
Animation
|
||||||
|
Video
|
||||||
|
Voice
|
||||||
|
VideoNote
|
||||||
|
Contact
|
||||||
|
Location
|
||||||
|
Venue
|
||||||
|
Sticker
|
||||||
|
Poll
|
||||||
|
PollOption
|
||||||
|
|
||||||
|
Bots
|
||||||
|
----
|
||||||
|
|
||||||
|
.. autosummary::
|
||||||
|
:nosignatures:
|
||||||
|
|
||||||
|
ReplyKeyboardMarkup
|
||||||
|
KeyboardButton
|
||||||
|
ReplyKeyboardRemove
|
||||||
|
InlineKeyboardMarkup
|
||||||
|
InlineKeyboardButton
|
||||||
|
ForceReply
|
||||||
|
CallbackQuery
|
||||||
|
|
||||||
|
Input Media
|
||||||
|
-----------
|
||||||
|
|
||||||
|
.. autosummary::
|
||||||
|
:nosignatures:
|
||||||
|
|
||||||
|
InputMediaPhoto
|
||||||
|
InputMediaVideo
|
||||||
|
InputMediaAudio
|
||||||
|
InputMediaAnimation
|
||||||
|
InputMediaDocument
|
||||||
|
InputPhoneContact
|
||||||
|
|
||||||
|
.. User & Chats
|
||||||
|
------------
|
||||||
|
|
||||||
|
.. autoclass:: User
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: UserStatus
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: Chat
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: ChatPhoto
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: ChatMember
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: ChatMembers
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: Dialog
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: Dialogs
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. Messages & Media
|
||||||
|
----------------
|
||||||
|
|
||||||
|
.. autoclass:: Message
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: Messages
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: MessageEntity
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: Photo
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: PhotoSize
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: UserProfilePhotos
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: Audio
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: Document
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: Animation
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: Video
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: Voice
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: VideoNote
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: Contact
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: Location
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: Venue
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: Sticker
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: Poll
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: PollOption
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. Bots
|
||||||
|
----
|
||||||
|
|
||||||
|
.. autoclass:: ReplyKeyboardMarkup
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: KeyboardButton
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: ReplyKeyboardRemove
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: InlineKeyboardMarkup
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: InlineKeyboardButton
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: ForceReply
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: CallbackQuery
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. Input Media
|
||||||
|
-----------
|
||||||
|
|
||||||
|
.. autoclass:: InputMediaPhoto
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: InputMediaVideo
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: InputMediaAudio
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: InputMediaAnimation
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: InputMediaDocument
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: InputPhoneContact
|
||||||
|
:members:
|
@ -1,6 +0,0 @@
|
|||||||
CallbackQueryHandler
|
|
||||||
====================
|
|
||||||
|
|
||||||
.. autoclass:: pyrogram.CallbackQueryHandler
|
|
||||||
:members:
|
|
||||||
:undoc-members:
|
|
@ -1,6 +0,0 @@
|
|||||||
DeletedMessagesHandler
|
|
||||||
======================
|
|
||||||
|
|
||||||
.. autoclass:: pyrogram.DeletedMessagesHandler
|
|
||||||
:members:
|
|
||||||
:undoc-members:
|
|
@ -1,6 +0,0 @@
|
|||||||
DisconnectHandler
|
|
||||||
=================
|
|
||||||
|
|
||||||
.. autoclass:: pyrogram.DisconnectHandler
|
|
||||||
:members:
|
|
||||||
:undoc-members:
|
|
@ -1,6 +0,0 @@
|
|||||||
MessageHandler
|
|
||||||
==============
|
|
||||||
|
|
||||||
.. autoclass:: pyrogram.MessageHandler
|
|
||||||
:members:
|
|
||||||
:undoc-members:
|
|
@ -1,6 +0,0 @@
|
|||||||
RawUpdateHandler
|
|
||||||
================
|
|
||||||
|
|
||||||
.. autoclass:: pyrogram.RawUpdateHandler
|
|
||||||
:members:
|
|
||||||
:undoc-members:
|
|
@ -1,11 +0,0 @@
|
|||||||
:tocdepth: 1
|
|
||||||
|
|
||||||
Handlers
|
|
||||||
========
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
MessageHandler
|
|
||||||
DeletedMessagesHandler
|
|
||||||
CallbackQueryHandler
|
|
||||||
DisconnectHandler
|
|
||||||
RawUpdateHandler
|
|
@ -7,9 +7,11 @@ In this section you can find a detailed description of the Pyrogram package and
|
|||||||
after the well established `Telegram Bot API`_ methods, thus offering a familiar look to Bot developers.
|
after the well established `Telegram Bot API`_ methods, thus offering a familiar look to Bot developers.
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
|
:maxdepth: 1
|
||||||
|
|
||||||
Client
|
Client
|
||||||
types/index
|
Types
|
||||||
handlers/index
|
Handlers
|
||||||
Filters
|
Filters
|
||||||
ChatAction
|
ChatAction
|
||||||
ParseMode
|
ParseMode
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
Audio
|
|
||||||
=====
|
|
||||||
|
|
||||||
.. autoclass:: pyrogram.Audio
|
|
||||||
:members:
|
|
@ -1,5 +0,0 @@
|
|||||||
CallbackQuery
|
|
||||||
=============
|
|
||||||
|
|
||||||
.. autoclass:: pyrogram.CallbackQuery
|
|
||||||
:members:
|
|
@ -1,5 +0,0 @@
|
|||||||
Chat
|
|
||||||
====
|
|
||||||
|
|
||||||
.. autoclass:: pyrogram.Chat
|
|
||||||
:members:
|
|
@ -1,5 +0,0 @@
|
|||||||
ChatMember
|
|
||||||
==========
|
|
||||||
|
|
||||||
.. autoclass:: pyrogram.ChatMember
|
|
||||||
:members:
|
|
@ -1,5 +0,0 @@
|
|||||||
ChatPhoto
|
|
||||||
=========
|
|
||||||
|
|
||||||
.. autoclass:: pyrogram.ChatPhoto
|
|
||||||
:members:
|
|
@ -1,5 +0,0 @@
|
|||||||
Contact
|
|
||||||
=======
|
|
||||||
|
|
||||||
.. autoclass:: pyrogram.Contact
|
|
||||||
:members:
|
|
@ -1,5 +0,0 @@
|
|||||||
Document
|
|
||||||
========
|
|
||||||
|
|
||||||
.. autoclass:: pyrogram.Document
|
|
||||||
:members:
|
|
@ -1,5 +0,0 @@
|
|||||||
GIF
|
|
||||||
===
|
|
||||||
|
|
||||||
.. autoclass:: pyrogram.GIF
|
|
||||||
:members:
|
|
@ -1,5 +0,0 @@
|
|||||||
InputMediaPhoto
|
|
||||||
===============
|
|
||||||
|
|
||||||
.. autoclass:: pyrogram.InputMediaPhoto
|
|
||||||
:members:
|
|
@ -1,5 +0,0 @@
|
|||||||
InputMediaVideo
|
|
||||||
===============
|
|
||||||
|
|
||||||
.. autoclass:: pyrogram.InputMediaVideo
|
|
||||||
:members:
|
|
@ -1,5 +0,0 @@
|
|||||||
InputPhoneContact
|
|
||||||
=================
|
|
||||||
|
|
||||||
.. autoclass:: pyrogram.InputPhoneContact
|
|
||||||
:members:
|
|
@ -1,5 +0,0 @@
|
|||||||
Location
|
|
||||||
========
|
|
||||||
|
|
||||||
.. autoclass:: pyrogram.Location
|
|
||||||
:members:
|
|
@ -1,5 +0,0 @@
|
|||||||
Message
|
|
||||||
=======
|
|
||||||
|
|
||||||
.. autoclass:: pyrogram.Message
|
|
||||||
:members:
|
|
@ -1,5 +0,0 @@
|
|||||||
MessageEntity
|
|
||||||
=============
|
|
||||||
|
|
||||||
.. autoclass:: pyrogram.MessageEntity
|
|
||||||
:members:
|
|
@ -1,5 +0,0 @@
|
|||||||
Messages
|
|
||||||
========
|
|
||||||
|
|
||||||
.. autoclass:: pyrogram.Messages
|
|
||||||
:members:
|
|
@ -1,5 +0,0 @@
|
|||||||
Photo
|
|
||||||
=====
|
|
||||||
|
|
||||||
.. autoclass:: pyrogram.Photo
|
|
||||||
:members:
|
|
@ -1,5 +0,0 @@
|
|||||||
PhotoSize
|
|
||||||
=========
|
|
||||||
|
|
||||||
.. autoclass:: pyrogram.PhotoSize
|
|
||||||
:members:
|
|
@ -1,5 +0,0 @@
|
|||||||
Sticker
|
|
||||||
=======
|
|
||||||
|
|
||||||
.. autoclass:: pyrogram.Sticker
|
|
||||||
:members:
|
|
@ -1,5 +0,0 @@
|
|||||||
Update
|
|
||||||
======
|
|
||||||
|
|
||||||
.. autoclass:: pyrogram.Update
|
|
||||||
:members:
|
|
@ -1,5 +0,0 @@
|
|||||||
User
|
|
||||||
====
|
|
||||||
|
|
||||||
.. autoclass:: pyrogram.User
|
|
||||||
:members:
|
|
@ -1,5 +0,0 @@
|
|||||||
UserProfilePhotos
|
|
||||||
=================
|
|
||||||
|
|
||||||
.. autoclass:: pyrogram.UserProfilePhotos
|
|
||||||
:members:
|
|
@ -1,5 +0,0 @@
|
|||||||
Venue
|
|
||||||
=====
|
|
||||||
|
|
||||||
.. autoclass:: pyrogram.Venue
|
|
||||||
:members:
|
|
@ -1,5 +0,0 @@
|
|||||||
Video
|
|
||||||
=====
|
|
||||||
|
|
||||||
.. autoclass:: pyrogram.Video
|
|
||||||
:members:
|
|
@ -1,5 +0,0 @@
|
|||||||
VideoNote
|
|
||||||
=========
|
|
||||||
|
|
||||||
.. autoclass:: pyrogram.VideoNote
|
|
||||||
:members:
|
|
@ -1,5 +0,0 @@
|
|||||||
Voice
|
|
||||||
=====
|
|
||||||
|
|
||||||
.. autoclass:: pyrogram.Voice
|
|
||||||
:members:
|
|
@ -1,36 +0,0 @@
|
|||||||
:tocdepth: 1
|
|
||||||
|
|
||||||
Types
|
|
||||||
=====
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
User
|
|
||||||
Chat
|
|
||||||
Message
|
|
||||||
MessageEntity
|
|
||||||
Messages
|
|
||||||
Photo
|
|
||||||
PhotoSize
|
|
||||||
Audio
|
|
||||||
Document
|
|
||||||
GIF
|
|
||||||
Video
|
|
||||||
Voice
|
|
||||||
VideoNote
|
|
||||||
Contact
|
|
||||||
Location
|
|
||||||
Venue
|
|
||||||
UserProfilePhotos
|
|
||||||
ChatPhoto
|
|
||||||
ChatMember
|
|
||||||
InputMediaPhoto
|
|
||||||
InputMediaVideo
|
|
||||||
InputPhoneContact
|
|
||||||
Sticker
|
|
||||||
reply_markup/ForceReply
|
|
||||||
reply_markup/InlineKeyboardButton
|
|
||||||
reply_markup/InlineKeyboardMarkup
|
|
||||||
reply_markup/KeyboardButton
|
|
||||||
reply_markup/ReplyKeyboardMarkup
|
|
||||||
reply_markup/ReplyKeyboardRemove
|
|
||||||
CallbackQuery
|
|
@ -1,5 +0,0 @@
|
|||||||
ForceReply
|
|
||||||
==========
|
|
||||||
|
|
||||||
.. autoclass:: pyrogram.ForceReply
|
|
||||||
:members:
|
|
@ -1,5 +0,0 @@
|
|||||||
InlineKeyboardButton
|
|
||||||
====================
|
|
||||||
|
|
||||||
.. autoclass:: pyrogram.InlineKeyboardButton
|
|
||||||
:members:
|
|
@ -1,5 +0,0 @@
|
|||||||
InlineKeyboardMarkup
|
|
||||||
====================
|
|
||||||
|
|
||||||
.. autoclass:: pyrogram.InlineKeyboardMarkup
|
|
||||||
:members:
|
|
@ -1,5 +0,0 @@
|
|||||||
KeyboardButton
|
|
||||||
==============
|
|
||||||
|
|
||||||
.. autoclass:: pyrogram.KeyboardButton
|
|
||||||
:members:
|
|
@ -1,5 +0,0 @@
|
|||||||
ReplyKeyboardMarkup
|
|
||||||
===================
|
|
||||||
|
|
||||||
.. autoclass:: pyrogram.ReplyKeyboardMarkup
|
|
||||||
:members:
|
|
@ -1,5 +0,0 @@
|
|||||||
ReplyKeyboardRemove
|
|
||||||
===================
|
|
||||||
|
|
||||||
.. autoclass:: pyrogram.ReplyKeyboardRemove
|
|
||||||
:members:
|
|
@ -35,11 +35,8 @@ Example:
|
|||||||
password="password" # (if you have one)
|
password="password" # (if you have one)
|
||||||
)
|
)
|
||||||
|
|
||||||
app.start()
|
with app:
|
||||||
|
print(app.get_me())
|
||||||
print(app.get_me())
|
|
||||||
|
|
||||||
app.stop()
|
|
||||||
|
|
||||||
Sign Up
|
Sign Up
|
||||||
-------
|
-------
|
||||||
@ -67,8 +64,5 @@ Example:
|
|||||||
last_name="" # Can be an empty string
|
last_name="" # Can be an empty string
|
||||||
)
|
)
|
||||||
|
|
||||||
app.start()
|
with app:
|
||||||
|
print(app.get_me())
|
||||||
print(app.get_me())
|
|
||||||
|
|
||||||
app.stop()
|
|
||||||
|
11
docs/source/resources/Changelog.rst
Normal file
11
docs/source/resources/Changelog.rst
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
Changelog
|
||||||
|
=========
|
||||||
|
|
||||||
|
Currently, all Pyrogram release notes live inside the GitHub repository web page:
|
||||||
|
https://github.com/pyrogram/pyrogram/releases
|
||||||
|
|
||||||
|
(You will be automatically redirected in 10 seconds.)
|
||||||
|
|
||||||
|
.. raw:: html
|
||||||
|
|
||||||
|
<meta http-equiv="refresh" content="10; URL=https://github.com/pyrogram/pyrogram/releases"/>
|
126
docs/source/resources/SmartPlugins.rst
Normal file
126
docs/source/resources/SmartPlugins.rst
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
Smart Plugins
|
||||||
|
=============
|
||||||
|
|
||||||
|
Pyrogram embeds a **smart** (automatic) and lightweight plugin system that is meant to further simplify the organization
|
||||||
|
of large projects and to provide a way for creating pluggable components that can be **easily shared** across different
|
||||||
|
Pyrogram applications with **minimal boilerplate code**.
|
||||||
|
|
||||||
|
.. tip::
|
||||||
|
|
||||||
|
Smart Plugins are completely optional and disabled by default.
|
||||||
|
|
||||||
|
Introduction
|
||||||
|
------------
|
||||||
|
|
||||||
|
Prior to the Smart Plugin system, pluggable handlers were already possible. For example, if you wanted to modularize
|
||||||
|
your applications, you had to do something like this...
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
This is an example application that replies in private chats with two messages: one containing the same
|
||||||
|
text message you sent and the other containing the reversed text message.
|
||||||
|
|
||||||
|
Example: *"Pyrogram"* replies with *"Pyrogram"* and *"margoryP"*
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
myproject/
|
||||||
|
config.ini
|
||||||
|
handlers.py
|
||||||
|
main.py
|
||||||
|
|
||||||
|
- ``handlers.py``
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
def echo(client, message):
|
||||||
|
message.reply(message.text)
|
||||||
|
|
||||||
|
|
||||||
|
def echo_reversed(client, message):
|
||||||
|
message.reply(message.text[::-1])
|
||||||
|
|
||||||
|
- ``main.py``
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from pyrogram import Client, MessageHandler, Filters
|
||||||
|
|
||||||
|
from handlers import echo, echo_reversed
|
||||||
|
|
||||||
|
app = Client("my_account")
|
||||||
|
|
||||||
|
app.add_handler(
|
||||||
|
MessageHandler(
|
||||||
|
echo,
|
||||||
|
Filters.text & Filters.private))
|
||||||
|
|
||||||
|
app.add_handler(
|
||||||
|
MessageHandler(
|
||||||
|
echo_reversed,
|
||||||
|
Filters.text & Filters.private),
|
||||||
|
group=1)
|
||||||
|
|
||||||
|
app.run()
|
||||||
|
|
||||||
|
...which is already nice and doesn't add *too much* boilerplate code, but things can get boring still; you have to
|
||||||
|
manually ``import``, manually :meth:`add_handler <pyrogram.Client.add_handler>` and manually instantiate each
|
||||||
|
:obj:`MessageHandler <pyrogram.MessageHandler>` object because **you can't use those cool decorators** for your
|
||||||
|
functions. So... What if you could?
|
||||||
|
|
||||||
|
Using Smart Plugins
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
Setting up your Pyrogram project to accommodate Smart Plugins is pretty straightforward:
|
||||||
|
|
||||||
|
#. Create a new folder to store all the plugins (e.g.: "plugins").
|
||||||
|
#. Put your files full of plugins inside.
|
||||||
|
#. Enable plugins in your Client.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
This is the same example application `as shown above <#introduction>`_, written using the Smart Plugin system.
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
:emphasize-lines: 2, 3
|
||||||
|
|
||||||
|
myproject/
|
||||||
|
plugins/
|
||||||
|
handlers.py
|
||||||
|
config.ini
|
||||||
|
main.py
|
||||||
|
|
||||||
|
- ``plugins/handlers.py``
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
:emphasize-lines: 4, 9
|
||||||
|
|
||||||
|
from pyrogram import Client, Filters
|
||||||
|
|
||||||
|
|
||||||
|
@Client.on_message(Filters.text & Filters.private)
|
||||||
|
def echo(client, message):
|
||||||
|
message.reply(message.text)
|
||||||
|
|
||||||
|
|
||||||
|
@Client.on_message(Filters.text & Filters.private, group=1)
|
||||||
|
def echo_reversed(client, message):
|
||||||
|
message.reply(message.text[::-1])
|
||||||
|
|
||||||
|
- ``main.py``
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from pyrogram import Client
|
||||||
|
|
||||||
|
Client("my_account", plugins_dir="plugins").run()
|
||||||
|
|
||||||
|
The first important thing to note is the new ``plugins`` folder, whose name is passed to the the ``plugins_dir``
|
||||||
|
parameter when creating a :obj:`Client <pyrogram.Client>` in the ``main.py`` file — you can put *any python file* in
|
||||||
|
there and each file can contain *any decorated function* (handlers) with only one limitation: within a single plugin
|
||||||
|
file you must use different names for each decorated function. Your Pyrogram Client instance will **automatically**
|
||||||
|
scan the folder upon creation 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
|
||||||
|
functions in a static way, i.e. **without having the Client instance around**: simply use ``@Client`` (Client class)
|
||||||
|
instead of the usual ``@app`` (Client instance) namespace and things will work just the same.
|
39
docs/source/resources/TestServers.rst
Normal file
39
docs/source/resources/TestServers.rst
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
Test Servers
|
||||||
|
============
|
||||||
|
|
||||||
|
If you wish to test your application in a separate environment, Pyrogram is able to authorize your account into
|
||||||
|
Telegram's test servers without hassle. All you need to do is start a new session (e.g.: "my_account_test") using
|
||||||
|
``test_mode=True``:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from pyrogram import Client
|
||||||
|
|
||||||
|
with Client("my_account_test", test_mode=True) as app:
|
||||||
|
print(app.get_me())
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
If this is the first time you login into test servers, you will be asked to register your account first.
|
||||||
|
Don't worry about your contacts and chats, they will be kept untouched inside the production environment;
|
||||||
|
accounts authorized on test servers reside in a different, parallel instance of a Telegram database.
|
||||||
|
|
||||||
|
Test Mode in Official Apps
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
You can also login yourself into test servers using official desktop apps, such as Webogram and TDesktop:
|
||||||
|
|
||||||
|
- **Webogram**: Login here: https://web.telegram.org/?test=1
|
||||||
|
- **TDesktop**: Open settings and type ``testmode``.
|
||||||
|
|
||||||
|
Test Numbers
|
||||||
|
------------
|
||||||
|
|
||||||
|
Beside normal numbers, the test environment allows you to login with reserved test numbers.
|
||||||
|
Valid phone numbers follow the pattern ``99966XYYYY``, where ``X`` is the DC number (1 to 3) and ``YYYY`` are random
|
||||||
|
numbers. Users with such numbers always get ``XXXXX`` as the confirmation code (the DC number, repeated five times).
|
||||||
|
|
||||||
|
.. important::
|
||||||
|
|
||||||
|
Do not store any important or private information in such test users' accounts; anyone can make use of the
|
||||||
|
simplified authorization mechanism and login at any time.
|
@ -1,8 +1,11 @@
|
|||||||
Text Formatting
|
Text Formatting
|
||||||
===============
|
===============
|
||||||
|
|
||||||
Pyrogram, just like `Telegram Bot API`_, supports basic Markdown and HTML formatting styles for text messages and
|
Pyrogram, just like the `Telegram Bot API`_, natively supports basic Markdown and HTML formatting styles for text
|
||||||
media captions; Markdown uses the same syntax as Telegram Desktop's and is enabled by default.
|
messages and media captions.
|
||||||
|
|
||||||
|
Markdown style uses the same syntax as Telegram Desktop's and is enabled by default.
|
||||||
|
|
||||||
Beside bold, italic, and pre-formatted code, **Pyrogram does also support inline URLs and inline mentions of users**.
|
Beside bold, italic, and pre-formatted code, **Pyrogram does also support inline URLs and inline mentions of users**.
|
||||||
|
|
||||||
Markdown Style
|
Markdown Style
|
||||||
@ -11,7 +14,7 @@ Markdown Style
|
|||||||
To use this mode, pass :obj:`MARKDOWN <pyrogram.ParseMode.MARKDOWN>` or "markdown" in the *parse_mode* field when using
|
To use this mode, pass :obj:`MARKDOWN <pyrogram.ParseMode.MARKDOWN>` or "markdown" in the *parse_mode* field when using
|
||||||
:obj:`send_message() <pyrogram.Client.send_message>`. Use the following syntax in your message:
|
:obj:`send_message() <pyrogram.Client.send_message>`. Use the following syntax in your message:
|
||||||
|
|
||||||
.. code-block:: txt
|
.. code-block:: text
|
||||||
|
|
||||||
**bold text**
|
**bold text**
|
||||||
|
|
||||||
@ -34,7 +37,7 @@ HTML Style
|
|||||||
To use this mode, pass :obj:`HTML <pyrogram.ParseMode.HTML>` or "html" in the *parse_mode* field when using
|
To use this mode, pass :obj:`HTML <pyrogram.ParseMode.HTML>` or "html" in the *parse_mode* field when using
|
||||||
:obj:`send_message() <pyrogram.Client.send_message>`. The following tags are currently supported:
|
:obj:`send_message() <pyrogram.Client.send_message>`. The following tags are currently supported:
|
||||||
|
|
||||||
.. code-block:: txt
|
.. code-block:: text
|
||||||
|
|
||||||
<b>bold</b>, <strong>bold</strong>
|
<b>bold</b>, <strong>bold</strong>
|
||||||
|
|
||||||
@ -46,9 +49,7 @@ To use this mode, pass :obj:`HTML <pyrogram.ParseMode.HTML>` or "html" in the *p
|
|||||||
|
|
||||||
<code>inline fixed-width code</code>
|
<code>inline fixed-width code</code>
|
||||||
|
|
||||||
<pre>pre-formatted fixed-width
|
<pre>pre-formatted fixed-width code block</pre>
|
||||||
code block
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
.. note:: Mentions are only guaranteed to work if you have already met the user (in groups or private chats).
|
.. note:: Mentions are only guaranteed to work if you have already met the user (in groups or private chats).
|
||||||
|
|
||||||
|
@ -2,188 +2,58 @@ Update Handling
|
|||||||
===============
|
===============
|
||||||
|
|
||||||
Updates are events that happen in your Telegram account (incoming messages, new channel posts, new members join, ...)
|
Updates are events that happen in your Telegram account (incoming messages, new channel posts, new members join, ...)
|
||||||
and are handled by registering one or more callback functions with an Handler. There are multiple Handlers to choose
|
and can be handled by registering one or more callback functions in your app by using `Handlers <../pyrogram/Handlers.html>`_.
|
||||||
from, one for each kind of update:
|
|
||||||
|
|
||||||
- `MessageHandler <../pyrogram/handlers/MessageHandler.html>`_
|
To put it simply, whenever an update is received from Telegram it will be dispatched and your previously defined callback
|
||||||
- `DeletedMessagesHandler <../pyrogram/handlers/DeletedMessagesHandler.html>`_
|
function(s) matching it will be called back with the update itself as argument.
|
||||||
- `CallbackQueryHandler <../pyrogram/handlers/CallbackQueryHandler.html>`_
|
|
||||||
- `RawUpdateHandler <../pyrogram/handlers/RawUpdateHandler.html>`_
|
|
||||||
- `DisconnectHandler <../pyrogram/handlers/DisconnectHandler.html>`_
|
|
||||||
|
|
||||||
Registering an Handler
|
Registering an Handler
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
We shall examine the :obj:`MessageHandler <pyrogram.MessageHandler>`, which will be in charge for handling
|
To explain how handlers work let's have a look at the most used one, the
|
||||||
:obj:`Message <pyrogram.Message>` objects.
|
:obj:`MessageHandler <pyrogram.MessageHandler>`, which will be in charge for handling :obj:`Message <pyrogram.Message>`
|
||||||
|
updates coming from all around your chats. Every other handler shares the same setup logic; you should not have troubles
|
||||||
- The easiest and nicest way to register a MessageHandler is by decorating your function with the
|
settings them up once you learn from this section.
|
||||||
:meth:`on_message() <pyrogram.Client.on_message>` decorator. Here's a full example that prints out the content
|
|
||||||
of a message as soon as it arrives.
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
from pyrogram import Client
|
|
||||||
|
|
||||||
app = Client("my_account")
|
|
||||||
|
|
||||||
|
|
||||||
@app.on_message()
|
Using Decorators
|
||||||
def my_handler(client, message):
|
|
||||||
print(message)
|
|
||||||
|
|
||||||
|
|
||||||
app.run()
|
|
||||||
|
|
||||||
- If you prefer not to use decorators, there is an alternative way for registering Handlers.
|
|
||||||
This is useful, for example, when you want to keep your callback functions in separate files.
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
from pyrogram import Client, MessageHandler
|
|
||||||
|
|
||||||
|
|
||||||
def my_handler(client, message):
|
|
||||||
print(message)
|
|
||||||
|
|
||||||
|
|
||||||
app = Client("my_account")
|
|
||||||
|
|
||||||
app.add_handler(MessageHandler(my_handler))
|
|
||||||
|
|
||||||
app.run()
|
|
||||||
|
|
||||||
Using Filters
|
|
||||||
-------------
|
|
||||||
|
|
||||||
For a finer grained control over what kind of messages will be allowed or not in your callback functions, you can use
|
|
||||||
:class:`Filters <pyrogram.Filters>`.
|
|
||||||
|
|
||||||
- This example will show you how to **only** handle messages containing an
|
|
||||||
:obj:`Audio <pyrogram.Audio>` object and filter out any other message:
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
from pyrogram import Filters
|
|
||||||
|
|
||||||
|
|
||||||
@app.on_message(Filters.audio)
|
|
||||||
def my_handler(client, message):
|
|
||||||
print(message)
|
|
||||||
|
|
||||||
- or, without decorators:
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
from pyrogram import Filters, MessageHandler
|
|
||||||
|
|
||||||
|
|
||||||
def my_handler(client, message):
|
|
||||||
print(message)
|
|
||||||
|
|
||||||
|
|
||||||
app.add_handler(MessageHandler(my_handler, Filters.audio))
|
|
||||||
|
|
||||||
Combining Filters
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
Filters can also be used in a more advanced way by combining more filters together using bitwise operators:
|
|
||||||
|
|
||||||
- Use ``~`` to invert a filter (behaves like the ``not`` operator).
|
|
||||||
- Use ``&`` and ``|`` to merge two filters (behave like ``and``, ``or`` operators respectively).
|
|
||||||
|
|
||||||
Here are some examples:
|
|
||||||
|
|
||||||
- Message is a **text** message **and** is **not edited**.
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
@app.on_message(Filters.text & ~Filters.edited)
|
|
||||||
def my_handler(client, message):
|
|
||||||
print(message)
|
|
||||||
|
|
||||||
- Message is a **sticker** **and** is coming from a **channel or** a **private** chat.
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
@app.on_message(Filters.sticker & (Filters.channel | Filters.private))
|
|
||||||
def my_handler(client, message):
|
|
||||||
print(message)
|
|
||||||
|
|
||||||
Advanced Filters
|
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
Some filters, like :obj:`command() <pyrogram.Filters.command>` or :obj:`regex() <pyrogram.Filters.regex>`
|
The easiest and nicest way to register a MessageHandler is by decorating your function with the
|
||||||
can also accept arguments:
|
:meth:`on_message() <pyrogram.Client.on_message>` decorator. Here's a full example that prints out the content
|
||||||
|
of a message as soon as it arrives.
|
||||||
- Message is either a */start* or */help* **command**.
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
@app.on_message(Filters.command(["start", "help"]))
|
|
||||||
def my_handler(client, message):
|
|
||||||
print(message)
|
|
||||||
|
|
||||||
- Message is a **text** message matching the given **regex** pattern.
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
@app.on_message(Filters.regex("pyrogram"))
|
|
||||||
def my_handler(client, message):
|
|
||||||
print(message)
|
|
||||||
|
|
||||||
More handlers using different filters can also live together.
|
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
@app.on_message(Filters.command("start"))
|
from pyrogram import Client
|
||||||
def start_command(client, message):
|
|
||||||
print("This is the /start command")
|
app = Client("my_account")
|
||||||
|
|
||||||
|
|
||||||
@app.on_message(Filters.command("help"))
|
@app.on_message()
|
||||||
def help_command(client, message):
|
def my_handler(client, message):
|
||||||
print("This is the /help command")
|
print(message)
|
||||||
|
|
||||||
|
|
||||||
@app.on_message(Filters.chat("PyrogramChat"))
|
app.run()
|
||||||
def from_pyrogramchat(client, message):
|
|
||||||
print("New message in @PyrogramChat")
|
|
||||||
|
|
||||||
Handler Groups
|
Using add_handler()
|
||||||
--------------
|
-------------------
|
||||||
|
|
||||||
If you register handlers with overlapping filters, only the first one is executed and any other handler will be ignored.
|
If you prefer not to use decorators for any reason, there is an alternative way for registering Handlers.
|
||||||
|
This is useful, for example, when you want to keep your callback functions in separate files.
|
||||||
In order to process the same message more than once, you can register your handler in a different group.
|
|
||||||
Groups are identified by a number (number 0 being the default) and are sorted. This means that a lower group number has
|
|
||||||
a higher priority.
|
|
||||||
|
|
||||||
For example, in:
|
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
@app.on_message(Filters.text | Filters.sticker)
|
from pyrogram import Client, MessageHandler
|
||||||
def text_or_sticker(client, message):
|
|
||||||
print("Text or Sticker")
|
|
||||||
|
|
||||||
|
|
||||||
@app.on_message(Filters.text)
|
def my_handler(client, message):
|
||||||
def just_text(client, message):
|
print(message)
|
||||||
print("Just Text")
|
|
||||||
|
|
||||||
``just_text`` is never executed. To enable it, simply register the function using a different group:
|
|
||||||
|
|
||||||
.. code-block:: python
|
app = Client("my_account")
|
||||||
|
|
||||||
@app.on_message(Filters.text, group=1)
|
app.add_handler(MessageHandler(my_handler))
|
||||||
def just_text(client, message):
|
|
||||||
print("Just Text")
|
|
||||||
|
|
||||||
or, if you want ``just_text`` to be fired *before* ``text_or_sticker``:
|
app.run()
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
@app.on_message(Filters.text, group=-1)
|
|
||||||
def just_text(client, message):
|
|
||||||
print("Just Text")
|
|
||||||
|
228
docs/source/resources/UsingFilters.rst
Normal file
228
docs/source/resources/UsingFilters.rst
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
Using Filters
|
||||||
|
=============
|
||||||
|
|
||||||
|
For a finer grained control over what kind of messages will be allowed or not in your callback functions, you can use
|
||||||
|
:class:`Filters <pyrogram.Filters>`.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
This section makes use of Handlers to handle updates. Learn more at `Update Handling <UpdateHandling.html>`_.
|
||||||
|
|
||||||
|
- This example will show you how to **only** handle messages containing an :obj:`Audio <pyrogram.Audio>` object and
|
||||||
|
ignore any other message:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from pyrogram import Filters
|
||||||
|
|
||||||
|
|
||||||
|
@app.on_message(Filters.audio)
|
||||||
|
def my_handler(client, message):
|
||||||
|
print(message)
|
||||||
|
|
||||||
|
- or, without decorators:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from pyrogram import Filters, MessageHandler
|
||||||
|
|
||||||
|
|
||||||
|
def my_handler(client, message):
|
||||||
|
print(message)
|
||||||
|
|
||||||
|
|
||||||
|
app.add_handler(MessageHandler(my_handler, Filters.audio))
|
||||||
|
|
||||||
|
Combining Filters
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
Filters can also be used in a more advanced way by inverting and combining more filters together using bitwise
|
||||||
|
operators:
|
||||||
|
|
||||||
|
- Use ``~`` to invert a filter (behaves like the ``not`` operator).
|
||||||
|
- Use ``&`` and ``|`` to merge two filters (behave like ``and``, ``or`` operators respectively).
|
||||||
|
|
||||||
|
Here are some examples:
|
||||||
|
|
||||||
|
- Message is a **text** message **and** is **not edited**.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
@app.on_message(Filters.text & ~Filters.edited)
|
||||||
|
def my_handler(client, message):
|
||||||
|
print(message)
|
||||||
|
|
||||||
|
- Message is a **sticker** **and** is coming from a **channel or** a **private** chat.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
@app.on_message(Filters.sticker & (Filters.channel | Filters.private))
|
||||||
|
def my_handler(client, message):
|
||||||
|
print(message)
|
||||||
|
|
||||||
|
Advanced Filters
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Some filters, like :meth:`command() <pyrogram.Filters.command>` or :meth:`regex() <pyrogram.Filters.regex>`
|
||||||
|
can also accept arguments:
|
||||||
|
|
||||||
|
- Message is either a */start* or */help* **command**.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
@app.on_message(Filters.command(["start", "help"]))
|
||||||
|
def my_handler(client, message):
|
||||||
|
print(message)
|
||||||
|
|
||||||
|
- Message is a **text** message matching the given **regex** pattern.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
@app.on_message(Filters.regex("pyrogram"))
|
||||||
|
def my_handler(client, message):
|
||||||
|
print(message)
|
||||||
|
|
||||||
|
More handlers using different filters can also live together.
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
@app.on_message(Filters.command("start"))
|
||||||
|
def start_command(client, message):
|
||||||
|
print("This is the /start command")
|
||||||
|
|
||||||
|
|
||||||
|
@app.on_message(Filters.command("help"))
|
||||||
|
def help_command(client, message):
|
||||||
|
print("This is the /help command")
|
||||||
|
|
||||||
|
|
||||||
|
@app.on_message(Filters.chat("PyrogramChat"))
|
||||||
|
def from_pyrogramchat(client, message):
|
||||||
|
print("New message in @PyrogramChat")
|
||||||
|
|
||||||
|
Handler Groups
|
||||||
|
--------------
|
||||||
|
|
||||||
|
If you register handlers with overlapping filters, only the first one is executed and any other handler will be ignored.
|
||||||
|
|
||||||
|
In order to process the same message more than once, you can register your handler in a different group.
|
||||||
|
Groups are identified by a number (number 0 being the default) and are sorted. This means that a lower group number has
|
||||||
|
a higher priority.
|
||||||
|
|
||||||
|
For example, in:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
@app.on_message(Filters.text | Filters.sticker)
|
||||||
|
def text_or_sticker(client, message):
|
||||||
|
print("Text or Sticker")
|
||||||
|
|
||||||
|
|
||||||
|
@app.on_message(Filters.text)
|
||||||
|
def just_text(client, message):
|
||||||
|
print("Just Text")
|
||||||
|
|
||||||
|
``just_text`` is never executed because ``text_or_sticker`` already handles texts. To enable it, simply register the
|
||||||
|
function using a different group:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
@app.on_message(Filters.text, group=1)
|
||||||
|
def just_text(client, message):
|
||||||
|
print("Just Text")
|
||||||
|
|
||||||
|
or, if you want ``just_text`` to be fired *before* ``text_or_sticker`` (note ``-1``, which is less than ``0``):
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
@app.on_message(Filters.text, group=-1)
|
||||||
|
def just_text(client, message):
|
||||||
|
print("Just Text")
|
||||||
|
|
||||||
|
Custom Filters
|
||||||
|
--------------
|
||||||
|
|
||||||
|
Pyrogram already provides lots of built-in :class:`Filters <pyrogram.Filters>` to work with, but in case you can't find
|
||||||
|
a specific one for your needs or want to build a custom filter by yourself (to be used in a different handler, for
|
||||||
|
example) you can use :meth:`Filters.create() <pyrogram.Filters.create>`.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
At the moment, the built-in filters are intended to be used with the :obj:`MessageHandler <pyrogram.MessageHandler>`
|
||||||
|
only.
|
||||||
|
|
||||||
|
An example to demonstrate how custom filters work is to show how to create and use one for the
|
||||||
|
:obj:`CallbackQueryHandler <pyrogram.CallbackQueryHandler>`. Note that callback queries updates are only received by Bots;
|
||||||
|
create and `authorize your bot <../start/Setup.html#bot-authorization>`_, then send a message with an inline keyboard to
|
||||||
|
yourself. This allows you to test your filter by pressing the inline button:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
from pyrogram import InlineKeyboardMarkup, InlineKeyboardButton
|
||||||
|
|
||||||
|
app.send_message(
|
||||||
|
"username", # Change this to your username or id
|
||||||
|
"Pyrogram's custom filter test",
|
||||||
|
reply_markup=InlineKeyboardMarkup(
|
||||||
|
[[InlineKeyboardButton("Press me", b"pyrogram")]]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
Basic Filters
|
||||||
|
^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
For this basic filter we will be using only the first two parameters of :meth:`Filters.create() <pyrogram.Filters.create>`.
|
||||||
|
|
||||||
|
The code below creates a simple filter for hardcoded callback data. This filter will only allow callback queries
|
||||||
|
containing "pyrogram" as data:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
hardcoded_data = Filters.create(
|
||||||
|
name="HardcodedData",
|
||||||
|
func=lambda filter, callback_query: callback_query.data == b"pyrogram"
|
||||||
|
)
|
||||||
|
|
||||||
|
The ``lambda`` operator in python is used to create small anonymous functions and is perfect for this example, the same
|
||||||
|
could be achieved with a normal function, but we don't really need it as it makes sense only inside the filter itself:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
def func(filter, callback_query):
|
||||||
|
return callback_query.data == b"pyrogram"
|
||||||
|
|
||||||
|
hardcoded_data = Filters.create(
|
||||||
|
name="HardcodedData",
|
||||||
|
func=func
|
||||||
|
)
|
||||||
|
|
||||||
|
The filter usage remains the same:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
@app.on_callback_query(hardcoded_data)
|
||||||
|
def pyrogram_data(client, callback_query):
|
||||||
|
client.answer_callback_query(callback_query.id, "it works!")
|
||||||
|
|
||||||
|
Filters with Arguments
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
A much cooler filter would be one that accepts "pyrogram" or any other data as argument at usage time.
|
||||||
|
A dynamic filter like this will make use of the third parameter of :meth:`Filters.create() <pyrogram.Filters.create>`.
|
||||||
|
|
||||||
|
This is how a dynamic custom filter looks like:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
def dynamic_data(data):
|
||||||
|
return Filters.create(
|
||||||
|
name="DynamicData",
|
||||||
|
func=lambda filter, callback_query: filter.data == callback_query.data,
|
||||||
|
data=data # "data" kwarg is accessed with "filter.data"
|
||||||
|
)
|
||||||
|
|
||||||
|
And its usage:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
@app.on_callback_query(dynamic_data(b"pyrogram"))
|
||||||
|
def pyrogram_data(client, callback_query):
|
||||||
|
client.answer_callback_query(callback_query.id, "it works!")
|
@ -4,47 +4,85 @@ 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) and pip
|
||||||
by following the instructions at https://pip.pypa.io/en/latest/installing/.
|
by following the instructions at https://pip.pypa.io/en/latest/installing/.
|
||||||
|
|
||||||
Pyrogram supports Python 3 only, starting from version 3.4 and PyPy.
|
.. important::
|
||||||
|
|
||||||
|
Pyrogram supports **Python 3** only, starting from version 3.4. **PyPy** is supported too.
|
||||||
|
|
||||||
Install Pyrogram
|
Install Pyrogram
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
- The easiest way to install and upgrade Pyrogram is by using **pip**:
|
- The easiest way to install and upgrade Pyrogram to its latest stable version is by using **pip**:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: text
|
||||||
|
|
||||||
$ pip3 install --upgrade pyrogram
|
$ pip3 install --upgrade pyrogram
|
||||||
|
|
||||||
- or, with TgCrypto_ (recommended):
|
- or, with TgCrypto_ as extra requirement (recommended):
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: text
|
||||||
|
|
||||||
$ pip3 install --upgrade pyrogram[tgcrypto]
|
$ pip3 install --upgrade pyrogram[fast]
|
||||||
|
|
||||||
Bleeding Edge
|
Bleeding Edge
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
If you want the latest development version of Pyrogram, you can install it straight from the develop_
|
If you want the latest development version of Pyrogram, you can install it straight from the develop_
|
||||||
branch using this command:
|
branch using this command (you might need to install **git** first):
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: text
|
||||||
|
|
||||||
$ pip3 install --upgrade git+https://github.com/pyrogram/pyrogram.git
|
$ pip3 install --upgrade git+https://github.com/pyrogram/pyrogram.git
|
||||||
|
|
||||||
|
Asynchronous
|
||||||
|
------------
|
||||||
|
|
||||||
|
Pyrogram heavily depends on IO-bound network code (it's a cloud-based messaging client library after all), and here's
|
||||||
|
where asyncio shines the most by providing extra performance while running on a single OS-level thread only.
|
||||||
|
|
||||||
|
**A fully asynchronous variant of Pyrogram is therefore available** (Python 3.5+ required).
|
||||||
|
Use this command to install:
|
||||||
|
|
||||||
|
.. code-block:: text
|
||||||
|
|
||||||
|
$ pip3 install --upgrade git+https://github.com/pyrogram/pyrogram.git@asyncio
|
||||||
|
|
||||||
|
|
||||||
|
Pyrogram API remains the same and features are kept up to date from the non-async, default develop branch, but you
|
||||||
|
are obviously required Python asyncio knowledge in order to take full advantage of it.
|
||||||
|
|
||||||
|
|
||||||
|
.. tip::
|
||||||
|
|
||||||
|
The idea to turn Pyrogram fully asynchronous is still under consideration, but is wise to expect that in future this
|
||||||
|
would be the one and only way to work with Pyrogram.
|
||||||
|
|
||||||
|
You can start using Pyrogram Async variant right now as an excuse to learn more about asynchronous programming and
|
||||||
|
do experiments with it!
|
||||||
|
|
||||||
|
.. raw:: html
|
||||||
|
|
||||||
|
<script async
|
||||||
|
src="https://telegram.org/js/telegram-widget.js?4"
|
||||||
|
data-telegram-post="Pyrogram/4"
|
||||||
|
data-width="100%">
|
||||||
|
</script>
|
||||||
|
|
||||||
|
.. centered:: Subscribe to `@Pyrogram <https://t.me/Pyrogram>`_ for news and announcements
|
||||||
|
|
||||||
Verifying
|
Verifying
|
||||||
---------
|
---------
|
||||||
|
|
||||||
To verify that Pyrogram is correctly installed, open a Python shell and import it.
|
To verify that Pyrogram is correctly installed, open a Python shell and import it.
|
||||||
If no error shows up you are good to go.
|
If no error shows up you are good to go.
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: python
|
||||||
|
|
||||||
>>> import pyrogram
|
>>> import pyrogram
|
||||||
>>> pyrogram.__version__
|
>>> pyrogram.__version__
|
||||||
'0.7.5'
|
'0.9.4'
|
||||||
|
|
||||||
.. _TgCrypto: https://docs.pyrogram.ml/resources/TgCrypto
|
.. _TgCrypto: https://docs.pyrogram.ml/resources/TgCrypto
|
||||||
.. _develop: http://github.com/pyrogram/pyrogram
|
.. _develop: http://github.com/pyrogram/pyrogram
|
||||||
|
@ -8,22 +8,29 @@ with Pyrogram.
|
|||||||
API Keys
|
API Keys
|
||||||
--------
|
--------
|
||||||
|
|
||||||
The very first step requires you to obtain a valid Telegram API key.
|
The very first step requires you to obtain a valid Telegram API key (API id/hash pair).
|
||||||
If you already have one you can skip this step, otherwise:
|
If you already have one you can skip this step, otherwise:
|
||||||
|
|
||||||
#. Visit https://my.telegram.org/apps and log in with your Telegram Account.
|
#. Visit https://my.telegram.org/apps and log in with your Telegram Account.
|
||||||
#. Fill out the form to register a new Telegram application.
|
#. Fill out the form to register a new Telegram application.
|
||||||
#. Done. The Telegram API key consists of two parts: the **App api_id** and the **App api_hash**.
|
#. Done. The API key consists of two parts: **App api_id** and **App api_hash**.
|
||||||
|
|
||||||
.. important:: This key should be kept secret.
|
|
||||||
|
.. important::
|
||||||
|
|
||||||
|
This API key is personal and should be kept secret.
|
||||||
|
|
||||||
Configuration
|
Configuration
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
There are two ways to configure a Pyrogram application project, and you can choose the one that fits better for you:
|
The API key obtained in the `previous step <#api-keys>`_ defines a token for your application allowing you to access
|
||||||
|
the Telegram database using the MTProto API — **it is therefore required for all authorizations of both Users and Bots**.
|
||||||
|
|
||||||
|
Having it handy, it's time to configure your Pyrogram project. There are two ways to do so, and you can choose what
|
||||||
|
fits better for you:
|
||||||
|
|
||||||
- Create a new ``config.ini`` file at the root of your working directory, copy-paste the following and replace the
|
- Create a new ``config.ini`` file at the root of your working directory, copy-paste the following and replace the
|
||||||
**api_id** and **api_hash** values with `your own <#api-keys>`_. This is the preferred method because allows you
|
**api_id** and **api_hash** values with your own. This is the preferred method because allows you
|
||||||
to keep your credentials out of your code without having to deal with how to load them:
|
to keep your credentials out of your code without having to deal with how to load them:
|
||||||
|
|
||||||
.. code-block:: ini
|
.. code-block:: ini
|
||||||
@ -45,7 +52,9 @@ There are two ways to configure a Pyrogram application project, and you can choo
|
|||||||
api_hash="0123456789abcdef0123456789abcdef"
|
api_hash="0123456789abcdef0123456789abcdef"
|
||||||
)
|
)
|
||||||
|
|
||||||
.. note:: The examples below assume you have created a ``config.ini`` file, thus they won't show the *api_id*
|
.. note::
|
||||||
|
|
||||||
|
The examples below assume you have created a ``config.ini`` file, thus they won't show the *api_id*
|
||||||
and *api_hash* parameters usage.
|
and *api_hash* parameters usage.
|
||||||
|
|
||||||
User Authorization
|
User Authorization
|
||||||
@ -66,7 +75,7 @@ the :class:`Client <pyrogram.Client>` class by passing to it a ``session_name``
|
|||||||
This starts an interactive shell asking you to input your **phone number** (including your `Country Code`_)
|
This starts an interactive shell asking you to input your **phone number** (including your `Country Code`_)
|
||||||
and the **phone code** you will receive:
|
and the **phone code** you will receive:
|
||||||
|
|
||||||
.. code::
|
.. code-block:: text
|
||||||
|
|
||||||
Enter phone number: +39**********
|
Enter phone number: +39**********
|
||||||
Is "+39**********" correct? (y/n): y
|
Is "+39**********" correct? (y/n): y
|
||||||
@ -76,16 +85,19 @@ After successfully authorizing yourself, a new file called ``my_account.session`
|
|||||||
Pyrogram executing API calls with your identity. This file will be loaded again when you restart your app,
|
Pyrogram executing API calls with your identity. This file will be loaded again when you restart your app,
|
||||||
and as long as you keep the session alive, Pyrogram won't ask you again to enter your phone number.
|
and as long as you keep the session alive, Pyrogram won't ask you again to enter your phone number.
|
||||||
|
|
||||||
.. important:: Your ``*.session`` file(s) must be kept secret.
|
.. important::
|
||||||
|
|
||||||
|
Your ``*.session`` files are personal and must be kept secret.
|
||||||
|
|
||||||
Bot Authorization
|
Bot Authorization
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
Being written entirely from the ground up, Pyrogram is also able to authorize Bots.
|
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 which also make use of MTProto, the underlying Telegram protocol.
|
BotFather_. Bot tokens replace the Users' phone numbers only — you still need to
|
||||||
This means that you can use Pyrogram to execute API calls with a Bot identity.
|
`configure a Telegram API key <#configuration>`_ with Pyrogram, even when using Bots.
|
||||||
|
|
||||||
Instead of phone numbers, Bots are authorized via their tokens which are created by BotFather_:
|
The authorization process is automatically managed. All you need to do is pass the bot token as ``session_name``.
|
||||||
|
The session file will be named after the Bot user_id, which is ``123456.session`` for the example below.
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
@ -94,9 +106,6 @@ Instead of phone numbers, Bots are authorized via their tokens which are created
|
|||||||
app = Client("123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11")
|
app = Client("123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11")
|
||||||
app.run()
|
app.run()
|
||||||
|
|
||||||
That's all, no further action is needed. The session file will be named after the Bot user_id, which is
|
|
||||||
``123456.session`` for the example above.
|
|
||||||
|
|
||||||
.. _installed Pyrogram: Installation.html
|
.. _installed Pyrogram: Installation.html
|
||||||
.. _`Country Code`: https://en.wikipedia.org/wiki/List_of_country_calling_codes
|
.. _`Country Code`: https://en.wikipedia.org/wiki/List_of_country_calling_codes
|
||||||
.. _BotFather: https://t.me/botfather
|
.. _BotFather: https://t.me/botfather
|
@ -10,35 +10,50 @@ High-level API
|
|||||||
The easiest and recommended way to interact with Telegram is via the high-level Pyrogram methods_ and types_, which are
|
The easiest and recommended way to interact with Telegram is via the high-level Pyrogram methods_ and types_, which are
|
||||||
named after the `Telegram Bot API`_.
|
named after the `Telegram Bot API`_.
|
||||||
|
|
||||||
Examples (more on `GitHub <https://github.com/pyrogram/pyrogram/tree/develop/examples>`_):
|
Here's a simple example:
|
||||||
|
|
||||||
- Get information about the authorized user:
|
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
|
from pyrogram import Client
|
||||||
|
|
||||||
|
app = Client("my_account")
|
||||||
|
|
||||||
|
app.start()
|
||||||
|
|
||||||
print(app.get_me())
|
print(app.get_me())
|
||||||
|
app.send_message("me", "Hi there! I'm using **Pyrogram**")
|
||||||
|
app.send_location("me", 51.500729, -0.124583)
|
||||||
|
|
||||||
- Send a message to yourself (Saved Messages):
|
app.stop()
|
||||||
|
|
||||||
|
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
|
||||||
|
exceptions in your code:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
|
||||||
app.send_message("me", "Hi there! I'm using Pyrogram")
|
from pyrogram import Client
|
||||||
|
|
||||||
- Upload a new photo (with caption):
|
app = Client("my_account")
|
||||||
|
|
||||||
.. code-block:: python
|
with app:
|
||||||
|
print(app.get_me())
|
||||||
|
app.send_message("me", "Hi there! I'm using **Pyrogram**")
|
||||||
|
app.send_location("me", 51.500729, -0.124583)
|
||||||
|
|
||||||
app.send_photo("me", "/home/dan/perla.jpg", "Cute!")
|
More examples on `GitHub <https://github.com/pyrogram/pyrogram/tree/develop/examples>`_.
|
||||||
|
|
||||||
Raw Functions
|
Raw Functions
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
If you can't find a high-level method for your needs or if want complete, low-level access to the whole Telegram API,
|
If you can't find a high-level method for your needs or if you want complete, low-level access to the whole Telegram API,
|
||||||
you have to use the raw :mod:`functions <pyrogram.api.functions>` and :mod:`types <pyrogram.api.types>` exposed by the
|
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>`
|
``pyrogram.api`` package and call any Telegram API method you wish using the :meth:`send() <pyrogram.Client.send>`
|
||||||
method provided by the Client class.
|
method provided by the Client class.
|
||||||
|
|
||||||
.. hint:: Every high-level method mentioned in the section above is built on top of these raw functions.
|
.. hint::
|
||||||
|
|
||||||
|
Every high-level method mentioned in the section above 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.
|
||||||
@ -54,17 +69,13 @@ Examples (more on `GitHub <https://github.com/pyrogram/pyrogram/tree/develop/exa
|
|||||||
from pyrogram import Client
|
from pyrogram import Client
|
||||||
from pyrogram.api import functions
|
from pyrogram.api import functions
|
||||||
|
|
||||||
app = Client("my_account")
|
with Client("my_account") as app:
|
||||||
app.start()
|
app.send(
|
||||||
|
functions.account.UpdateProfile(
|
||||||
app.send(
|
first_name="Dan", last_name="Tès",
|
||||||
functions.account.UpdateProfile(
|
about="Bio written from Pyrogram"
|
||||||
first_name="Dan", last_name="Tès",
|
)
|
||||||
about="Bio written from Pyrogram"
|
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
app.stop()
|
|
||||||
|
|
||||||
- Share your Last Seen time only with your contacts:
|
- Share your Last Seen time only with your contacts:
|
||||||
|
|
||||||
@ -73,17 +84,13 @@ Examples (more on `GitHub <https://github.com/pyrogram/pyrogram/tree/develop/exa
|
|||||||
from pyrogram import Client
|
from pyrogram import Client
|
||||||
from pyrogram.api import functions, types
|
from pyrogram.api import functions, types
|
||||||
|
|
||||||
app = Client("my_account")
|
with Client("my_account") as app:
|
||||||
app.start()
|
app.send(
|
||||||
|
functions.account.SetPrivacy(
|
||||||
app.send(
|
key=types.InputPrivacyKeyStatusTimestamp(),
|
||||||
functions.account.SetPrivacy(
|
rules=[types.InputPrivacyValueAllowContacts()]
|
||||||
key=types.InputPrivacyKeyStatusTimestamp(),
|
)
|
||||||
rules=[types.InputPrivacyValueAllowContacts()]
|
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
app.stop()
|
|
||||||
|
|
||||||
- Invite users to your channel/supergroup:
|
- Invite users to your channel/supergroup:
|
||||||
|
|
||||||
@ -92,25 +99,21 @@ Examples (more on `GitHub <https://github.com/pyrogram/pyrogram/tree/develop/exa
|
|||||||
from pyrogram import Client
|
from pyrogram import Client
|
||||||
from pyrogram.api import functions, types
|
from pyrogram.api import functions, types
|
||||||
|
|
||||||
app = Client("my_account")
|
with Client("my_account") as app:
|
||||||
app.start()
|
app.send(
|
||||||
|
functions.channels.InviteToChannel(
|
||||||
app.send(
|
channel=app.resolve_peer(123456789), # ID or Username
|
||||||
functions.channels.InviteToChannel(
|
users=[ # The users you want to invite
|
||||||
channel=app.resolve_peer(123456789), # ID or Username
|
app.resolve_peer(23456789), # By ID
|
||||||
users=[ # The users you want to invite
|
app.resolve_peer("username"), # By username
|
||||||
app.resolve_peer(23456789), # By ID
|
app.resolve_peer("393281234567"), # By phone number
|
||||||
app.resolve_peer("username"), # By username
|
]
|
||||||
app.resolve_peer("393281234567"), # By phone number
|
)
|
||||||
]
|
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
app.stop()
|
.. _methods: ../pyrogram/Client.html#messages
|
||||||
|
.. _plenty of them: ../pyrogram/Client.html#messages
|
||||||
.. _methods: ../pyrogram/Client.html#available-methods
|
.. _types: ../pyrogram/Types.html
|
||||||
.. _plenty of them: ../pyrogram/Client.html#available-methods
|
|
||||||
.. _types: ../pyrogram/types/index.html
|
|
||||||
.. _Raw Functions: Usage.html#using-raw-functions
|
.. _Raw Functions: Usage.html#using-raw-functions
|
||||||
.. _Community: https://t.me/PyrogramChat
|
.. _Community: https://t.me/PyrogramChat
|
||||||
.. _project set up: Setup.html
|
.. _project set up: Setup.html
|
||||||
|
121
examples/LICENSE
Normal file
121
examples/LICENSE
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
Creative Commons Legal Code
|
||||||
|
|
||||||
|
CC0 1.0 Universal
|
||||||
|
|
||||||
|
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
|
||||||
|
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
|
||||||
|
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
|
||||||
|
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
|
||||||
|
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
|
||||||
|
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
|
||||||
|
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
|
||||||
|
HEREUNDER.
|
||||||
|
|
||||||
|
Statement of Purpose
|
||||||
|
|
||||||
|
The laws of most jurisdictions throughout the world automatically confer
|
||||||
|
exclusive Copyright and Related Rights (defined below) upon the creator
|
||||||
|
and subsequent owner(s) (each and all, an "owner") of an original work of
|
||||||
|
authorship and/or a database (each, a "Work").
|
||||||
|
|
||||||
|
Certain owners wish to permanently relinquish those rights to a Work for
|
||||||
|
the purpose of contributing to a commons of creative, cultural and
|
||||||
|
scientific works ("Commons") that the public can reliably and without fear
|
||||||
|
of later claims of infringement build upon, modify, incorporate in other
|
||||||
|
works, reuse and redistribute as freely as possible in any form whatsoever
|
||||||
|
and for any purposes, including without limitation commercial purposes.
|
||||||
|
These owners may contribute to the Commons to promote the ideal of a free
|
||||||
|
culture and the further production of creative, cultural and scientific
|
||||||
|
works, or to gain reputation or greater distribution for their Work in
|
||||||
|
part through the use and efforts of others.
|
||||||
|
|
||||||
|
For these and/or other purposes and motivations, and without any
|
||||||
|
expectation of additional consideration or compensation, the person
|
||||||
|
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
|
||||||
|
is an owner of Copyright and Related Rights in the Work, voluntarily
|
||||||
|
elects to apply CC0 to the Work and publicly distribute the Work under its
|
||||||
|
terms, with knowledge of his or her Copyright and Related Rights in the
|
||||||
|
Work and the meaning and intended legal effect of CC0 on those rights.
|
||||||
|
|
||||||
|
1. Copyright and Related Rights. A Work made available under CC0 may be
|
||||||
|
protected by copyright and related or neighboring rights ("Copyright and
|
||||||
|
Related Rights"). Copyright and Related Rights include, but are not
|
||||||
|
limited to, the following:
|
||||||
|
|
||||||
|
i. the right to reproduce, adapt, distribute, perform, display,
|
||||||
|
communicate, and translate a Work;
|
||||||
|
ii. moral rights retained by the original author(s) and/or performer(s);
|
||||||
|
iii. publicity and privacy rights pertaining to a person's image or
|
||||||
|
likeness depicted in a Work;
|
||||||
|
iv. rights protecting against unfair competition in regards to a Work,
|
||||||
|
subject to the limitations in paragraph 4(a), below;
|
||||||
|
v. rights protecting the extraction, dissemination, use and reuse of data
|
||||||
|
in a Work;
|
||||||
|
vi. database rights (such as those arising under Directive 96/9/EC of the
|
||||||
|
European Parliament and of the Council of 11 March 1996 on the legal
|
||||||
|
protection of databases, and under any national implementation
|
||||||
|
thereof, including any amended or successor version of such
|
||||||
|
directive); and
|
||||||
|
vii. other similar, equivalent or corresponding rights throughout the
|
||||||
|
world based on applicable law or treaty, and any national
|
||||||
|
implementations thereof.
|
||||||
|
|
||||||
|
2. Waiver. To the greatest extent permitted by, but not in contravention
|
||||||
|
of, applicable law, Affirmer hereby overtly, fully, permanently,
|
||||||
|
irrevocably and unconditionally waives, abandons, and surrenders all of
|
||||||
|
Affirmer's Copyright and Related Rights and associated claims and causes
|
||||||
|
of action, whether now known or unknown (including existing as well as
|
||||||
|
future claims and causes of action), in the Work (i) in all territories
|
||||||
|
worldwide, (ii) for the maximum duration provided by applicable law or
|
||||||
|
treaty (including future time extensions), (iii) in any current or future
|
||||||
|
medium and for any number of copies, and (iv) for any purpose whatsoever,
|
||||||
|
including without limitation commercial, advertising or promotional
|
||||||
|
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
|
||||||
|
member of the public at large and to the detriment of Affirmer's heirs and
|
||||||
|
successors, fully intending that such Waiver shall not be subject to
|
||||||
|
revocation, rescission, cancellation, termination, or any other legal or
|
||||||
|
equitable action to disrupt the quiet enjoyment of the Work by the public
|
||||||
|
as contemplated by Affirmer's express Statement of Purpose.
|
||||||
|
|
||||||
|
3. Public License Fallback. Should any part of the Waiver for any reason
|
||||||
|
be judged legally invalid or ineffective under applicable law, then the
|
||||||
|
Waiver shall be preserved to the maximum extent permitted taking into
|
||||||
|
account Affirmer's express Statement of Purpose. In addition, to the
|
||||||
|
extent the Waiver is so judged Affirmer hereby grants to each affected
|
||||||
|
person a royalty-free, non transferable, non sublicensable, non exclusive,
|
||||||
|
irrevocable and unconditional license to exercise Affirmer's Copyright and
|
||||||
|
Related Rights in the Work (i) in all territories worldwide, (ii) for the
|
||||||
|
maximum duration provided by applicable law or treaty (including future
|
||||||
|
time extensions), (iii) in any current or future medium and for any number
|
||||||
|
of copies, and (iv) for any purpose whatsoever, including without
|
||||||
|
limitation commercial, advertising or promotional purposes (the
|
||||||
|
"License"). The License shall be deemed effective as of the date CC0 was
|
||||||
|
applied by Affirmer to the Work. Should any part of the License for any
|
||||||
|
reason be judged legally invalid or ineffective under applicable law, such
|
||||||
|
partial invalidity or ineffectiveness shall not invalidate the remainder
|
||||||
|
of the License, and in such case Affirmer hereby affirms that he or she
|
||||||
|
will not (i) exercise any of his or her remaining Copyright and Related
|
||||||
|
Rights in the Work or (ii) assert any associated claims and causes of
|
||||||
|
action with respect to the Work, in either case contrary to Affirmer's
|
||||||
|
express Statement of Purpose.
|
||||||
|
|
||||||
|
4. Limitations and Disclaimers.
|
||||||
|
|
||||||
|
a. No trademark or patent rights held by Affirmer are waived, abandoned,
|
||||||
|
surrendered, licensed or otherwise affected by this document.
|
||||||
|
b. Affirmer offers the Work as-is and makes no representations or
|
||||||
|
warranties of any kind concerning the Work, express, implied,
|
||||||
|
statutory or otherwise, including without limitation warranties of
|
||||||
|
title, merchantability, fitness for a particular purpose, non
|
||||||
|
infringement, or the absence of latent or other defects, accuracy, or
|
||||||
|
the present or absence of errors, whether or not discoverable, all to
|
||||||
|
the greatest extent permissible under applicable law.
|
||||||
|
c. Affirmer disclaims responsibility for clearing rights of other persons
|
||||||
|
that may apply to the Work or any use thereof, including without
|
||||||
|
limitation any person's Copyright and Related Rights in the Work.
|
||||||
|
Further, Affirmer disclaims responsibility for obtaining any necessary
|
||||||
|
consents, permissions or other rights required for any use of the
|
||||||
|
Work.
|
||||||
|
d. Affirmer understands and acknowledges that Creative Commons is not a
|
||||||
|
party to this document and has no duty or obligation with respect to
|
||||||
|
this CC0 or use of the Work.
|
@ -5,14 +5,17 @@ This folder contains example scripts to show you how **Pyrogram** looks like.
|
|||||||
Every script is working right away (provided you correctly set up your credentials), meaning
|
Every script is working right away (provided you correctly set up your credentials), meaning
|
||||||
you can simply copy-paste and run. The only things you have to change are session names and target chats.
|
you can simply copy-paste and run. The only things you have to change are session names and target chats.
|
||||||
|
|
||||||
|
All the examples listed in this directory are licensed under the terms of the [CC0 1.0 Universal](LICENSE) license and
|
||||||
|
can be freely used as basic building blocks for your own applications without worrying about copyrights.
|
||||||
|
|
||||||
Example | Description
|
Example | Description
|
||||||
---: | :---
|
---: | :---
|
||||||
[**hello_world**](hello_world.py) | Demonstration of basic API usages
|
[**hello_world**](hello_world.py) | Demonstration of basic API usages
|
||||||
[**echo_bot**](echo_bot.py) | Echo bot that replies to every private text message
|
[**echo_bot**](echo_bot.py) | Echo bot that replies to every private text message
|
||||||
[**welcome_bot**](welcome_bot.py) | The Welcome Bot source code in [@PyrogramChat](https://t.me/pyrogramchat)
|
[**welcome_bot**](welcome_bot.py) | The Welcome Bot source code in [@PyrogramChat](https://t.me/pyrogramchat)
|
||||||
[**get_history**](get_history.py) | How to retrieve the full message history of a chat
|
[**get_history**](get_history.py) | How to retrieve the full message history of a chat
|
||||||
[**get_participants**](get_participants.py) | How to get the first 10.000 members of a supergroup/channel
|
[**get_chat_members**](get_chat_members.py) | How to get the first 10.000 members of a supergroup/channel
|
||||||
[**get_participants2**](get_participants2.py) | Improved version to get more than 10.000 users
|
[**get_chat_members2**](get_chat_members2.py) | Improved version to get more than 10.000 members
|
||||||
[**query_inline_bots**](query_inline_bots.py) | How to query an inline bot and send a result to a chat
|
[**query_inline_bots**](query_inline_bots.py) | How to query an inline bot and send a result to a chat
|
||||||
[**send_bot_keyboards**](send_bot_keyboards.py) | How to send normal and inline keyboards using regular bots
|
[**send_bot_keyboards**](send_bot_keyboards.py) | How to send normal and inline keyboards using regular bots
|
||||||
[**callback_query_handler**](callback_query_handler.py) | How to handle queries coming from inline button presses
|
[**callback_query_handler**](callback_query_handler.py) | How to handle queries coming from inline button presses
|
||||||
|
@ -1,37 +1,16 @@
|
|||||||
# Pyrogram - Telegram MTProto API Client Library for Python
|
|
||||||
# Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance>
|
|
||||||
#
|
|
||||||
# This file is part of Pyrogram.
|
|
||||||
#
|
|
||||||
# Pyrogram is free software: you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU Lesser General Public License as published
|
|
||||||
# by the Free Software Foundation, either version 3 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# Pyrogram is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU Lesser General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Lesser General Public License
|
|
||||||
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
from pyrogram import Client
|
|
||||||
|
|
||||||
"""This example shows how to handle callback queries, i.e.: queries coming from inline button presses.
|
"""This example shows how to handle callback queries, i.e.: queries coming from inline button presses.
|
||||||
|
|
||||||
It uses the @on_callback_query decorator to register a CallbackQueryHandler."""
|
It uses the @on_callback_query decorator to register a CallbackQueryHandler.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from pyrogram import Client
|
||||||
|
|
||||||
app = Client("123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11")
|
app = Client("123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11")
|
||||||
|
|
||||||
|
|
||||||
@app.on_callback_query()
|
@app.on_callback_query()
|
||||||
def answer(client, callback_query):
|
def answer(client, callback_query):
|
||||||
client.answer_callback_query(
|
callback_query.answer('Button contains: "{}"'.format(callback_query.data), show_alert=True)
|
||||||
callback_query.id,
|
|
||||||
text='Button contains: "{}"'.format(callback_query.data),
|
|
||||||
show_alert=True
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
app.run() # Automatically start() and idle()
|
app.run() # Automatically start() and idle()
|
||||||
|
@ -1,39 +1,17 @@
|
|||||||
# Pyrogram - Telegram MTProto API Client Library for Python
|
|
||||||
# Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance>
|
|
||||||
#
|
|
||||||
# This file is part of Pyrogram.
|
|
||||||
#
|
|
||||||
# Pyrogram is free software: you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU Lesser General Public License as published
|
|
||||||
# by the Free Software Foundation, either version 3 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# Pyrogram is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU Lesser General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Lesser General Public License
|
|
||||||
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
from pyrogram import Client, Filters
|
|
||||||
|
|
||||||
"""This simple echo bot replies to every private text message.
|
"""This simple echo bot replies to every private text message.
|
||||||
|
|
||||||
It uses the @on_message decorator to register a MessageHandler
|
It uses the @on_message decorator to register a MessageHandler and applies two filters on it:
|
||||||
and applies two filters on it, Filters.text and Filters.private to make
|
Filters.text and Filters.private to make sure it will reply to private text messages only.
|
||||||
sure it will only reply to private text messages.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from pyrogram import Client, Filters
|
||||||
|
|
||||||
app = Client("my_account")
|
app = Client("my_account")
|
||||||
|
|
||||||
|
|
||||||
@app.on_message(Filters.text & Filters.private)
|
@app.on_message(Filters.text & Filters.private)
|
||||||
def echo(client, message):
|
def echo(client, message):
|
||||||
client.send_message(
|
message.reply(message.text, quote=True)
|
||||||
message.chat.id, message.text,
|
|
||||||
reply_to_message_id=message.message_id
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
app.run() # Automatically start() and idle()
|
app.run() # Automatically start() and idle()
|
||||||
|
31
examples/get_chat_members.py
Normal file
31
examples/get_chat_members.py
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
"""This example shows you how to get the first 10.000 members of a chat.
|
||||||
|
Refer to get_chat_members2.py for more than 10.000 members.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import time
|
||||||
|
|
||||||
|
from pyrogram import Client
|
||||||
|
from pyrogram.api.errors import FloodWait
|
||||||
|
|
||||||
|
app = Client("my_account")
|
||||||
|
|
||||||
|
target = "pyrogramchat" # Target channel/supergroup
|
||||||
|
members = [] # List that will contain all the members of the target chat
|
||||||
|
offset = 0 # Offset starts at 0
|
||||||
|
limit = 200 # Amount of users to retrieve for each API call (max 200)
|
||||||
|
|
||||||
|
with app:
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
chunk = app.get_chat_members(target, offset)
|
||||||
|
except FloodWait as e: # Very large chats could trigger FloodWait
|
||||||
|
time.sleep(e.x) # When it happens, wait X seconds and try again
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not chunk.chat_members:
|
||||||
|
break # No more members left
|
||||||
|
|
||||||
|
members.extend(chunk.chat_members)
|
||||||
|
offset += len(chunk.chat_members)
|
||||||
|
|
||||||
|
# Now the "members" list contains all the members of the target chat
|
50
examples/get_chat_members2.py
Normal file
50
examples/get_chat_members2.py
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
"""This is an improved version of get_chat_members.py
|
||||||
|
|
||||||
|
Since Telegram will return at most 10.000 members for a single query, this script
|
||||||
|
repeats the search using numbers ("0" to "9") and all the available ascii letters ("a" to "z").
|
||||||
|
|
||||||
|
This can be further improved by also searching for non-ascii characters (e.g.: Japanese script),
|
||||||
|
as some user names may not contain ascii letters at all.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import time
|
||||||
|
from string import ascii_lowercase
|
||||||
|
|
||||||
|
from pyrogram import Client
|
||||||
|
from pyrogram.api.errors import FloodWait
|
||||||
|
|
||||||
|
app = Client("my_account")
|
||||||
|
|
||||||
|
target = "pyrogramchat" # Target channel/supergroup
|
||||||
|
members = {} # List that will contain all the members of the target chat
|
||||||
|
limit = 200 # Amount of users to retrieve for each API call (max 200)
|
||||||
|
|
||||||
|
# "" + "0123456789" + "abcdefghijklmnopqrstuvwxyz" (as list)
|
||||||
|
queries = [""] + [str(i) for i in range(10)] + list(ascii_lowercase)
|
||||||
|
|
||||||
|
with app:
|
||||||
|
for q in queries:
|
||||||
|
print('Searching for "{}"'.format(q))
|
||||||
|
offset = 0 # For each query, offset restarts from 0
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
chunk = app.get_chat_members(target, offset, query=q)
|
||||||
|
except FloodWait as e: # Very large chats could trigger FloodWait
|
||||||
|
print("Flood wait: {} seconds".format(e.x))
|
||||||
|
time.sleep(e.x) # When it happens, wait X seconds and try again
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not chunk.chat_members:
|
||||||
|
print('Done searching for "{}"'.format(q))
|
||||||
|
print()
|
||||||
|
break # No more members left
|
||||||
|
|
||||||
|
members.update({i.user.id: i for i in chunk.chat_members})
|
||||||
|
offset += len(chunk.chat_members)
|
||||||
|
|
||||||
|
print("Total members: {}".format(len(members)))
|
||||||
|
|
||||||
|
print("Grand total: {}".format(len(members)))
|
||||||
|
|
||||||
|
# Now the "members" list contains all the members of the target chat
|
@ -1,53 +1,31 @@
|
|||||||
# Pyrogram - Telegram MTProto API Client Library for Python
|
"""This example shows how to retrieve the full message history of a chat"""
|
||||||
# Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance>
|
|
||||||
#
|
|
||||||
# This file is part of Pyrogram.
|
|
||||||
#
|
|
||||||
# Pyrogram is free software: you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU Lesser General Public License as published
|
|
||||||
# by the Free Software Foundation, either version 3 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# Pyrogram is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU Lesser General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Lesser General Public License
|
|
||||||
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from pyrogram import Client
|
from pyrogram import Client
|
||||||
from pyrogram.api.errors import FloodWait
|
from pyrogram.api.errors import FloodWait
|
||||||
|
|
||||||
"""This example shows how to retrieve the full message history of a chat"""
|
|
||||||
|
|
||||||
app = Client("my_account")
|
app = Client("my_account")
|
||||||
target = "me" # "me" refers to your own chat (Saved Messages)
|
target = "me" # "me" refers to your own chat (Saved Messages)
|
||||||
messages = [] # List that will contain all the messages of the target chat
|
messages = [] # List that will contain all the messages of the target chat
|
||||||
offset_id = 0 # ID of the last message of the chunk
|
offset_id = 0 # ID of the last message of the chunk
|
||||||
|
|
||||||
app.start()
|
with app:
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
m = app.get_history(target, offset_id=offset_id)
|
||||||
|
except FloodWait as e: # For very large chats the method call can raise a FloodWait
|
||||||
|
print("waiting {}".format(e.x))
|
||||||
|
time.sleep(e.x) # Sleep X seconds before continuing
|
||||||
|
continue
|
||||||
|
|
||||||
while True:
|
if not m.messages:
|
||||||
try:
|
break
|
||||||
m = app.get_history(target, offset_id=offset_id)
|
|
||||||
except FloodWait as e:
|
|
||||||
# For very large chats the method call can raise a FloodWait
|
|
||||||
print("waiting {}".format(e.x))
|
|
||||||
time.sleep(e.x) # Sleep X seconds before continuing
|
|
||||||
continue
|
|
||||||
|
|
||||||
if not m.messages:
|
messages += m.messages
|
||||||
break
|
offset_id = m.messages[-1].message_id
|
||||||
|
|
||||||
messages += m.messages
|
print("Messages: {}".format(len(messages)))
|
||||||
offset_id = m.messages[-1].message_id
|
|
||||||
|
|
||||||
print("Messages: {}".format(len(messages)))
|
|
||||||
|
|
||||||
app.stop()
|
|
||||||
|
|
||||||
# Now the "messages" list contains all the messages sorted by date in
|
# Now the "messages" list contains all the messages sorted by date in
|
||||||
# descending order (from the most recent to the oldest one)
|
# descending order (from the most recent to the oldest one)
|
||||||
|
@ -1,63 +0,0 @@
|
|||||||
# Pyrogram - Telegram MTProto API Client Library for Python
|
|
||||||
# Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance>
|
|
||||||
#
|
|
||||||
# This file is part of Pyrogram.
|
|
||||||
#
|
|
||||||
# Pyrogram is free software: you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU Lesser General Public License as published
|
|
||||||
# by the Free Software Foundation, either version 3 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# Pyrogram is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU Lesser General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Lesser General Public License
|
|
||||||
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
import time
|
|
||||||
|
|
||||||
from pyrogram import Client
|
|
||||||
from pyrogram.api import functions, types
|
|
||||||
from pyrogram.api.errors import FloodWait
|
|
||||||
|
|
||||||
"""This simple GetParticipants method usage shows you how to get the first 10.000 users of a chat.
|
|
||||||
|
|
||||||
Refer to get_participants2.py for more than 10.000 users.
|
|
||||||
"""
|
|
||||||
|
|
||||||
app = Client("my_account")
|
|
||||||
target = "pyrogramchat" # Target channel/supergroup
|
|
||||||
users = [] # List that will contain all the users of the target chat
|
|
||||||
limit = 200 # Amount of users to retrieve for each API call
|
|
||||||
offset = 0 # Offset starts at 0
|
|
||||||
|
|
||||||
app.start()
|
|
||||||
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
participants = app.send(
|
|
||||||
functions.channels.GetParticipants(
|
|
||||||
channel=app.resolve_peer(target),
|
|
||||||
filter=types.ChannelParticipantsSearch(""), # Filter by empty string (search for all)
|
|
||||||
offset=offset,
|
|
||||||
limit=limit,
|
|
||||||
hash=0
|
|
||||||
)
|
|
||||||
)
|
|
||||||
except FloodWait as e:
|
|
||||||
# Very large channels will trigger FloodWait.
|
|
||||||
# When happens, wait X seconds before continuing
|
|
||||||
time.sleep(e.x)
|
|
||||||
continue
|
|
||||||
|
|
||||||
if not participants.participants:
|
|
||||||
break # No more participants left
|
|
||||||
|
|
||||||
users.extend(participants.users)
|
|
||||||
offset += limit
|
|
||||||
|
|
||||||
app.stop()
|
|
||||||
|
|
||||||
# Now the "users" list contains all the members of the target chat
|
|
@ -1,79 +0,0 @@
|
|||||||
# Pyrogram - Telegram MTProto API Client Library for Python
|
|
||||||
# Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance>
|
|
||||||
#
|
|
||||||
# This file is part of Pyrogram.
|
|
||||||
#
|
|
||||||
# Pyrogram is free software: you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU Lesser General Public License as published
|
|
||||||
# by the Free Software Foundation, either version 3 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# Pyrogram is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU Lesser General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Lesser General Public License
|
|
||||||
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
import time
|
|
||||||
from string import ascii_lowercase
|
|
||||||
|
|
||||||
from pyrogram import Client
|
|
||||||
from pyrogram.api import functions, types
|
|
||||||
from pyrogram.api.errors import FloodWait
|
|
||||||
|
|
||||||
"""This is an improved version of get_participants.py
|
|
||||||
|
|
||||||
Since Telegram will return at most 10.000 users for a single query, this script
|
|
||||||
repeats the search using numbers ("0" to "9") and all the available ascii letters ("a" to "z").
|
|
||||||
|
|
||||||
This can be further improved by also searching for non-ascii characters (e.g.: Japanese script),
|
|
||||||
as some user names may not contain ascii letters at all.
|
|
||||||
"""
|
|
||||||
|
|
||||||
app = Client("my_account")
|
|
||||||
target = "pyrogramchat" # Target channel/supergroup username or id
|
|
||||||
users = {} # To ensure uniqueness, users will be stored in a dictionary with user_id as key
|
|
||||||
limit = 200 # Amount of users to retrieve for each API call (200 is the maximum)
|
|
||||||
# "" + "0123456789" + "abcdefghijklmnopqrstuvwxyz" (as list)
|
|
||||||
queries = [""] + [str(i) for i in range(10)] + list(ascii_lowercase)
|
|
||||||
|
|
||||||
app.start()
|
|
||||||
|
|
||||||
for q in queries:
|
|
||||||
print("Searching for '{}'".format(q))
|
|
||||||
offset = 0 # For each query, offset restarts from 0
|
|
||||||
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
participants = app.send(
|
|
||||||
functions.channels.GetParticipants(
|
|
||||||
channel=app.resolve_peer(target),
|
|
||||||
filter=types.ChannelParticipantsSearch(q),
|
|
||||||
offset=offset,
|
|
||||||
limit=limit,
|
|
||||||
hash=0
|
|
||||||
)
|
|
||||||
)
|
|
||||||
except FloodWait as e:
|
|
||||||
# Very large chats could trigger FloodWait.
|
|
||||||
# When happens, wait X seconds before continuing
|
|
||||||
print("Flood wait: {} seconds".format(e.x))
|
|
||||||
time.sleep(e.x)
|
|
||||||
continue
|
|
||||||
|
|
||||||
if not participants.participants:
|
|
||||||
print("Done searching for '{}'".format(q))
|
|
||||||
print()
|
|
||||||
break # No more participants left
|
|
||||||
|
|
||||||
# User information are stored in the participants.users list.
|
|
||||||
# Add those users to the dictionary
|
|
||||||
users.update({i.id: i for i in participants.users})
|
|
||||||
|
|
||||||
offset += len(participants.participants)
|
|
||||||
|
|
||||||
print("Total users: {}".format(len(users)))
|
|
||||||
|
|
||||||
app.stop()
|
|
@ -1,25 +1,7 @@
|
|||||||
# Pyrogram - Telegram MTProto API Client Library for Python
|
"""This example demonstrates a basic API usage"""
|
||||||
# Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance>
|
|
||||||
#
|
|
||||||
# This file is part of Pyrogram.
|
|
||||||
#
|
|
||||||
# Pyrogram is free software: you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU Lesser General Public License as published
|
|
||||||
# by the Free Software Foundation, either version 3 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# Pyrogram is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU Lesser General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Lesser General Public License
|
|
||||||
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
from pyrogram import Client
|
from pyrogram import Client
|
||||||
|
|
||||||
"""This example demonstrates a basic API usage"""
|
|
||||||
|
|
||||||
# Create a new Client instance
|
# Create a new Client instance
|
||||||
app = Client("my_account")
|
app = Client("my_account")
|
||||||
|
|
||||||
|
@ -1,25 +1,7 @@
|
|||||||
# Pyrogram - Telegram MTProto API Client Library for Python
|
"""This example shows how to query an inline bot"""
|
||||||
# Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance>
|
|
||||||
#
|
|
||||||
# This file is part of Pyrogram.
|
|
||||||
#
|
|
||||||
# Pyrogram is free software: you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU Lesser General Public License as published
|
|
||||||
# by the Free Software Foundation, either version 3 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# Pyrogram is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU Lesser General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Lesser General Public License
|
|
||||||
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
from pyrogram import Client
|
from pyrogram import Client
|
||||||
|
|
||||||
"""This example shows how to query an inline bot"""
|
|
||||||
|
|
||||||
# Create a new Client
|
# Create a new Client
|
||||||
app = Client("my_account")
|
app = Client("my_account")
|
||||||
|
|
||||||
|
@ -1,25 +1,7 @@
|
|||||||
# Pyrogram - Telegram MTProto API Client Library for Python
|
"""This example shows how to handle raw updates"""
|
||||||
# Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance>
|
|
||||||
#
|
|
||||||
# This file is part of Pyrogram.
|
|
||||||
#
|
|
||||||
# Pyrogram is free software: you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU Lesser General Public License as published
|
|
||||||
# by the Free Software Foundation, either version 3 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# Pyrogram is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU Lesser General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Lesser General Public License
|
|
||||||
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
from pyrogram import Client
|
from pyrogram import Client
|
||||||
|
|
||||||
"""This example shows how to handle raw updates"""
|
|
||||||
|
|
||||||
app = Client("my_account")
|
app = Client("my_account")
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,23 +1,3 @@
|
|||||||
# Pyrogram - Telegram MTProto API Client Library for Python
|
|
||||||
# Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance>
|
|
||||||
#
|
|
||||||
# This file is part of Pyrogram.
|
|
||||||
#
|
|
||||||
# Pyrogram is free software: you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU Lesser General Public License as published
|
|
||||||
# by the Free Software Foundation, either version 3 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# Pyrogram is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU Lesser General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Lesser General Public License
|
|
||||||
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
from pyrogram import Client, ReplyKeyboardMarkup, InlineKeyboardMarkup, InlineKeyboardButton
|
|
||||||
|
|
||||||
"""This example will show you how to send normal and inline keyboards.
|
"""This example will show you how to send normal and inline keyboards.
|
||||||
|
|
||||||
You must log-in as a regular bot in order to send keyboards (use the token from @BotFather).
|
You must log-in as a regular bot in order to send keyboards (use the token from @BotFather).
|
||||||
@ -27,6 +7,8 @@ send_message() is used as example, but a keyboard can be sent with any other sen
|
|||||||
like send_audio(), send_document(), send_location(), etc...
|
like send_audio(), send_document(), send_location(), etc...
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
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("123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11")
|
||||||
app.start()
|
app.start()
|
||||||
|
@ -1,52 +1,45 @@
|
|||||||
# Pyrogram - Telegram MTProto API Client Library for Python
|
|
||||||
# Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance>
|
|
||||||
#
|
|
||||||
# This file is part of Pyrogram.
|
|
||||||
#
|
|
||||||
# Pyrogram is free software: you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU Lesser General Public License as published
|
|
||||||
# by the Free Software Foundation, either version 3 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# Pyrogram is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU Lesser General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Lesser General Public License
|
|
||||||
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
from pyrogram import Client, Emoji, Filters
|
|
||||||
|
|
||||||
"""This is the Welcome Bot in @PyrogramChat.
|
"""This is the Welcome Bot in @PyrogramChat.
|
||||||
|
|
||||||
It uses the Emoji module to easily add emojis in your text messages and Filters
|
It uses the Emoji module to easily add emojis in your text messages and Filters
|
||||||
to make it only work for specific messages in a specific chat
|
to make it only work for specific messages in a specific chat.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from pyrogram import Client, Emoji, Filters
|
||||||
|
|
||||||
|
USER = "**{}**"
|
||||||
|
MESSAGE = "{} Welcome to [Pyrogram](https://docs.pyrogram.ml/)'s group chat {{}}!".format(Emoji.SPARKLES)
|
||||||
|
|
||||||
|
enabled_groups = Filters.chat("PyrogramChat")
|
||||||
|
last_welcomes = {}
|
||||||
|
|
||||||
app = Client("my_account")
|
app = Client("my_account")
|
||||||
|
|
||||||
|
|
||||||
@app.on_message(Filters.chat("PyrogramChat") & Filters.new_chat_members)
|
@app.on_message(enabled_groups & Filters.new_chat_members)
|
||||||
def welcome(client, message):
|
def welcome(client, message):
|
||||||
# Build the new members list (with mentions) by using their first_name
|
chat_id = message.chat.id
|
||||||
new_members = ", ".join([
|
|
||||||
"[{}](tg://user?id={})".format(i.first_name, i.id)
|
|
||||||
for i in message.new_chat_members
|
|
||||||
])
|
|
||||||
|
|
||||||
# Build the welcome message by using an emoji and the list we built above
|
# Get the previous welcome message and members, if any
|
||||||
text = "{} Welcome to [Pyrogram](https://docs.pyrogram.ml/)'s group chat {}!".format(
|
previous_welcome, previous_members = last_welcomes.pop(chat_id, (None, []))
|
||||||
Emoji.SPARKLES,
|
|
||||||
new_members
|
|
||||||
)
|
|
||||||
|
|
||||||
# Send the welcome message
|
# Delete the previous message, if exists
|
||||||
client.send_message(
|
if previous_welcome:
|
||||||
message.chat.id, text,
|
previous_welcome.delete()
|
||||||
reply_to_message_id=message.message_id,
|
|
||||||
disable_web_page_preview=True
|
# Build the new members list by using their first_name. Also append the previous members, if any
|
||||||
)
|
new_members = [USER.format(i.first_name) for i in message.new_chat_members] + previous_members
|
||||||
|
|
||||||
|
# Build the welcome message by using an emoji and the list we created above
|
||||||
|
text = MESSAGE.format(", ".join(new_members))
|
||||||
|
|
||||||
|
# Actually send the welcome and save the new message and the new members list
|
||||||
|
last_welcomes[message.chat.id] = message.reply(text, disable_web_page_preview=True), new_members
|
||||||
|
|
||||||
|
|
||||||
|
@app.on_message(enabled_groups)
|
||||||
|
def reset(client, message):
|
||||||
|
# Don't make the bot delete the previous welcome in case someone talks in the middle
|
||||||
|
last_welcomes.pop(message.chat.id, None)
|
||||||
|
|
||||||
|
|
||||||
app.run() # Automatically start() and idle()
|
app.run() # Automatically start() and idle()
|
||||||
|
@ -23,21 +23,19 @@ __copyright__ = "Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance
|
|||||||
"e" if sys.getfilesystemencoding() != "utf-8" else "\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.7.5"
|
__version__ = "0.9.4.future"
|
||||||
|
|
||||||
from .api.errors import Error
|
from .api.errors import Error
|
||||||
from .client.types import (
|
from .client.types import (
|
||||||
Audio, Chat, ChatMember, ChatPhoto, Contact, Document, InputMediaPhoto,
|
Audio, Chat, ChatMember, ChatMembers, ChatPhoto, Contact, Document, InputMediaPhoto,
|
||||||
InputMediaVideo, InputPhoneContact, Location, Message, MessageEntity,
|
InputMediaVideo, InputMediaDocument, InputMediaAudio, InputMediaAnimation, InputPhoneContact,
|
||||||
Photo, PhotoSize, Sticker, Update, User, UserProfilePhotos, Venue, GIF,
|
Location, Message, MessageEntity, Dialog, Dialogs, Photo, PhotoSize, Sticker, User, UserStatus,
|
||||||
Video, VideoNote, Voice, CallbackQuery, Messages
|
UserProfilePhotos, Venue, Animation, Video, VideoNote, Voice, CallbackQuery, Messages, ForceReply,
|
||||||
)
|
InlineKeyboardButton, InlineKeyboardMarkup, KeyboardButton, ReplyKeyboardMarkup, ReplyKeyboardRemove,
|
||||||
from .client.types.reply_markup import (
|
Poll, PollOption
|
||||||
ForceReply, InlineKeyboardButton, InlineKeyboardMarkup,
|
|
||||||
KeyboardButton, ReplyKeyboardMarkup, ReplyKeyboardRemove
|
|
||||||
)
|
)
|
||||||
from .client import (
|
from .client import (
|
||||||
Client, ChatAction, ParseMode, Emoji,
|
Client, ChatAction, ParseMode, Emoji,
|
||||||
MessageHandler, DeletedMessagesHandler, CallbackQueryHandler,
|
MessageHandler, DeletedMessagesHandler, CallbackQueryHandler,
|
||||||
RawUpdateHandler, DisconnectHandler, Filters
|
RawUpdateHandler, DisconnectHandler, UserStatusHandler, Filters
|
||||||
)
|
)
|
||||||
|
@ -30,7 +30,7 @@ class BoolFalse(Object):
|
|||||||
return cls.value
|
return cls.value
|
||||||
|
|
||||||
def __new__(cls) -> bytes:
|
def __new__(cls) -> bytes:
|
||||||
return int.to_bytes(cls.ID, 4, "little")
|
return cls.ID.to_bytes(4, "little")
|
||||||
|
|
||||||
|
|
||||||
class BoolTrue(BoolFalse):
|
class BoolTrue(BoolFalse):
|
||||||
|
@ -48,7 +48,7 @@ class Bytes(Object):
|
|||||||
else:
|
else:
|
||||||
return (
|
return (
|
||||||
bytes([254])
|
bytes([254])
|
||||||
+ int.to_bytes(length, 3, "little")
|
+ length.to_bytes(3, "little")
|
||||||
+ value
|
+ value
|
||||||
+ bytes(-length % 4)
|
+ bytes(-length % 4)
|
||||||
)
|
)
|
||||||
|
@ -29,15 +29,12 @@ class Int(Object):
|
|||||||
return int.from_bytes(b.read(cls.SIZE), "little", signed=signed)
|
return int.from_bytes(b.read(cls.SIZE), "little", signed=signed)
|
||||||
|
|
||||||
def __new__(cls, value: int, signed: bool = True) -> bytes:
|
def __new__(cls, value: int, signed: bool = True) -> bytes:
|
||||||
return int.to_bytes(value, cls.SIZE, "little", signed=signed)
|
return value.to_bytes(cls.SIZE, "little", signed=signed)
|
||||||
|
|
||||||
|
|
||||||
class Long(Int):
|
class Long(Int):
|
||||||
SIZE = 8
|
SIZE = 8
|
||||||
|
|
||||||
def __new__(cls, *args):
|
|
||||||
return super().__new__(cls, *args)
|
|
||||||
|
|
||||||
|
|
||||||
class Int128(Int):
|
class Int128(Int):
|
||||||
SIZE = 16
|
SIZE = 16
|
||||||
|
@ -29,4 +29,4 @@ class Null(Object):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def __new__(cls) -> bytes:
|
def __new__(cls) -> bytes:
|
||||||
return int.to_bytes(cls.ID, 4, "little")
|
return cls.ID.to_bytes(4, "little")
|
||||||
|
@ -24,7 +24,7 @@ from . import Bytes
|
|||||||
class String(Bytes):
|
class String(Bytes):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def read(b: BytesIO, *args) -> str:
|
def read(b: BytesIO, *args) -> str:
|
||||||
return super(String, String).read(b).decode()
|
return super(String, String).read(b).decode(errors="replace")
|
||||||
|
|
||||||
def __new__(cls, value: str) -> bytes:
|
def __new__(cls, value: str) -> bytes:
|
||||||
return super().__new__(cls, value.encode())
|
return super().__new__(cls, value.encode())
|
||||||
|
@ -22,5 +22,5 @@ from .filters import Filters
|
|||||||
from .handlers import (
|
from .handlers import (
|
||||||
MessageHandler, DeletedMessagesHandler,
|
MessageHandler, DeletedMessagesHandler,
|
||||||
CallbackQueryHandler, RawUpdateHandler,
|
CallbackQueryHandler, RawUpdateHandler,
|
||||||
DisconnectHandler
|
DisconnectHandler, UserStatusHandler
|
||||||
)
|
)
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
|
|
||||||
import base64
|
import base64
|
||||||
import binascii
|
import binascii
|
||||||
import getpass
|
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import math
|
import math
|
||||||
@ -33,8 +32,11 @@ import time
|
|||||||
from configparser import ConfigParser
|
from configparser import ConfigParser
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from hashlib import sha256, md5
|
from hashlib import sha256, md5
|
||||||
|
from importlib import import_module
|
||||||
|
from pathlib import Path
|
||||||
from signal import signal, SIGINT, SIGTERM, SIGABRT
|
from signal import signal, SIGINT, SIGTERM, SIGABRT
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
from typing import Union, List
|
||||||
|
|
||||||
from pyrogram.api import functions, types
|
from pyrogram.api import functions, types
|
||||||
from pyrogram.api.core import Object
|
from pyrogram.api.core import Object
|
||||||
@ -43,8 +45,12 @@ from pyrogram.api.errors import (
|
|||||||
PhoneNumberUnoccupied, PhoneCodeInvalid, PhoneCodeHashEmpty,
|
PhoneNumberUnoccupied, PhoneCodeInvalid, PhoneCodeHashEmpty,
|
||||||
PhoneCodeExpired, PhoneCodeEmpty, SessionPasswordNeeded,
|
PhoneCodeExpired, PhoneCodeEmpty, SessionPasswordNeeded,
|
||||||
PasswordHashInvalid, FloodWait, PeerIdInvalid, FirstnameInvalid, PhoneNumberBanned,
|
PasswordHashInvalid, FloodWait, PeerIdInvalid, FirstnameInvalid, PhoneNumberBanned,
|
||||||
VolumeLocNotFound, UserMigrate, FileIdInvalid)
|
VolumeLocNotFound, UserMigrate, FileIdInvalid, ChannelPrivate, PhoneNumberOccupied,
|
||||||
|
PasswordRecoveryNa, PasswordEmpty
|
||||||
|
)
|
||||||
from pyrogram.client.handlers import DisconnectHandler
|
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.crypto import AES
|
||||||
from pyrogram.session import Auth, Session
|
from pyrogram.session import Auth, Session
|
||||||
from .dispatcher import Dispatcher
|
from .dispatcher import Dispatcher
|
||||||
@ -90,6 +96,10 @@ class Client(Methods, BaseClient):
|
|||||||
Code of the language used on the client, in ISO 639-1 standard. Defaults to "en".
|
Code of the language used on the client, in ISO 639-1 standard. Defaults to "en".
|
||||||
This is an alternative way to set it if you don't want to use the *config.ini* file.
|
This is an alternative way to set it if you don't want to use the *config.ini* file.
|
||||||
|
|
||||||
|
ipv6 (``bool``, *optional*):
|
||||||
|
Pass True to connect to Telegram using IPv6.
|
||||||
|
Defaults to False (IPv4).
|
||||||
|
|
||||||
proxy (``dict``, *optional*):
|
proxy (``dict``, *optional*):
|
||||||
Your SOCKS5 Proxy settings as dict,
|
Your SOCKS5 Proxy settings as dict,
|
||||||
e.g.: *dict(hostname="11.22.33.44", port=1080, username="user", password="pass")*.
|
e.g.: *dict(hostname="11.22.33.44", port=1080, username="user", password="pass")*.
|
||||||
@ -136,27 +146,34 @@ class Client(Methods, BaseClient):
|
|||||||
|
|
||||||
config_file (``str``, *optional*):
|
config_file (``str``, *optional*):
|
||||||
Path of the configuration file. Defaults to ./config.ini
|
Path of the configuration file. Defaults to ./config.ini
|
||||||
|
|
||||||
|
plugins_dir (``str``, *optional*):
|
||||||
|
Define a custom directory for your plugins. The plugins directory is the location in your
|
||||||
|
filesystem where Pyrogram will automatically load your update handlers.
|
||||||
|
Defaults to None (plugins disabled).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
session_name: str,
|
session_name: str,
|
||||||
api_id: int or str = None,
|
api_id: Union[int, str] = None,
|
||||||
api_hash: str = None,
|
api_hash: str = None,
|
||||||
app_version: str = None,
|
app_version: str = None,
|
||||||
device_model: str = None,
|
device_model: str = None,
|
||||||
system_version: str = None,
|
system_version: str = None,
|
||||||
lang_code: str = None,
|
lang_code: str = None,
|
||||||
|
ipv6: bool = False,
|
||||||
proxy: dict = None,
|
proxy: dict = None,
|
||||||
test_mode: bool = False,
|
test_mode: bool = False,
|
||||||
phone_number: str = None,
|
phone_number: str = None,
|
||||||
phone_code: str or callable = None,
|
phone_code: Union[str, callable] = None,
|
||||||
password: str = None,
|
password: str = None,
|
||||||
force_sms: bool = False,
|
force_sms: bool = False,
|
||||||
first_name: str = None,
|
first_name: str = None,
|
||||||
last_name: str = None,
|
last_name: str = None,
|
||||||
workers: int = 4,
|
workers: int = BaseClient.WORKERS,
|
||||||
workdir: str = ".",
|
workdir: str = BaseClient.WORKDIR,
|
||||||
config_file: str = "./config.ini"):
|
config_file: str = BaseClient.CONFIG_FILE,
|
||||||
|
plugins_dir: str = None):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
self.session_name = session_name
|
self.session_name = session_name
|
||||||
@ -166,6 +183,7 @@ class Client(Methods, BaseClient):
|
|||||||
self.device_model = device_model
|
self.device_model = device_model
|
||||||
self.system_version = system_version
|
self.system_version = system_version
|
||||||
self.lang_code = lang_code
|
self.lang_code = lang_code
|
||||||
|
self.ipv6 = ipv6
|
||||||
# TODO: Make code consistent, use underscore for private/protected fields
|
# TODO: Make code consistent, use underscore for private/protected fields
|
||||||
self._proxy = proxy
|
self._proxy = proxy
|
||||||
self.test_mode = test_mode
|
self.test_mode = test_mode
|
||||||
@ -178,9 +196,16 @@ class Client(Methods, BaseClient):
|
|||||||
self.workers = workers
|
self.workers = workers
|
||||||
self.workdir = workdir
|
self.workdir = workdir
|
||||||
self.config_file = config_file
|
self.config_file = config_file
|
||||||
|
self.plugins_dir = plugins_dir
|
||||||
|
|
||||||
self.dispatcher = Dispatcher(self, workers)
|
self.dispatcher = Dispatcher(self, workers)
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
return self.start()
|
||||||
|
|
||||||
|
def __exit__(self, *args):
|
||||||
|
self.stop()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def proxy(self):
|
def proxy(self):
|
||||||
return self._proxy
|
return self._proxy
|
||||||
@ -195,17 +220,19 @@ class Client(Methods, BaseClient):
|
|||||||
Requires no parameters.
|
Requires no parameters.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
:class:`Error <pyrogram.Error>`
|
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
|
||||||
|
``ConnectionError`` in case you try to start an already started Client.
|
||||||
"""
|
"""
|
||||||
if self.is_started:
|
if self.is_started:
|
||||||
raise ConnectionError("Client has already been started")
|
raise ConnectionError("Client has already been started")
|
||||||
|
|
||||||
if self.BOT_TOKEN_RE.match(self.session_name):
|
if self.BOT_TOKEN_RE.match(self.session_name):
|
||||||
self.token = self.session_name
|
self.bot_token = self.session_name
|
||||||
self.session_name = self.session_name.split(":")[0]
|
self.session_name = self.session_name.split(":")[0]
|
||||||
|
|
||||||
self.load_config()
|
self.load_config()
|
||||||
self.load_session()
|
self.load_session()
|
||||||
|
self.load_plugins()
|
||||||
|
|
||||||
self.session = Session(
|
self.session = Session(
|
||||||
self,
|
self,
|
||||||
@ -216,28 +243,33 @@ class Client(Methods, BaseClient):
|
|||||||
self.session.start()
|
self.session.start()
|
||||||
self.is_started = True
|
self.is_started = True
|
||||||
|
|
||||||
if self.user_id is None:
|
try:
|
||||||
if self.token is None:
|
if self.user_id is None:
|
||||||
self.authorize_user()
|
if self.bot_token is None:
|
||||||
|
self.authorize_user()
|
||||||
|
else:
|
||||||
|
self.authorize_bot()
|
||||||
|
|
||||||
|
self.save_session()
|
||||||
|
|
||||||
|
if self.bot_token is None:
|
||||||
|
now = time.time()
|
||||||
|
|
||||||
|
if abs(now - self.date) > Client.OFFLINE_SLEEP:
|
||||||
|
self.peers_by_username = {}
|
||||||
|
self.peers_by_phone = {}
|
||||||
|
|
||||||
|
self.get_initial_dialogs()
|
||||||
|
self.get_contacts()
|
||||||
|
else:
|
||||||
|
self.send(functions.messages.GetPinnedDialogs())
|
||||||
|
self.get_initial_dialogs_chunk()
|
||||||
else:
|
else:
|
||||||
self.authorize_bot()
|
self.send(functions.updates.GetState())
|
||||||
|
except Exception as e:
|
||||||
self.save_session()
|
self.is_started = False
|
||||||
|
self.session.stop()
|
||||||
if self.token is None:
|
raise e
|
||||||
now = time.time()
|
|
||||||
|
|
||||||
if abs(now - self.date) > Client.OFFLINE_SLEEP:
|
|
||||||
self.peers_by_username = {}
|
|
||||||
self.peers_by_phone = {}
|
|
||||||
|
|
||||||
self.get_dialogs()
|
|
||||||
self.get_contacts()
|
|
||||||
else:
|
|
||||||
self.send(functions.messages.GetPinnedDialogs())
|
|
||||||
self.get_dialogs_chunk(0)
|
|
||||||
else:
|
|
||||||
self.send(functions.updates.GetState())
|
|
||||||
|
|
||||||
for i in range(self.UPDATES_WORKERS):
|
for i in range(self.UPDATES_WORKERS):
|
||||||
self.updates_workers_list.append(
|
self.updates_workers_list.append(
|
||||||
@ -264,9 +296,14 @@ class Client(Methods, BaseClient):
|
|||||||
mimetypes.init()
|
mimetypes.init()
|
||||||
Syncer.add(self)
|
Syncer.add(self)
|
||||||
|
|
||||||
|
return self
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
"""Use this method to manually stop the Client.
|
"""Use this method to manually stop the Client.
|
||||||
Requires no parameters.
|
Requires no parameters.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
``ConnectionError`` in case you try to stop an already stopped Client.
|
||||||
"""
|
"""
|
||||||
if not self.is_started:
|
if not self.is_started:
|
||||||
raise ConnectionError("Client is already stopped")
|
raise ConnectionError("Client is already stopped")
|
||||||
@ -298,6 +335,8 @@ class Client(Methods, BaseClient):
|
|||||||
self.is_started = False
|
self.is_started = False
|
||||||
self.session.stop()
|
self.session.stop()
|
||||||
|
|
||||||
|
return self
|
||||||
|
|
||||||
def idle(self, stop_signals: tuple = (SIGINT, SIGTERM, SIGABRT)):
|
def idle(self, stop_signals: tuple = (SIGINT, SIGTERM, SIGABRT)):
|
||||||
"""Blocks the program execution until one of the signals are received,
|
"""Blocks the program execution until one of the signals are received,
|
||||||
then gently stop the Client by closing the underlying connection.
|
then gently stop the Client by closing the underlying connection.
|
||||||
@ -326,12 +365,12 @@ class Client(Methods, BaseClient):
|
|||||||
Requires no parameters.
|
Requires no parameters.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
:class:`Error <pyrogram.Error>`
|
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
|
||||||
"""
|
"""
|
||||||
self.start()
|
self.start()
|
||||||
self.idle()
|
self.idle()
|
||||||
|
|
||||||
def add_handler(self, handler, group: int = 0):
|
def add_handler(self, handler: Handler, group: int = 0):
|
||||||
"""Use this method to register an update handler.
|
"""Use this method to register an update handler.
|
||||||
|
|
||||||
You can register multiple handlers, but at most one handler within a group
|
You can register multiple handlers, but at most one handler within a group
|
||||||
@ -355,7 +394,7 @@ class Client(Methods, BaseClient):
|
|||||||
|
|
||||||
return handler, group
|
return handler, group
|
||||||
|
|
||||||
def remove_handler(self, handler, group: int = 0):
|
def remove_handler(self, handler: Handler, group: int = 0):
|
||||||
"""Removes a previously-added update handler.
|
"""Removes a previously-added update handler.
|
||||||
|
|
||||||
Make sure to provide the right group that the handler was added in. You can use
|
Make sure to provide the right group that the handler was added in. You can use
|
||||||
@ -381,14 +420,14 @@ class Client(Methods, BaseClient):
|
|||||||
flags=0,
|
flags=0,
|
||||||
api_id=self.api_id,
|
api_id=self.api_id,
|
||||||
api_hash=self.api_hash,
|
api_hash=self.api_hash,
|
||||||
bot_auth_token=self.token
|
bot_auth_token=self.bot_token
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
except UserMigrate as e:
|
except UserMigrate as e:
|
||||||
self.session.stop()
|
self.session.stop()
|
||||||
|
|
||||||
self.dc_id = e.x
|
self.dc_id = e.x
|
||||||
self.auth_key = Auth(self.dc_id, self.test_mode, self._proxy).create()
|
self.auth_key = Auth(self.dc_id, self.test_mode, self.ipv6, self._proxy).create()
|
||||||
|
|
||||||
self.session = Session(
|
self.session = Session(
|
||||||
self,
|
self,
|
||||||
@ -401,6 +440,8 @@ class Client(Methods, BaseClient):
|
|||||||
else:
|
else:
|
||||||
self.user_id = r.user.id
|
self.user_id = r.user.id
|
||||||
|
|
||||||
|
print("Logged in successfully as @{}".format(r.user.username))
|
||||||
|
|
||||||
def authorize_user(self):
|
def authorize_user(self):
|
||||||
phone_number_invalid_raises = self.phone_number is not None
|
phone_number_invalid_raises = self.phone_number is not None
|
||||||
phone_code_invalid_raises = self.phone_code is not None
|
phone_code_invalid_raises = self.phone_code is not None
|
||||||
@ -433,7 +474,7 @@ class Client(Methods, BaseClient):
|
|||||||
self.session.stop()
|
self.session.stop()
|
||||||
|
|
||||||
self.dc_id = e.x
|
self.dc_id = e.x
|
||||||
self.auth_key = Auth(self.dc_id, self.test_mode, self._proxy).create()
|
self.auth_key = Auth(self.dc_id, self.test_mode, self.ipv6, self._proxy).create()
|
||||||
|
|
||||||
self.session = Session(
|
self.session = Session(
|
||||||
self,
|
self,
|
||||||
@ -491,16 +532,8 @@ class Client(Methods, BaseClient):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
if phone_registered:
|
if phone_registered:
|
||||||
r = self.send(
|
|
||||||
functions.auth.SignIn(
|
|
||||||
self.phone_number,
|
|
||||||
phone_code_hash,
|
|
||||||
self.phone_code
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
try:
|
try:
|
||||||
self.send(
|
r = self.send(
|
||||||
functions.auth.SignIn(
|
functions.auth.SignIn(
|
||||||
self.phone_number,
|
self.phone_number,
|
||||||
phone_code_hash,
|
phone_code_hash,
|
||||||
@ -508,20 +541,27 @@ class Client(Methods, BaseClient):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
except PhoneNumberUnoccupied:
|
except PhoneNumberUnoccupied:
|
||||||
pass
|
log.warning("Phone number unregistered")
|
||||||
|
phone_registered = False
|
||||||
|
continue
|
||||||
|
else:
|
||||||
self.first_name = self.first_name if self.first_name is not None else input("First name: ")
|
self.first_name = self.first_name if self.first_name is not None else input("First name: ")
|
||||||
self.last_name = self.last_name if self.last_name is not None else input("Last name: ")
|
self.last_name = self.last_name if self.last_name is not None else input("Last name: ")
|
||||||
|
|
||||||
r = self.send(
|
try:
|
||||||
functions.auth.SignUp(
|
r = self.send(
|
||||||
self.phone_number,
|
functions.auth.SignUp(
|
||||||
phone_code_hash,
|
self.phone_number,
|
||||||
self.phone_code,
|
phone_code_hash,
|
||||||
self.first_name,
|
self.phone_code,
|
||||||
self.last_name
|
self.first_name,
|
||||||
|
self.last_name
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
except PhoneNumberOccupied:
|
||||||
|
log.warning("Phone number already registered")
|
||||||
|
phone_registered = True
|
||||||
|
continue
|
||||||
except (PhoneCodeInvalid, PhoneCodeEmpty, PhoneCodeExpired, PhoneCodeHashEmpty) as e:
|
except (PhoneCodeInvalid, PhoneCodeEmpty, PhoneCodeExpired, PhoneCodeHashEmpty) as e:
|
||||||
if phone_code_invalid_raises:
|
if phone_code_invalid_raises:
|
||||||
raise
|
raise
|
||||||
@ -536,21 +576,46 @@ class Client(Methods, BaseClient):
|
|||||||
self.first_name = None
|
self.first_name = None
|
||||||
except SessionPasswordNeeded as e:
|
except SessionPasswordNeeded as e:
|
||||||
print(e.MESSAGE)
|
print(e.MESSAGE)
|
||||||
r = self.send(functions.account.GetPassword())
|
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
|
r = self.send(functions.account.GetPassword())
|
||||||
|
|
||||||
if self.password is None:
|
if self.password is None:
|
||||||
print("Hint: {}".format(r.hint))
|
print("Hint: {}".format(r.hint))
|
||||||
self.password = getpass.getpass("Enter password: ")
|
|
||||||
|
|
||||||
if type(self.password) is str:
|
self.password = input("Enter password (empty to recover): ")
|
||||||
self.password = r.current_salt + self.password.encode() + r.current_salt
|
|
||||||
|
|
||||||
password_hash = sha256(self.password).digest()
|
if self.password == "":
|
||||||
|
r = self.send(functions.auth.RequestPasswordRecovery())
|
||||||
|
|
||||||
r = self.send(functions.auth.CheckPassword(password_hash))
|
print("An e-mail containing the recovery code has been sent to {}".format(
|
||||||
|
r.email_pattern
|
||||||
|
))
|
||||||
|
|
||||||
|
r = self.send(
|
||||||
|
functions.auth.RecoverPassword(
|
||||||
|
code=input("Enter password recovery code: ")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
r = self.send(
|
||||||
|
functions.auth.CheckPassword(
|
||||||
|
password=compute_check(r, self.password)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
except PasswordEmpty as e:
|
||||||
|
if password_hash_invalid_raises:
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
print(e.MESSAGE)
|
||||||
|
self.password = None
|
||||||
|
except PasswordRecoveryNa as e:
|
||||||
|
if password_hash_invalid_raises:
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
print(e.MESSAGE)
|
||||||
|
self.password = None
|
||||||
except PasswordHashInvalid as e:
|
except PasswordHashInvalid as e:
|
||||||
if password_hash_invalid_raises:
|
if password_hash_invalid_raises:
|
||||||
raise
|
raise
|
||||||
@ -563,6 +628,7 @@ class Client(Methods, BaseClient):
|
|||||||
else:
|
else:
|
||||||
print(e.MESSAGE.format(x=e.x))
|
print(e.MESSAGE.format(x=e.x))
|
||||||
time.sleep(e.x)
|
time.sleep(e.x)
|
||||||
|
self.password = None
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.error(e, exc_info=True)
|
log.error(e, exc_info=True)
|
||||||
else:
|
else:
|
||||||
@ -585,9 +651,11 @@ class Client(Methods, BaseClient):
|
|||||||
self.password = None
|
self.password = None
|
||||||
self.user_id = r.user.id
|
self.user_id = r.user.id
|
||||||
|
|
||||||
print("Login successful")
|
print("Logged in successfully as {}".format(r.user.first_name))
|
||||||
|
|
||||||
def fetch_peers(self, entities: list):
|
def fetch_peers(self, entities: List[Union[types.User,
|
||||||
|
types.Chat, types.ChatForbidden,
|
||||||
|
types.Channel, types.ChannelForbidden]]):
|
||||||
for entity in entities:
|
for entity in entities:
|
||||||
if isinstance(entity, types.User):
|
if isinstance(entity, types.User):
|
||||||
user_id = entity.id
|
user_id = entity.id
|
||||||
@ -613,7 +681,7 @@ class Client(Methods, BaseClient):
|
|||||||
if phone is not None:
|
if phone is not None:
|
||||||
self.peers_by_phone[phone] = input_peer
|
self.peers_by_phone[phone] = input_peer
|
||||||
|
|
||||||
if isinstance(entity, types.Chat):
|
if isinstance(entity, (types.Chat, types.ChatForbidden)):
|
||||||
chat_id = entity.id
|
chat_id = entity.id
|
||||||
peer_id = -chat_id
|
peer_id = -chat_id
|
||||||
|
|
||||||
@ -623,7 +691,7 @@ class Client(Methods, BaseClient):
|
|||||||
|
|
||||||
self.peers_by_id[peer_id] = input_peer
|
self.peers_by_id[peer_id] = input_peer
|
||||||
|
|
||||||
if isinstance(entity, types.Channel):
|
if isinstance(entity, (types.Channel, types.ChannelForbidden)):
|
||||||
channel_id = entity.id
|
channel_id = entity.id
|
||||||
peer_id = int("-100" + str(channel_id))
|
peer_id = int("-100" + str(channel_id))
|
||||||
|
|
||||||
@ -632,7 +700,7 @@ class Client(Methods, BaseClient):
|
|||||||
if access_hash is None:
|
if access_hash is None:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
username = entity.username
|
username = getattr(entity, "username", None)
|
||||||
|
|
||||||
input_peer = types.InputPeerChannel(
|
input_peer = types.InputPeerChannel(
|
||||||
channel_id=channel_id,
|
channel_id=channel_id,
|
||||||
@ -711,7 +779,9 @@ class Client(Methods, BaseClient):
|
|||||||
|
|
||||||
file_name = "{}_{}_{}{}".format(
|
file_name = "{}_{}_{}{}".format(
|
||||||
media_type_str,
|
media_type_str,
|
||||||
datetime.fromtimestamp(media.date or time.time()).strftime("%Y-%m-%d_%H-%M-%S"),
|
datetime.fromtimestamp(
|
||||||
|
getattr(media, "date", None) or time.time()
|
||||||
|
).strftime("%Y-%m-%d_%H-%M-%S"),
|
||||||
self.rnd_id(),
|
self.rnd_id(),
|
||||||
extension
|
extension
|
||||||
)
|
)
|
||||||
@ -776,27 +846,33 @@ class Client(Methods, BaseClient):
|
|||||||
pts = getattr(update, "pts", None)
|
pts = getattr(update, "pts", None)
|
||||||
pts_count = getattr(update, "pts_count", None)
|
pts_count = getattr(update, "pts_count", None)
|
||||||
|
|
||||||
|
if isinstance(update, types.UpdateChannelTooLong):
|
||||||
|
log.warning(update)
|
||||||
|
|
||||||
if isinstance(update, types.UpdateNewChannelMessage):
|
if isinstance(update, types.UpdateNewChannelMessage):
|
||||||
message = update.message
|
message = update.message
|
||||||
|
|
||||||
if not isinstance(message, types.MessageEmpty):
|
if not isinstance(message, types.MessageEmpty):
|
||||||
diff = self.send(
|
try:
|
||||||
functions.updates.GetChannelDifference(
|
diff = self.send(
|
||||||
channel=self.resolve_peer(int("-100" + str(channel_id))),
|
functions.updates.GetChannelDifference(
|
||||||
filter=types.ChannelMessagesFilter(
|
channel=self.resolve_peer(int("-100" + str(channel_id))),
|
||||||
ranges=[types.MessageRange(
|
filter=types.ChannelMessagesFilter(
|
||||||
min_id=update.message.id,
|
ranges=[types.MessageRange(
|
||||||
max_id=update.message.id
|
min_id=update.message.id,
|
||||||
)]
|
max_id=update.message.id
|
||||||
),
|
)]
|
||||||
pts=pts - pts_count,
|
),
|
||||||
limit=pts
|
pts=pts - pts_count,
|
||||||
|
limit=pts
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
except ChannelPrivate:
|
||||||
|
pass
|
||||||
if not isinstance(diff, types.updates.ChannelDifferenceEmpty):
|
else:
|
||||||
updates.users += diff.users
|
if not isinstance(diff, types.updates.ChannelDifferenceEmpty):
|
||||||
updates.chats += diff.chats
|
updates.users += diff.users
|
||||||
|
updates.chats += diff.chats
|
||||||
|
|
||||||
if channel_id and pts:
|
if channel_id and pts:
|
||||||
if channel_id not in self.channels_pts:
|
if channel_id not in self.channels_pts:
|
||||||
@ -810,7 +886,7 @@ class Client(Methods, BaseClient):
|
|||||||
if len(self.channels_pts[channel_id]) > 50:
|
if len(self.channels_pts[channel_id]) > 50:
|
||||||
self.channels_pts[channel_id] = self.channels_pts[channel_id][25:]
|
self.channels_pts[channel_id] = self.channels_pts[channel_id][25:]
|
||||||
|
|
||||||
self.dispatcher.updates.put((update, updates.users, updates.chats))
|
self.dispatcher.updates_queue.put((update, updates.users, updates.chats))
|
||||||
elif isinstance(updates, (types.UpdateShortMessage, types.UpdateShortChatMessage)):
|
elif isinstance(updates, (types.UpdateShortMessage, types.UpdateShortChatMessage)):
|
||||||
diff = self.send(
|
diff = self.send(
|
||||||
functions.updates.GetDifference(
|
functions.updates.GetDifference(
|
||||||
@ -821,7 +897,7 @@ class Client(Methods, BaseClient):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if diff.new_messages:
|
if diff.new_messages:
|
||||||
self.dispatcher.updates.put((
|
self.dispatcher.updates_queue.put((
|
||||||
types.UpdateNewMessage(
|
types.UpdateNewMessage(
|
||||||
message=diff.new_messages[0],
|
message=diff.new_messages[0],
|
||||||
pts=updates.pts,
|
pts=updates.pts,
|
||||||
@ -831,15 +907,20 @@ class Client(Methods, BaseClient):
|
|||||||
diff.chats
|
diff.chats
|
||||||
))
|
))
|
||||||
else:
|
else:
|
||||||
self.dispatcher.updates.put((diff.other_updates[0], [], []))
|
self.dispatcher.updates_queue.put((diff.other_updates[0], [], []))
|
||||||
elif isinstance(updates, types.UpdateShort):
|
elif isinstance(updates, types.UpdateShort):
|
||||||
self.dispatcher.updates.put((updates.update, [], []))
|
self.dispatcher.updates_queue.put((updates.update, [], []))
|
||||||
|
elif isinstance(updates, types.UpdatesTooLong):
|
||||||
|
log.warning(updates)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.error(e, exc_info=True)
|
log.error(e, exc_info=True)
|
||||||
|
|
||||||
log.debug("{} stopped".format(name))
|
log.debug("{} stopped".format(name))
|
||||||
|
|
||||||
def send(self, data: Object, retries: int = Session.MAX_RETRIES, timeout: float = Session.WAIT_TIMEOUT):
|
def send(self,
|
||||||
|
data: Object,
|
||||||
|
retries: int = Session.MAX_RETRIES,
|
||||||
|
timeout: float = Session.WAIT_TIMEOUT):
|
||||||
"""Use this method to send Raw Function queries.
|
"""Use this method to send Raw Function queries.
|
||||||
|
|
||||||
This method makes possible to manually call every single Telegram API method in a low-level manner.
|
This method makes possible to manually call every single Telegram API method in a low-level manner.
|
||||||
@ -848,7 +929,7 @@ class Client(Methods, BaseClient):
|
|||||||
|
|
||||||
Args:
|
Args:
|
||||||
data (``Object``):
|
data (``Object``):
|
||||||
The API Scheme function filled with proper arguments.
|
The API Schema function filled with proper arguments.
|
||||||
|
|
||||||
retries (``int``):
|
retries (``int``):
|
||||||
Number of retries.
|
Number of retries.
|
||||||
@ -857,7 +938,7 @@ class Client(Methods, BaseClient):
|
|||||||
Timeout in seconds.
|
Timeout in seconds.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
:class:`Error <pyrogram.Error>`
|
:class:`Error <pyrogram.Error>` 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")
|
||||||
@ -885,30 +966,18 @@ class Client(Methods, BaseClient):
|
|||||||
"More info: https://docs.pyrogram.ml/start/ProjectSetup#configuration"
|
"More info: https://docs.pyrogram.ml/start/ProjectSetup#configuration"
|
||||||
)
|
)
|
||||||
|
|
||||||
for option in {"app_version", "device_model", "system_version", "lang_code"}:
|
for option in ["app_version", "device_model", "system_version", "lang_code"]:
|
||||||
if getattr(self, option):
|
if getattr(self, option):
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
setattr(self, option, Client.APP_VERSION)
|
|
||||||
|
|
||||||
if parser.has_section("pyrogram"):
|
if parser.has_section("pyrogram"):
|
||||||
setattr(self, option, parser.get(
|
setattr(self, option, parser.get(
|
||||||
"pyrogram",
|
"pyrogram",
|
||||||
option,
|
option,
|
||||||
fallback=getattr(Client, option.upper())
|
fallback=getattr(Client, option.upper())
|
||||||
))
|
))
|
||||||
|
else:
|
||||||
if self.lang_code:
|
setattr(self, option, getattr(Client, option.upper()))
|
||||||
pass
|
|
||||||
else:
|
|
||||||
self.lang_code = Client.LANG_CODE
|
|
||||||
|
|
||||||
if parser.has_section("pyrogram"):
|
|
||||||
self.lang_code = parser.get(
|
|
||||||
"pyrogram",
|
|
||||||
"lang_code",
|
|
||||||
fallback=Client.LANG_CODE
|
|
||||||
)
|
|
||||||
|
|
||||||
if self._proxy:
|
if self._proxy:
|
||||||
self._proxy["enabled"] = True
|
self._proxy["enabled"] = True
|
||||||
@ -929,7 +998,7 @@ class Client(Methods, BaseClient):
|
|||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
self.dc_id = 1
|
self.dc_id = 1
|
||||||
self.date = 0
|
self.date = 0
|
||||||
self.auth_key = Auth(self.dc_id, self.test_mode, self._proxy).create()
|
self.auth_key = Auth(self.dc_id, self.test_mode, self.ipv6, self._proxy).create()
|
||||||
else:
|
else:
|
||||||
self.dc_id = s["dc_id"]
|
self.dc_id = s["dc_id"]
|
||||||
self.test_mode = s["test_mode"]
|
self.test_mode = s["test_mode"]
|
||||||
@ -952,6 +1021,45 @@ class Client(Methods, BaseClient):
|
|||||||
if peer:
|
if peer:
|
||||||
self.peers_by_phone[k] = peer
|
self.peers_by_phone[k] = peer
|
||||||
|
|
||||||
|
def load_plugins(self):
|
||||||
|
if self.plugins_dir is not None:
|
||||||
|
plugins_count = 0
|
||||||
|
|
||||||
|
for path in Path(self.plugins_dir).rglob("*.py"):
|
||||||
|
file_path = os.path.splitext(str(path))[0]
|
||||||
|
import_path = []
|
||||||
|
|
||||||
|
while file_path:
|
||||||
|
file_path, tail = os.path.split(file_path)
|
||||||
|
import_path.insert(0, tail)
|
||||||
|
|
||||||
|
import_path = ".".join(import_path)
|
||||||
|
module = import_module(import_path)
|
||||||
|
|
||||||
|
for name in dir(module):
|
||||||
|
# noinspection PyBroadException
|
||||||
|
try:
|
||||||
|
handler, group = getattr(module, name)
|
||||||
|
|
||||||
|
if isinstance(handler, Handler) and isinstance(group, int):
|
||||||
|
self.add_handler(handler, group)
|
||||||
|
|
||||||
|
log.info('{}("{}") from "{}" loaded in group {}'.format(
|
||||||
|
type(handler).__name__, name, import_path, group))
|
||||||
|
|
||||||
|
plugins_count += 1
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if plugins_count > 0:
|
||||||
|
log.warning('Successfully loaded {} plugin{} from "{}"'.format(
|
||||||
|
plugins_count,
|
||||||
|
"s" if plugins_count > 1 else "",
|
||||||
|
self.plugins_dir
|
||||||
|
))
|
||||||
|
else:
|
||||||
|
log.warning('No plugin loaded: "{}" doesn\'t contain any valid plugin'.format(self.plugins_dir))
|
||||||
|
|
||||||
def save_session(self):
|
def save_session(self):
|
||||||
auth_key = base64.b64encode(self.auth_key).decode()
|
auth_key = base64.b64encode(self.auth_key).decode()
|
||||||
auth_key = [auth_key[i: i + 43] for i in range(0, len(auth_key), 43)]
|
auth_key = [auth_key[i: i + 43] for i in range(0, len(auth_key), 43)]
|
||||||
@ -971,13 +1079,18 @@ class Client(Methods, BaseClient):
|
|||||||
indent=4
|
indent=4
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_dialogs_chunk(self, offset_date):
|
def get_initial_dialogs_chunk(self,
|
||||||
|
offset_date: int = 0):
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
r = self.send(
|
r = self.send(
|
||||||
functions.messages.GetDialogs(
|
functions.messages.GetDialogs(
|
||||||
offset_date, 0, types.InputPeerEmpty(),
|
offset_date=offset_date,
|
||||||
self.DIALOGS_AT_ONCE, True
|
offset_id=0,
|
||||||
|
offset_peer=types.InputPeerEmpty(),
|
||||||
|
limit=self.DIALOGS_AT_ONCE,
|
||||||
|
hash=0,
|
||||||
|
exclude_pinned=True
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
except FloodWait as e:
|
except FloodWait as e:
|
||||||
@ -987,84 +1100,90 @@ class Client(Methods, BaseClient):
|
|||||||
log.info("Total peers: {}".format(len(self.peers_by_id)))
|
log.info("Total peers: {}".format(len(self.peers_by_id)))
|
||||||
return r
|
return r
|
||||||
|
|
||||||
def get_dialogs(self):
|
def get_initial_dialogs(self):
|
||||||
self.send(functions.messages.GetPinnedDialogs())
|
self.send(functions.messages.GetPinnedDialogs())
|
||||||
|
|
||||||
dialogs = self.get_dialogs_chunk(0)
|
dialogs = self.get_initial_dialogs_chunk()
|
||||||
offset_date = utils.get_offset_date(dialogs)
|
offset_date = utils.get_offset_date(dialogs)
|
||||||
|
|
||||||
while len(dialogs.dialogs) == self.DIALOGS_AT_ONCE:
|
while len(dialogs.dialogs) == self.DIALOGS_AT_ONCE:
|
||||||
dialogs = self.get_dialogs_chunk(offset_date)
|
dialogs = self.get_initial_dialogs_chunk(offset_date)
|
||||||
offset_date = utils.get_offset_date(dialogs)
|
offset_date = utils.get_offset_date(dialogs)
|
||||||
|
|
||||||
self.get_dialogs_chunk(0)
|
self.get_initial_dialogs_chunk()
|
||||||
|
|
||||||
def resolve_peer(self, peer_id: int or str):
|
def resolve_peer(self,
|
||||||
"""Use this method to get the *InputPeer* of a known *peer_id*.
|
peer_id: Union[int, str]):
|
||||||
|
"""Use this method to get the InputPeer of a known peer_id.
|
||||||
|
|
||||||
It is intended to be used when working with Raw Functions (i.e: a Telegram API method you wish to use which is
|
This is a utility method intended to be used only when working with Raw Functions (i.e: a Telegram API method
|
||||||
not available yet in the Client class as an easy-to-use method).
|
you wish to use which is not available yet in the Client class as an easy-to-use method), whenever an InputPeer
|
||||||
|
type is required.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
peer_id (``int`` | ``str`` | ``Peer``):
|
peer_id (``int`` | ``str``):
|
||||||
The Peer ID you want to extract the InputPeer from. Can be one of these types: ``int`` (direct ID),
|
The peer id you want to extract the InputPeer from.
|
||||||
``str`` (@username), :obj:`PeerUser <pyrogram.api.types.PeerUser>`,
|
Can be a direct id (int), a username (str) or a phone number (str).
|
||||||
:obj:`PeerChat <pyrogram.api.types.PeerChat>`, :obj:`PeerChannel <pyrogram.api.types.PeerChannel>`
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
:obj:`InputPeerUser <pyrogram.api.types.InputPeerUser>` or
|
On success, the resolved peer id is returned in form of an InputPeer object.
|
||||||
:obj:`InputPeerChat <pyrogram.api.types.InputPeerChat>` or
|
|
||||||
:obj:`InputPeerChannel <pyrogram.api.types.InputPeerChannel>` depending on the *peer_id*.
|
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
:class:`Error <pyrogram.Error>`
|
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
|
||||||
|
``KeyError`` in case the peer doesn't exist in the internal database.
|
||||||
"""
|
"""
|
||||||
if type(peer_id) is str:
|
try:
|
||||||
if peer_id in ("self", "me"):
|
|
||||||
return types.InputPeerSelf()
|
|
||||||
|
|
||||||
match = self.INVITE_LINK_RE.match(peer_id)
|
|
||||||
|
|
||||||
try:
|
|
||||||
decoded = base64.b64decode(match.group(1) + "=" * (-len(match.group(1)) % 4), "-_")
|
|
||||||
return self.resolve_peer(struct.unpack(">2iq", decoded)[1])
|
|
||||||
except (AttributeError, binascii.Error, struct.error):
|
|
||||||
pass
|
|
||||||
|
|
||||||
peer_id = re.sub(r"[@+\s]", "", peer_id.lower())
|
|
||||||
|
|
||||||
try:
|
|
||||||
int(peer_id)
|
|
||||||
except ValueError:
|
|
||||||
try:
|
|
||||||
return self.peers_by_username[peer_id]
|
|
||||||
except KeyError:
|
|
||||||
self.send(functions.contacts.ResolveUsername(peer_id))
|
|
||||||
return self.peers_by_username[peer_id]
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
return self.peers_by_phone[peer_id]
|
|
||||||
except KeyError:
|
|
||||||
raise PeerIdInvalid
|
|
||||||
|
|
||||||
if type(peer_id) is not int:
|
|
||||||
if isinstance(peer_id, types.PeerUser):
|
|
||||||
peer_id = peer_id.user_id
|
|
||||||
elif isinstance(peer_id, types.PeerChat):
|
|
||||||
peer_id = -peer_id.chat_id
|
|
||||||
elif isinstance(peer_id, types.PeerChannel):
|
|
||||||
peer_id = int("-100" + str(peer_id.channel_id))
|
|
||||||
|
|
||||||
try: # User
|
|
||||||
return self.peers_by_id[peer_id]
|
return self.peers_by_id[peer_id]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
try: # Chat
|
if type(peer_id) is str:
|
||||||
return self.peers_by_id[-peer_id]
|
if peer_id in ("self", "me"):
|
||||||
|
return types.InputPeerSelf()
|
||||||
|
|
||||||
|
peer_id = re.sub(r"[@+\s]", "", peer_id.lower())
|
||||||
|
|
||||||
|
try:
|
||||||
|
int(peer_id)
|
||||||
|
except ValueError:
|
||||||
|
if peer_id not in self.peers_by_username:
|
||||||
|
self.send(
|
||||||
|
functions.contacts.ResolveUsername(
|
||||||
|
username=peer_id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return self.peers_by_username[peer_id]
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
return self.peers_by_phone[peer_id]
|
||||||
|
except KeyError:
|
||||||
|
raise PeerIdInvalid
|
||||||
|
|
||||||
|
if peer_id > 0:
|
||||||
|
self.fetch_peers(
|
||||||
|
self.send(
|
||||||
|
functions.users.GetUsers(
|
||||||
|
id=[types.InputUser(peer_id, 0)]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
if str(peer_id).startswith("-100"):
|
||||||
|
self.send(
|
||||||
|
functions.channels.GetChannels(
|
||||||
|
id=[types.InputChannel(int(str(peer_id)[4:]), 0)]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.send(
|
||||||
|
functions.messages.GetChats(
|
||||||
|
id=[-peer_id]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self.peers_by_id[peer_id]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
try: # Channel
|
raise PeerIdInvalid
|
||||||
return self.peers_by_id[int("-100" + str(peer_id))]
|
|
||||||
except (KeyError, ValueError):
|
|
||||||
raise PeerIdInvalid
|
|
||||||
|
|
||||||
def save_file(self,
|
def save_file(self,
|
||||||
path: str,
|
path: str,
|
||||||
@ -1074,6 +1193,13 @@ class Client(Methods, BaseClient):
|
|||||||
progress_args: tuple = ()):
|
progress_args: tuple = ()):
|
||||||
part_size = 512 * 1024
|
part_size = 512 * 1024
|
||||||
file_size = os.path.getsize(path)
|
file_size = os.path.getsize(path)
|
||||||
|
|
||||||
|
if file_size == 0:
|
||||||
|
raise ValueError("File size equals to 0 B")
|
||||||
|
|
||||||
|
if file_size > 1500 * 1024 * 1024:
|
||||||
|
raise ValueError("Telegram doesn't support uploading files bigger than 1500 MiB")
|
||||||
|
|
||||||
file_total_parts = int(math.ceil(file_size / part_size))
|
file_total_parts = int(math.ceil(file_size / part_size))
|
||||||
is_big = True if file_size > 10 * 1024 * 1024 else False
|
is_big = True if file_size > 10 * 1024 * 1024 else False
|
||||||
is_missing_part = True if file_id is not None else False
|
is_missing_part = True if file_id is not None else False
|
||||||
@ -1148,10 +1274,9 @@ class Client(Methods, BaseClient):
|
|||||||
volume_id: int = None,
|
volume_id: int = None,
|
||||||
local_id: int = None,
|
local_id: int = None,
|
||||||
secret: int = None,
|
secret: int = None,
|
||||||
version: int = 0,
|
|
||||||
size: int = None,
|
size: int = None,
|
||||||
progress: callable = None,
|
progress: callable = None,
|
||||||
progress_args: tuple = None) -> str:
|
progress_args: tuple = ()) -> str:
|
||||||
with self.media_sessions_lock:
|
with self.media_sessions_lock:
|
||||||
session = self.media_sessions.get(dc_id, None)
|
session = self.media_sessions.get(dc_id, None)
|
||||||
|
|
||||||
@ -1166,7 +1291,7 @@ class Client(Methods, BaseClient):
|
|||||||
session = Session(
|
session = Session(
|
||||||
self,
|
self,
|
||||||
dc_id,
|
dc_id,
|
||||||
Auth(dc_id, self.test_mode, self._proxy).create(),
|
Auth(dc_id, self.test_mode, self.ipv6, self._proxy).create(),
|
||||||
is_media=True
|
is_media=True
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1196,13 +1321,14 @@ class Client(Methods, BaseClient):
|
|||||||
location = types.InputFileLocation(
|
location = types.InputFileLocation(
|
||||||
volume_id=volume_id,
|
volume_id=volume_id,
|
||||||
local_id=local_id,
|
local_id=local_id,
|
||||||
secret=secret
|
secret=secret,
|
||||||
|
file_reference=b""
|
||||||
)
|
)
|
||||||
else: # Any other file can be more easily accessed by id and access_hash
|
else: # Any other file can be more easily accessed by id and access_hash
|
||||||
location = types.InputDocumentFileLocation(
|
location = types.InputDocumentFileLocation(
|
||||||
id=id,
|
id=id,
|
||||||
access_hash=access_hash,
|
access_hash=access_hash,
|
||||||
version=version
|
file_reference=b""
|
||||||
)
|
)
|
||||||
|
|
||||||
limit = 1024 * 1024
|
limit = 1024 * 1024
|
||||||
@ -1229,13 +1355,11 @@ class Client(Methods, BaseClient):
|
|||||||
break
|
break
|
||||||
|
|
||||||
f.write(chunk)
|
f.write(chunk)
|
||||||
f.flush()
|
|
||||||
os.fsync(f.fileno())
|
|
||||||
|
|
||||||
offset += limit
|
offset += limit
|
||||||
|
|
||||||
if progress:
|
if progress:
|
||||||
progress(self, min(offset, size), size, *progress_args)
|
progress(self, min(offset, size) if size != 0 else offset, size, *progress_args)
|
||||||
|
|
||||||
r = session.send(
|
r = session.send(
|
||||||
functions.upload.GetFile(
|
functions.upload.GetFile(
|
||||||
@ -1253,7 +1377,7 @@ class Client(Methods, BaseClient):
|
|||||||
cdn_session = Session(
|
cdn_session = Session(
|
||||||
self,
|
self,
|
||||||
r.dc_id,
|
r.dc_id,
|
||||||
Auth(r.dc_id, self.test_mode, self._proxy).create(),
|
Auth(r.dc_id, self.test_mode, self.ipv6, self._proxy).create(),
|
||||||
is_media=True,
|
is_media=True,
|
||||||
is_cdn=True
|
is_cdn=True
|
||||||
)
|
)
|
||||||
@ -1313,13 +1437,11 @@ class Client(Methods, BaseClient):
|
|||||||
assert h.hash == sha256(cdn_chunk).digest(), "Invalid CDN hash part {}".format(i)
|
assert h.hash == sha256(cdn_chunk).digest(), "Invalid CDN hash part {}".format(i)
|
||||||
|
|
||||||
f.write(decrypted_chunk)
|
f.write(decrypted_chunk)
|
||||||
f.flush()
|
|
||||||
os.fsync(f.fileno())
|
|
||||||
|
|
||||||
offset += limit
|
offset += limit
|
||||||
|
|
||||||
if progress:
|
if progress:
|
||||||
progress(self, min(offset, size), size, *progress_args)
|
progress(self, min(offset, size) if size != 0 else offset, size, *progress_args)
|
||||||
|
|
||||||
if len(chunk) < limit:
|
if len(chunk) < limit:
|
||||||
break
|
break
|
||||||
|
@ -24,8 +24,7 @@ from threading import Thread
|
|||||||
|
|
||||||
import pyrogram
|
import pyrogram
|
||||||
from pyrogram.api import types
|
from pyrogram.api import types
|
||||||
from ..ext import utils
|
from ..handlers import CallbackQueryHandler, MessageHandler, RawUpdateHandler, UserStatusHandler, DeletedMessagesHandler
|
||||||
from ..handlers import RawUpdateHandler, CallbackQueryHandler, MessageHandler, DeletedMessagesHandler
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -41,20 +40,44 @@ class Dispatcher:
|
|||||||
types.UpdateEditChannelMessage
|
types.UpdateEditChannelMessage
|
||||||
)
|
)
|
||||||
|
|
||||||
DELETE_MESSAGE_UPDATES = (
|
DELETE_MESSAGES_UPDATES = (
|
||||||
types.UpdateDeleteMessages,
|
types.UpdateDeleteMessages,
|
||||||
types.UpdateDeleteChannelMessages
|
types.UpdateDeleteChannelMessages
|
||||||
)
|
)
|
||||||
|
|
||||||
|
CALLBACK_QUERY_UPDATES = (
|
||||||
|
types.UpdateBotCallbackQuery,
|
||||||
|
types.UpdateInlineBotCallbackQuery
|
||||||
|
)
|
||||||
|
|
||||||
MESSAGE_UPDATES = NEW_MESSAGE_UPDATES + EDIT_MESSAGE_UPDATES
|
MESSAGE_UPDATES = NEW_MESSAGE_UPDATES + EDIT_MESSAGE_UPDATES
|
||||||
|
|
||||||
def __init__(self, client, workers):
|
def __init__(self, client, workers: int):
|
||||||
self.client = client
|
self.client = client
|
||||||
self.workers = workers
|
self.workers = workers
|
||||||
|
|
||||||
self.workers_list = []
|
self.workers_list = []
|
||||||
self.updates = Queue()
|
self.updates_queue = Queue()
|
||||||
self.groups = OrderedDict()
|
self.groups = OrderedDict()
|
||||||
|
|
||||||
|
self.update_parsers = {
|
||||||
|
Dispatcher.MESSAGE_UPDATES:
|
||||||
|
lambda upd, usr, cht: (pyrogram.Message._parse(self.client, upd.message, usr, cht), MessageHandler),
|
||||||
|
|
||||||
|
Dispatcher.DELETE_MESSAGES_UPDATES:
|
||||||
|
lambda upd, usr, cht: (pyrogram.Messages._parse_deleted(self.client, upd), DeletedMessagesHandler),
|
||||||
|
|
||||||
|
Dispatcher.CALLBACK_QUERY_UPDATES:
|
||||||
|
lambda upd, usr, cht: (pyrogram.CallbackQuery._parse(self.client, upd, usr), CallbackQueryHandler),
|
||||||
|
|
||||||
|
(types.UpdateUserStatus,):
|
||||||
|
lambda upd, usr, cht: (
|
||||||
|
pyrogram.UserStatus._parse(self.client, upd.status, upd.user_id), UserStatusHandler
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.update_parsers = {key: value for key_tuple, value in self.update_parsers.items() for key in key_tuple}
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
for i in range(self.workers):
|
for i in range(self.workers):
|
||||||
self.workers_list.append(
|
self.workers_list.append(
|
||||||
@ -68,10 +91,10 @@ class Dispatcher:
|
|||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
for _ in range(self.workers):
|
for _ in range(self.workers):
|
||||||
self.updates.put(None)
|
self.updates_queue.put(None)
|
||||||
|
|
||||||
for i in self.workers_list:
|
for worker in self.workers_list:
|
||||||
i.join()
|
worker.join()
|
||||||
|
|
||||||
self.workers_list.clear()
|
self.workers_list.clear()
|
||||||
|
|
||||||
@ -84,56 +107,16 @@ class Dispatcher:
|
|||||||
|
|
||||||
def remove_handler(self, handler, group: int):
|
def remove_handler(self, handler, group: int):
|
||||||
if group not in self.groups:
|
if group not in self.groups:
|
||||||
raise ValueError("Group {} does not exist. "
|
raise ValueError("Group {} does not exist. Handler was not removed.".format(group))
|
||||||
"Handler was not removed.".format(group))
|
|
||||||
self.groups[group].remove(handler)
|
self.groups[group].remove(handler)
|
||||||
|
|
||||||
def dispatch(self, update, users: dict = None, chats: dict = None, is_raw: bool = False):
|
|
||||||
for group in self.groups.values():
|
|
||||||
for handler in group:
|
|
||||||
if is_raw:
|
|
||||||
if not isinstance(handler, RawUpdateHandler):
|
|
||||||
continue
|
|
||||||
|
|
||||||
args = (self.client, update, users, chats)
|
|
||||||
else:
|
|
||||||
message = (update.message
|
|
||||||
or update.channel_post
|
|
||||||
or update.edited_message
|
|
||||||
or update.edited_channel_post)
|
|
||||||
|
|
||||||
deleted_messages = (update.deleted_channel_posts
|
|
||||||
or update.deleted_messages)
|
|
||||||
|
|
||||||
callback_query = update.callback_query
|
|
||||||
|
|
||||||
if message and isinstance(handler, MessageHandler):
|
|
||||||
if not handler.check(message):
|
|
||||||
continue
|
|
||||||
|
|
||||||
args = (self.client, message)
|
|
||||||
elif deleted_messages and isinstance(handler, DeletedMessagesHandler):
|
|
||||||
if not handler.check(deleted_messages):
|
|
||||||
continue
|
|
||||||
|
|
||||||
args = (self.client, deleted_messages)
|
|
||||||
elif callback_query and isinstance(handler, CallbackQueryHandler):
|
|
||||||
if not handler.check(callback_query):
|
|
||||||
continue
|
|
||||||
|
|
||||||
args = (self.client, callback_query)
|
|
||||||
else:
|
|
||||||
continue
|
|
||||||
|
|
||||||
handler.callback(*args)
|
|
||||||
break
|
|
||||||
|
|
||||||
def update_worker(self):
|
def update_worker(self):
|
||||||
name = threading.current_thread().name
|
name = threading.current_thread().name
|
||||||
log.debug("{} started".format(name))
|
log.debug("{} started".format(name))
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
update = self.updates.get()
|
update = self.updates_queue.get()
|
||||||
|
|
||||||
if update is None:
|
if update is None:
|
||||||
break
|
break
|
||||||
@ -143,71 +126,32 @@ class Dispatcher:
|
|||||||
chats = {i.id: i for i in update[2]}
|
chats = {i.id: i for i in update[2]}
|
||||||
update = update[0]
|
update = update[0]
|
||||||
|
|
||||||
self.dispatch(update, users=users, chats=chats, is_raw=True)
|
parser = self.update_parsers.get(type(update), None)
|
||||||
|
|
||||||
if isinstance(update, Dispatcher.MESSAGE_UPDATES):
|
if parser is None:
|
||||||
if isinstance(update.message, types.MessageEmpty):
|
|
||||||
continue
|
|
||||||
|
|
||||||
message = utils.parse_messages(
|
|
||||||
self.client,
|
|
||||||
update.message,
|
|
||||||
users,
|
|
||||||
chats
|
|
||||||
)
|
|
||||||
|
|
||||||
is_edited_message = isinstance(update, Dispatcher.EDIT_MESSAGE_UPDATES)
|
|
||||||
|
|
||||||
self.dispatch(
|
|
||||||
pyrogram.Update(
|
|
||||||
message=((message if message.chat.type != "channel"
|
|
||||||
else None) if not is_edited_message
|
|
||||||
else None),
|
|
||||||
edited_message=((message if message.chat.type != "channel"
|
|
||||||
else None) if is_edited_message
|
|
||||||
else None),
|
|
||||||
channel_post=((message if message.chat.type == "channel"
|
|
||||||
else None) if not is_edited_message
|
|
||||||
else None),
|
|
||||||
edited_channel_post=((message if message.chat.type == "channel"
|
|
||||||
else None) if is_edited_message
|
|
||||||
else None)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
elif isinstance(update, Dispatcher.DELETE_MESSAGE_UPDATES):
|
|
||||||
is_channel = hasattr(update, 'channel_id')
|
|
||||||
|
|
||||||
messages = utils.parse_deleted_messages(
|
|
||||||
update.messages,
|
|
||||||
(update.channel_id if is_channel else None)
|
|
||||||
)
|
|
||||||
|
|
||||||
self.dispatch(
|
|
||||||
pyrogram.Update(
|
|
||||||
deleted_messages=(messages if not is_channel else None),
|
|
||||||
deleted_channel_posts=(messages if is_channel else None)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
elif isinstance(update, types.UpdateBotCallbackQuery):
|
|
||||||
self.dispatch(
|
|
||||||
pyrogram.Update(
|
|
||||||
callback_query=utils.parse_callback_query(
|
|
||||||
self.client, update, users
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
elif isinstance(update, types.UpdateInlineBotCallbackQuery):
|
|
||||||
self.dispatch(
|
|
||||||
pyrogram.Update(
|
|
||||||
callback_query=utils.parse_inline_callback_query(
|
|
||||||
update, users
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
parsed_update, handler_type = parser(update, users, chats)
|
||||||
|
|
||||||
|
for group in self.groups.values():
|
||||||
|
for handler in group:
|
||||||
|
args = None
|
||||||
|
|
||||||
|
if isinstance(handler, RawUpdateHandler):
|
||||||
|
args = (update, users, chats)
|
||||||
|
elif isinstance(handler, handler_type):
|
||||||
|
if handler.check(parsed_update):
|
||||||
|
args = (parsed_update,)
|
||||||
|
|
||||||
|
if args is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
handler.callback(self.client, *args)
|
||||||
|
except Exception as e:
|
||||||
|
log.error(e, exc_info=True)
|
||||||
|
finally:
|
||||||
|
break
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.error(e, exc_info=True)
|
log.error(e, exc_info=True)
|
||||||
|
|
||||||
|
@ -41,7 +41,6 @@ class BaseClient:
|
|||||||
platform.release()
|
platform.release()
|
||||||
)
|
)
|
||||||
|
|
||||||
SYSTEM_LANG_CODE = "en"
|
|
||||||
LANG_CODE = "en"
|
LANG_CODE = "en"
|
||||||
|
|
||||||
INVITE_LINK_RE = re.compile(r"^(?:https?://)?(?:www\.)?(?:t(?:elegram)?\.(?:org|me|dog)/joinchat/)([\w-]+)$")
|
INVITE_LINK_RE = re.compile(r"^(?:https?://)?(?:www\.)?(?:t(?:elegram)?\.(?:org|me|dog)/joinchat/)([\w-]+)$")
|
||||||
@ -50,6 +49,9 @@ class BaseClient:
|
|||||||
UPDATES_WORKERS = 1
|
UPDATES_WORKERS = 1
|
||||||
DOWNLOAD_WORKERS = 1
|
DOWNLOAD_WORKERS = 1
|
||||||
OFFLINE_SLEEP = 300
|
OFFLINE_SLEEP = 300
|
||||||
|
WORKERS = 4
|
||||||
|
WORKDIR = "."
|
||||||
|
CONFIG_FILE = "./config.ini"
|
||||||
|
|
||||||
MEDIA_TYPE_ID = {
|
MEDIA_TYPE_ID = {
|
||||||
0: "thumbnail",
|
0: "thumbnail",
|
||||||
@ -60,12 +62,12 @@ class BaseClient:
|
|||||||
5: "document",
|
5: "document",
|
||||||
8: "sticker",
|
8: "sticker",
|
||||||
9: "audio",
|
9: "audio",
|
||||||
10: "gif",
|
10: "animation",
|
||||||
13: "video_note"
|
13: "video_note"
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.token = None
|
self.bot_token = None
|
||||||
self.dc_id = None
|
self.dc_id = None
|
||||||
self.auth_key = None
|
self.auth_key = None
|
||||||
self.user_id = None
|
self.user_id = None
|
||||||
@ -117,7 +119,8 @@ class BaseClient:
|
|||||||
def get_messages(
|
def get_messages(
|
||||||
self,
|
self,
|
||||||
chat_id: int or str,
|
chat_id: int or str,
|
||||||
message_ids,
|
message_ids: int or list = None,
|
||||||
|
reply_to_message_ids: int or list = None,
|
||||||
replies: int = 1
|
replies: int = 1
|
||||||
):
|
):
|
||||||
pass
|
pass
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -16,200 +16,9 @@
|
|||||||
# 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 base64 import b64decode, b64encode
|
from base64 import b64decode, b64encode
|
||||||
from struct import pack
|
|
||||||
from weakref import proxy
|
|
||||||
|
|
||||||
from pyrogram.api.errors import FloodWait
|
from ...api import types
|
||||||
from pyrogram.client import types as pyrogram_types
|
|
||||||
from ...api import types, functions
|
|
||||||
from ...api.errors import StickersetInvalid
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
# TODO: Organize the code better?
|
|
||||||
|
|
||||||
class Str(str):
|
|
||||||
__slots__ = "_client", "_entities"
|
|
||||||
|
|
||||||
def __init__(self, *args):
|
|
||||||
super().__init__()
|
|
||||||
self._client = None
|
|
||||||
self._entities = None
|
|
||||||
|
|
||||||
def init(self, client, entities):
|
|
||||||
self._client = client
|
|
||||||
self._entities = entities
|
|
||||||
|
|
||||||
@property
|
|
||||||
def text(self):
|
|
||||||
return self
|
|
||||||
|
|
||||||
@property
|
|
||||||
def markdown(self):
|
|
||||||
return self._client.markdown.unparse(self, self._entities)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def html(self):
|
|
||||||
return self._client.html.unparse(self, self._entities)
|
|
||||||
|
|
||||||
|
|
||||||
ENTITIES = {
|
|
||||||
types.MessageEntityMention.ID: "mention",
|
|
||||||
types.MessageEntityHashtag.ID: "hashtag",
|
|
||||||
types.MessageEntityBotCommand.ID: "bot_command",
|
|
||||||
types.MessageEntityUrl.ID: "url",
|
|
||||||
types.MessageEntityEmail.ID: "email",
|
|
||||||
types.MessageEntityBold.ID: "bold",
|
|
||||||
types.MessageEntityItalic.ID: "italic",
|
|
||||||
types.MessageEntityCode.ID: "code",
|
|
||||||
types.MessageEntityPre.ID: "pre",
|
|
||||||
types.MessageEntityTextUrl.ID: "text_link",
|
|
||||||
types.MessageEntityMentionName.ID: "text_mention"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def parse_entities(entities: list, users: dict) -> list:
|
|
||||||
output_entities = []
|
|
||||||
|
|
||||||
for entity in entities:
|
|
||||||
entity_type = ENTITIES.get(entity.ID, None)
|
|
||||||
|
|
||||||
if entity_type:
|
|
||||||
output_entities.append(pyrogram_types.MessageEntity(
|
|
||||||
type=entity_type,
|
|
||||||
offset=entity.offset,
|
|
||||||
length=entity.length,
|
|
||||||
url=getattr(entity, "url", None),
|
|
||||||
user=parse_user(
|
|
||||||
users.get(
|
|
||||||
getattr(entity, "user_id", None),
|
|
||||||
None
|
|
||||||
)
|
|
||||||
)
|
|
||||||
))
|
|
||||||
|
|
||||||
return output_entities
|
|
||||||
|
|
||||||
|
|
||||||
def parse_chat_photo(photo):
|
|
||||||
if not isinstance(photo, (types.UserProfilePhoto, types.ChatPhoto)):
|
|
||||||
return None
|
|
||||||
|
|
||||||
if not isinstance(photo.photo_small, types.FileLocation):
|
|
||||||
return None
|
|
||||||
|
|
||||||
if not isinstance(photo.photo_big, types.FileLocation):
|
|
||||||
return None
|
|
||||||
|
|
||||||
photo_id = getattr(photo, "photo_id", 0)
|
|
||||||
loc_small = photo.photo_small
|
|
||||||
loc_big = photo.photo_big
|
|
||||||
|
|
||||||
return pyrogram_types.ChatPhoto(
|
|
||||||
small_file_id=encode(
|
|
||||||
pack(
|
|
||||||
"<iiqqqqi", 1, loc_small.dc_id, photo_id, 0, loc_small.volume_id,
|
|
||||||
loc_small.secret, loc_small.local_id
|
|
||||||
)
|
|
||||||
),
|
|
||||||
big_file_id=encode(
|
|
||||||
pack(
|
|
||||||
"<iiqqqqi", 1, loc_big.dc_id, photo_id, 0, loc_big.volume_id,
|
|
||||||
loc_big.secret, loc_big.local_id
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def parse_user(user: types.User) -> pyrogram_types.User or None:
|
|
||||||
return pyrogram_types.User(
|
|
||||||
id=user.id,
|
|
||||||
is_bot=user.bot,
|
|
||||||
first_name=user.first_name,
|
|
||||||
last_name=user.last_name,
|
|
||||||
username=user.username,
|
|
||||||
language_code=user.lang_code,
|
|
||||||
phone_number=user.phone,
|
|
||||||
photo=parse_chat_photo(user.photo)
|
|
||||||
) if user else None
|
|
||||||
|
|
||||||
|
|
||||||
def parse_chat(message: types.Message, users: dict, chats: dict) -> pyrogram_types.Chat:
|
|
||||||
if isinstance(message.to_id, types.PeerUser):
|
|
||||||
return parse_user_chat(users[message.to_id.user_id if message.out else message.from_id])
|
|
||||||
elif isinstance(message.to_id, types.PeerChat):
|
|
||||||
return parse_chat_chat(chats[message.to_id.chat_id])
|
|
||||||
else:
|
|
||||||
return parse_channel_chat(chats[message.to_id.channel_id])
|
|
||||||
|
|
||||||
|
|
||||||
def parse_user_chat(user: types.User) -> pyrogram_types.Chat:
|
|
||||||
return pyrogram_types.Chat(
|
|
||||||
id=user.id,
|
|
||||||
type="private",
|
|
||||||
username=user.username,
|
|
||||||
first_name=user.first_name,
|
|
||||||
last_name=user.last_name,
|
|
||||||
photo=parse_chat_photo(user.photo)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def parse_chat_chat(chat: types.Chat) -> pyrogram_types.Chat:
|
|
||||||
admins_enabled = getattr(chat, "admins_enabled", None)
|
|
||||||
|
|
||||||
if admins_enabled is not None:
|
|
||||||
admins_enabled = not admins_enabled
|
|
||||||
|
|
||||||
return pyrogram_types.Chat(
|
|
||||||
id=-chat.id,
|
|
||||||
type="group",
|
|
||||||
title=chat.title,
|
|
||||||
all_members_are_administrators=admins_enabled,
|
|
||||||
photo=parse_chat_photo(getattr(chat, "photo", None))
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def parse_channel_chat(channel: types.Channel) -> pyrogram_types.Chat:
|
|
||||||
return pyrogram_types.Chat(
|
|
||||||
id=int("-100" + str(channel.id)),
|
|
||||||
type="supergroup" if channel.megagroup else "channel",
|
|
||||||
title=channel.title,
|
|
||||||
username=getattr(channel, "username", None),
|
|
||||||
photo=parse_chat_photo(getattr(channel, "photo", None))
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def parse_thumb(thumb: types.PhotoSize or types.PhotoCachedSize) -> pyrogram_types.PhotoSize or None:
|
|
||||||
if isinstance(thumb, (types.PhotoSize, types.PhotoCachedSize)):
|
|
||||||
loc = thumb.location
|
|
||||||
|
|
||||||
if isinstance(thumb, types.PhotoSize):
|
|
||||||
file_size = thumb.size
|
|
||||||
else:
|
|
||||||
file_size = len(thumb.bytes)
|
|
||||||
|
|
||||||
if isinstance(loc, types.FileLocation):
|
|
||||||
return pyrogram_types.PhotoSize(
|
|
||||||
file_id=encode(
|
|
||||||
pack(
|
|
||||||
"<iiqqqqi",
|
|
||||||
0,
|
|
||||||
loc.dc_id,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
loc.volume_id,
|
|
||||||
loc.secret,
|
|
||||||
loc.local_id
|
|
||||||
)
|
|
||||||
),
|
|
||||||
width=thumb.w,
|
|
||||||
height=thumb.h,
|
|
||||||
file_size=file_size
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def decode(s: str) -> bytes:
|
def decode(s: str) -> bytes:
|
||||||
@ -248,508 +57,6 @@ def encode(s: bytes) -> str:
|
|||||||
return b64encode(r, b"-_").decode().rstrip("=")
|
return b64encode(r, b"-_").decode().rstrip("=")
|
||||||
|
|
||||||
|
|
||||||
# TODO: Reorganize code, maybe split parts as well
|
|
||||||
def parse_messages(
|
|
||||||
client,
|
|
||||||
messages: list or types.Message or types.MessageService or types.MessageEmpty,
|
|
||||||
users: dict,
|
|
||||||
chats: dict,
|
|
||||||
replies: int = 1
|
|
||||||
) -> pyrogram_types.Message or list:
|
|
||||||
is_list = isinstance(messages, list)
|
|
||||||
messages = messages if is_list else [messages]
|
|
||||||
parsed_messages = []
|
|
||||||
|
|
||||||
for message in messages:
|
|
||||||
if isinstance(message, types.Message):
|
|
||||||
entities = parse_entities(message.entities, users)
|
|
||||||
|
|
||||||
forward_from = None
|
|
||||||
forward_from_chat = None
|
|
||||||
forward_from_message_id = None
|
|
||||||
forward_signature = None
|
|
||||||
forward_date = None
|
|
||||||
|
|
||||||
forward_header = message.fwd_from # type: types.MessageFwdHeader
|
|
||||||
|
|
||||||
if forward_header:
|
|
||||||
forward_date = forward_header.date
|
|
||||||
|
|
||||||
if forward_header.from_id:
|
|
||||||
forward_from = parse_user(users[forward_header.from_id])
|
|
||||||
else:
|
|
||||||
forward_from_chat = parse_channel_chat(chats[forward_header.channel_id])
|
|
||||||
forward_from_message_id = forward_header.channel_post
|
|
||||||
forward_signature = forward_header.post_author
|
|
||||||
|
|
||||||
photo = None
|
|
||||||
location = None
|
|
||||||
contact = None
|
|
||||||
venue = None
|
|
||||||
audio = None
|
|
||||||
voice = None
|
|
||||||
gif = None
|
|
||||||
video = None
|
|
||||||
video_note = None
|
|
||||||
sticker = None
|
|
||||||
document = None
|
|
||||||
|
|
||||||
media = message.media
|
|
||||||
|
|
||||||
if media:
|
|
||||||
if isinstance(media, types.MessageMediaPhoto):
|
|
||||||
photo = media.photo
|
|
||||||
|
|
||||||
if isinstance(photo, types.Photo):
|
|
||||||
sizes = photo.sizes
|
|
||||||
photo_sizes = []
|
|
||||||
|
|
||||||
for size in sizes:
|
|
||||||
if isinstance(size, (types.PhotoSize, types.PhotoCachedSize)):
|
|
||||||
loc = size.location
|
|
||||||
|
|
||||||
if isinstance(size, types.PhotoSize):
|
|
||||||
file_size = size.size
|
|
||||||
else:
|
|
||||||
file_size = len(size.bytes)
|
|
||||||
|
|
||||||
if isinstance(loc, types.FileLocation):
|
|
||||||
photo_size = pyrogram_types.PhotoSize(
|
|
||||||
file_id=encode(
|
|
||||||
pack(
|
|
||||||
"<iiqqqqi",
|
|
||||||
2,
|
|
||||||
loc.dc_id,
|
|
||||||
photo.id,
|
|
||||||
photo.access_hash,
|
|
||||||
loc.volume_id,
|
|
||||||
loc.secret,
|
|
||||||
loc.local_id
|
|
||||||
)
|
|
||||||
),
|
|
||||||
width=size.w,
|
|
||||||
height=size.h,
|
|
||||||
file_size=file_size
|
|
||||||
)
|
|
||||||
|
|
||||||
photo_sizes.append(photo_size)
|
|
||||||
|
|
||||||
photo = pyrogram_types.Photo(
|
|
||||||
id=b64encode(
|
|
||||||
pack(
|
|
||||||
"<qq",
|
|
||||||
photo.id,
|
|
||||||
photo.access_hash
|
|
||||||
),
|
|
||||||
b"-_"
|
|
||||||
).decode().rstrip("="),
|
|
||||||
date=photo.date,
|
|
||||||
sizes=photo_sizes
|
|
||||||
)
|
|
||||||
elif isinstance(media, types.MessageMediaGeo):
|
|
||||||
geo_point = media.geo
|
|
||||||
|
|
||||||
if isinstance(geo_point, types.GeoPoint):
|
|
||||||
location = pyrogram_types.Location(
|
|
||||||
longitude=geo_point.long,
|
|
||||||
latitude=geo_point.lat
|
|
||||||
)
|
|
||||||
elif isinstance(media, types.MessageMediaContact):
|
|
||||||
contact = pyrogram_types.Contact(
|
|
||||||
phone_number=media.phone_number,
|
|
||||||
first_name=media.first_name,
|
|
||||||
last_name=media.last_name or None,
|
|
||||||
user_id=media.user_id or None
|
|
||||||
)
|
|
||||||
elif isinstance(media, types.MessageMediaVenue):
|
|
||||||
venue = pyrogram_types.Venue(
|
|
||||||
location=pyrogram_types.Location(
|
|
||||||
longitude=media.geo.long,
|
|
||||||
latitude=media.geo.lat
|
|
||||||
),
|
|
||||||
title=media.title,
|
|
||||||
address=media.address,
|
|
||||||
foursquare_id=media.venue_id or None
|
|
||||||
)
|
|
||||||
elif isinstance(media, types.MessageMediaDocument):
|
|
||||||
doc = media.document
|
|
||||||
|
|
||||||
if isinstance(doc, types.Document):
|
|
||||||
attributes = {type(i): i for i in doc.attributes}
|
|
||||||
|
|
||||||
file_name = getattr(
|
|
||||||
attributes.get(
|
|
||||||
types.DocumentAttributeFilename, None
|
|
||||||
), "file_name", None
|
|
||||||
)
|
|
||||||
|
|
||||||
if types.DocumentAttributeAudio in attributes:
|
|
||||||
audio_attributes = attributes[types.DocumentAttributeAudio]
|
|
||||||
|
|
||||||
if audio_attributes.voice:
|
|
||||||
voice = pyrogram_types.Voice(
|
|
||||||
file_id=encode(
|
|
||||||
pack(
|
|
||||||
"<iiqq",
|
|
||||||
3,
|
|
||||||
doc.dc_id,
|
|
||||||
doc.id,
|
|
||||||
doc.access_hash
|
|
||||||
)
|
|
||||||
),
|
|
||||||
duration=audio_attributes.duration,
|
|
||||||
mime_type=doc.mime_type,
|
|
||||||
file_size=doc.size,
|
|
||||||
thumb=parse_thumb(doc.thumb),
|
|
||||||
file_name=file_name,
|
|
||||||
date=doc.date
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
audio = pyrogram_types.Audio(
|
|
||||||
file_id=encode(
|
|
||||||
pack(
|
|
||||||
"<iiqq",
|
|
||||||
9,
|
|
||||||
doc.dc_id,
|
|
||||||
doc.id,
|
|
||||||
doc.access_hash
|
|
||||||
)
|
|
||||||
),
|
|
||||||
duration=audio_attributes.duration,
|
|
||||||
performer=audio_attributes.performer,
|
|
||||||
title=audio_attributes.title,
|
|
||||||
mime_type=doc.mime_type,
|
|
||||||
file_size=doc.size,
|
|
||||||
thumb=parse_thumb(doc.thumb),
|
|
||||||
file_name=file_name,
|
|
||||||
date=doc.date
|
|
||||||
)
|
|
||||||
elif types.DocumentAttributeAnimated in attributes:
|
|
||||||
video_attributes = attributes.get(types.DocumentAttributeVideo, None)
|
|
||||||
|
|
||||||
gif = pyrogram_types.GIF(
|
|
||||||
file_id=encode(
|
|
||||||
pack(
|
|
||||||
"<iiqq",
|
|
||||||
10,
|
|
||||||
doc.dc_id,
|
|
||||||
doc.id,
|
|
||||||
doc.access_hash
|
|
||||||
)
|
|
||||||
),
|
|
||||||
width=getattr(video_attributes, "w", 0),
|
|
||||||
height=getattr(video_attributes, "h", 0),
|
|
||||||
duration=getattr(video_attributes, "duration", 0),
|
|
||||||
thumb=parse_thumb(doc.thumb),
|
|
||||||
mime_type=doc.mime_type,
|
|
||||||
file_size=doc.size,
|
|
||||||
file_name=file_name,
|
|
||||||
date=doc.date
|
|
||||||
)
|
|
||||||
elif types.DocumentAttributeVideo in attributes:
|
|
||||||
video_attributes = attributes[types.DocumentAttributeVideo]
|
|
||||||
|
|
||||||
if video_attributes.round_message:
|
|
||||||
video_note = pyrogram_types.VideoNote(
|
|
||||||
file_id=encode(
|
|
||||||
pack(
|
|
||||||
"<iiqq",
|
|
||||||
13,
|
|
||||||
doc.dc_id,
|
|
||||||
doc.id,
|
|
||||||
doc.access_hash
|
|
||||||
)
|
|
||||||
),
|
|
||||||
length=video_attributes.w,
|
|
||||||
duration=video_attributes.duration,
|
|
||||||
thumb=parse_thumb(doc.thumb),
|
|
||||||
file_size=doc.size,
|
|
||||||
file_name=file_name,
|
|
||||||
mime_type=doc.mime_type,
|
|
||||||
date=doc.date
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
video = pyrogram_types.Video(
|
|
||||||
file_id=encode(
|
|
||||||
pack(
|
|
||||||
"<iiqq",
|
|
||||||
4,
|
|
||||||
doc.dc_id,
|
|
||||||
doc.id,
|
|
||||||
doc.access_hash
|
|
||||||
)
|
|
||||||
),
|
|
||||||
width=video_attributes.w,
|
|
||||||
height=video_attributes.h,
|
|
||||||
duration=video_attributes.duration,
|
|
||||||
thumb=parse_thumb(doc.thumb),
|
|
||||||
mime_type=doc.mime_type,
|
|
||||||
file_size=doc.size,
|
|
||||||
file_name=file_name,
|
|
||||||
date=doc.date
|
|
||||||
)
|
|
||||||
elif types.DocumentAttributeSticker in attributes:
|
|
||||||
image_size_attributes = attributes.get(types.DocumentAttributeImageSize, None)
|
|
||||||
sticker_attribute = attributes[types.DocumentAttributeSticker]
|
|
||||||
|
|
||||||
if isinstance(sticker_attribute.stickerset, types.InputStickerSetID):
|
|
||||||
try:
|
|
||||||
set_name = client.send(
|
|
||||||
functions.messages.GetStickerSet(sticker_attribute.stickerset)
|
|
||||||
).set.short_name
|
|
||||||
except StickersetInvalid:
|
|
||||||
set_name = None
|
|
||||||
else:
|
|
||||||
set_name = None
|
|
||||||
|
|
||||||
sticker = pyrogram_types.Sticker(
|
|
||||||
file_id=encode(
|
|
||||||
pack(
|
|
||||||
"<iiqq",
|
|
||||||
8,
|
|
||||||
doc.dc_id,
|
|
||||||
doc.id,
|
|
||||||
doc.access_hash
|
|
||||||
)
|
|
||||||
),
|
|
||||||
width=image_size_attributes.w if image_size_attributes else 0,
|
|
||||||
height=image_size_attributes.h if image_size_attributes else 0,
|
|
||||||
thumb=parse_thumb(doc.thumb),
|
|
||||||
# TODO: mask_position
|
|
||||||
set_name=set_name,
|
|
||||||
emoji=sticker_attribute.alt or None,
|
|
||||||
file_size=doc.size,
|
|
||||||
mime_type=doc.mime_type,
|
|
||||||
file_name=file_name,
|
|
||||||
date=doc.date
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
document = pyrogram_types.Document(
|
|
||||||
file_id=encode(
|
|
||||||
pack(
|
|
||||||
"<iiqq",
|
|
||||||
5,
|
|
||||||
doc.dc_id,
|
|
||||||
doc.id,
|
|
||||||
doc.access_hash
|
|
||||||
)
|
|
||||||
),
|
|
||||||
thumb=parse_thumb(doc.thumb),
|
|
||||||
file_name=file_name,
|
|
||||||
mime_type=doc.mime_type,
|
|
||||||
file_size=doc.size,
|
|
||||||
date=doc.date
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
media = None
|
|
||||||
|
|
||||||
reply_markup = message.reply_markup
|
|
||||||
|
|
||||||
if reply_markup:
|
|
||||||
if isinstance(reply_markup, types.ReplyKeyboardForceReply):
|
|
||||||
reply_markup = pyrogram_types.ForceReply.read(reply_markup)
|
|
||||||
elif isinstance(reply_markup, types.ReplyKeyboardMarkup):
|
|
||||||
reply_markup = pyrogram_types.ReplyKeyboardMarkup.read(reply_markup)
|
|
||||||
elif isinstance(reply_markup, types.ReplyInlineMarkup):
|
|
||||||
reply_markup = pyrogram_types.InlineKeyboardMarkup.read(reply_markup)
|
|
||||||
elif isinstance(reply_markup, types.ReplyKeyboardHide):
|
|
||||||
reply_markup = pyrogram_types.ReplyKeyboardRemove.read(reply_markup)
|
|
||||||
else:
|
|
||||||
reply_markup = None
|
|
||||||
|
|
||||||
m = pyrogram_types.Message(
|
|
||||||
message_id=message.id,
|
|
||||||
date=message.date,
|
|
||||||
chat=parse_chat(message, users, chats),
|
|
||||||
from_user=parse_user(users.get(message.from_id, None)),
|
|
||||||
text=Str(message.message) or None if media is None else None,
|
|
||||||
caption=Str(message.message) or None if media is not None else None,
|
|
||||||
entities=entities or None if media is None else None,
|
|
||||||
caption_entities=entities or None if media is not None else None,
|
|
||||||
author_signature=message.post_author,
|
|
||||||
forward_from=forward_from,
|
|
||||||
forward_from_chat=forward_from_chat,
|
|
||||||
forward_from_message_id=forward_from_message_id,
|
|
||||||
forward_signature=forward_signature,
|
|
||||||
forward_date=forward_date,
|
|
||||||
edit_date=message.edit_date,
|
|
||||||
media_group_id=message.grouped_id,
|
|
||||||
photo=photo,
|
|
||||||
location=location,
|
|
||||||
contact=contact,
|
|
||||||
venue=venue,
|
|
||||||
audio=audio,
|
|
||||||
voice=voice,
|
|
||||||
gif=gif,
|
|
||||||
video=video,
|
|
||||||
video_note=video_note,
|
|
||||||
sticker=sticker,
|
|
||||||
document=document,
|
|
||||||
views=message.views,
|
|
||||||
via_bot=parse_user(users.get(message.via_bot_id, None)),
|
|
||||||
outgoing=message.out,
|
|
||||||
client=proxy(client),
|
|
||||||
reply_markup=reply_markup
|
|
||||||
)
|
|
||||||
|
|
||||||
if m.text:
|
|
||||||
m.text.init(m._client, m.entities or [])
|
|
||||||
|
|
||||||
if m.caption:
|
|
||||||
m.caption.init(m._client, m.caption_entities or [])
|
|
||||||
|
|
||||||
if message.reply_to_msg_id and replies:
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
m.reply_to_message = client.get_messages(
|
|
||||||
m.chat.id, message.reply_to_msg_id,
|
|
||||||
replies=replies - 1
|
|
||||||
)
|
|
||||||
except FloodWait as e:
|
|
||||||
log.warning("get_messages flood: waiting {} seconds".format(e.x))
|
|
||||||
time.sleep(e.x)
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
break
|
|
||||||
elif isinstance(message, types.MessageService):
|
|
||||||
action = message.action
|
|
||||||
|
|
||||||
new_chat_members = None
|
|
||||||
left_chat_member = None
|
|
||||||
new_chat_title = None
|
|
||||||
delete_chat_photo = None
|
|
||||||
migrate_to_chat_id = None
|
|
||||||
migrate_from_chat_id = None
|
|
||||||
group_chat_created = None
|
|
||||||
channel_chat_created = None
|
|
||||||
new_chat_photo = None
|
|
||||||
|
|
||||||
if isinstance(action, types.MessageActionChatAddUser):
|
|
||||||
new_chat_members = [parse_user(users[i]) for i in action.users]
|
|
||||||
elif isinstance(action, types.MessageActionChatJoinedByLink):
|
|
||||||
new_chat_members = [parse_user(users[message.from_id])]
|
|
||||||
elif isinstance(action, types.MessageActionChatDeleteUser):
|
|
||||||
left_chat_member = parse_user(users[action.user_id])
|
|
||||||
elif isinstance(action, types.MessageActionChatEditTitle):
|
|
||||||
new_chat_title = action.title
|
|
||||||
elif isinstance(action, types.MessageActionChatDeletePhoto):
|
|
||||||
delete_chat_photo = True
|
|
||||||
elif isinstance(action, types.MessageActionChatMigrateTo):
|
|
||||||
migrate_to_chat_id = action.channel_id
|
|
||||||
elif isinstance(action, types.MessageActionChannelMigrateFrom):
|
|
||||||
migrate_from_chat_id = action.chat_id
|
|
||||||
elif isinstance(action, types.MessageActionChatCreate):
|
|
||||||
group_chat_created = True
|
|
||||||
elif isinstance(action, types.MessageActionChannelCreate):
|
|
||||||
channel_chat_created = True
|
|
||||||
elif isinstance(action, types.MessageActionChatEditPhoto):
|
|
||||||
photo = action.photo
|
|
||||||
|
|
||||||
if isinstance(photo, types.Photo):
|
|
||||||
sizes = photo.sizes
|
|
||||||
photo_sizes = []
|
|
||||||
|
|
||||||
for size in sizes:
|
|
||||||
if isinstance(size, (types.PhotoSize, types.PhotoCachedSize)):
|
|
||||||
loc = size.location
|
|
||||||
|
|
||||||
if isinstance(size, types.PhotoSize):
|
|
||||||
file_size = size.size
|
|
||||||
else:
|
|
||||||
file_size = len(size.bytes)
|
|
||||||
|
|
||||||
if isinstance(loc, types.FileLocation):
|
|
||||||
photo_size = pyrogram_types.PhotoSize(
|
|
||||||
file_id=encode(
|
|
||||||
pack(
|
|
||||||
"<iiqqqqi",
|
|
||||||
2,
|
|
||||||
loc.dc_id,
|
|
||||||
photo.id,
|
|
||||||
photo.access_hash,
|
|
||||||
loc.volume_id,
|
|
||||||
loc.secret,
|
|
||||||
loc.local_id
|
|
||||||
)
|
|
||||||
),
|
|
||||||
width=size.w,
|
|
||||||
height=size.h,
|
|
||||||
file_size=file_size
|
|
||||||
)
|
|
||||||
|
|
||||||
photo_sizes.append(photo_size)
|
|
||||||
|
|
||||||
new_chat_photo = pyrogram_types.Photo(
|
|
||||||
id=b64encode(
|
|
||||||
pack(
|
|
||||||
"<qq",
|
|
||||||
photo.id,
|
|
||||||
photo.access_hash
|
|
||||||
),
|
|
||||||
b"-_"
|
|
||||||
).decode().rstrip("="),
|
|
||||||
date=photo.date,
|
|
||||||
sizes=photo_sizes
|
|
||||||
)
|
|
||||||
|
|
||||||
m = pyrogram_types.Message(
|
|
||||||
message_id=message.id,
|
|
||||||
date=message.date,
|
|
||||||
chat=parse_chat(message, users, chats),
|
|
||||||
from_user=parse_user(users.get(message.from_id, None)),
|
|
||||||
new_chat_members=new_chat_members,
|
|
||||||
left_chat_member=left_chat_member,
|
|
||||||
new_chat_title=new_chat_title,
|
|
||||||
new_chat_photo=new_chat_photo,
|
|
||||||
delete_chat_photo=delete_chat_photo,
|
|
||||||
migrate_to_chat_id=int("-100" + str(migrate_to_chat_id)) if migrate_to_chat_id else None,
|
|
||||||
migrate_from_chat_id=-migrate_from_chat_id if migrate_from_chat_id else None,
|
|
||||||
group_chat_created=group_chat_created,
|
|
||||||
channel_chat_created=channel_chat_created,
|
|
||||||
client=proxy(client)
|
|
||||||
# TODO: supergroup_chat_created
|
|
||||||
)
|
|
||||||
|
|
||||||
if isinstance(action, types.MessageActionPinMessage):
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
m.pinned_message = client.get_messages(
|
|
||||||
m.chat.id, message.reply_to_msg_id,
|
|
||||||
replies=0
|
|
||||||
)
|
|
||||||
except FloodWait as e:
|
|
||||||
log.warning("get_messages flood: waiting {} seconds".format(e.x))
|
|
||||||
time.sleep(e.x)
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
m = pyrogram_types.Message(message_id=message.id, client=proxy(client))
|
|
||||||
|
|
||||||
parsed_messages.append(m)
|
|
||||||
|
|
||||||
return parsed_messages if is_list else parsed_messages[0]
|
|
||||||
|
|
||||||
|
|
||||||
def parse_deleted_messages(
|
|
||||||
messages: list,
|
|
||||||
channel_id: int
|
|
||||||
) -> pyrogram_types.Messages:
|
|
||||||
parsed_messages = []
|
|
||||||
|
|
||||||
for message in messages:
|
|
||||||
parsed_messages.append(
|
|
||||||
pyrogram_types.Message(
|
|
||||||
message_id=message,
|
|
||||||
chat=(pyrogram_types.Chat(id=int("-100" + str(channel_id)), type="channel")
|
|
||||||
if channel_id is not None
|
|
||||||
else None)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
return pyrogram_types.Messages(len(parsed_messages), parsed_messages)
|
|
||||||
|
|
||||||
|
|
||||||
def get_peer_id(input_peer) -> int:
|
def get_peer_id(input_peer) -> int:
|
||||||
return (
|
return (
|
||||||
input_peer.user_id if isinstance(input_peer, types.InputPeerUser)
|
input_peer.user_id if isinstance(input_peer, types.InputPeerUser)
|
||||||
@ -775,141 +82,3 @@ def get_offset_date(dialogs):
|
|||||||
return m.date
|
return m.date
|
||||||
else:
|
else:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
def parse_profile_photos(photos):
|
|
||||||
if isinstance(photos, types.photos.Photos):
|
|
||||||
total_count = len(photos.photos)
|
|
||||||
else:
|
|
||||||
total_count = photos.count
|
|
||||||
|
|
||||||
user_profile_photos = []
|
|
||||||
|
|
||||||
for photo in photos.photos:
|
|
||||||
if isinstance(photo, types.Photo):
|
|
||||||
sizes = photo.sizes
|
|
||||||
photo_sizes = []
|
|
||||||
|
|
||||||
for size in sizes:
|
|
||||||
if isinstance(size, (types.PhotoSize, types.PhotoCachedSize)):
|
|
||||||
loc = size.location
|
|
||||||
|
|
||||||
if isinstance(size, types.PhotoSize):
|
|
||||||
file_size = size.size
|
|
||||||
else:
|
|
||||||
file_size = len(size.bytes)
|
|
||||||
|
|
||||||
if isinstance(loc, types.FileLocation):
|
|
||||||
photo_size = pyrogram_types.PhotoSize(
|
|
||||||
file_id=encode(
|
|
||||||
pack(
|
|
||||||
"<iiqqqqi",
|
|
||||||
2,
|
|
||||||
loc.dc_id,
|
|
||||||
photo.id,
|
|
||||||
photo.access_hash,
|
|
||||||
loc.volume_id,
|
|
||||||
loc.secret,
|
|
||||||
loc.local_id
|
|
||||||
)
|
|
||||||
),
|
|
||||||
width=size.w,
|
|
||||||
height=size.h,
|
|
||||||
file_size=file_size
|
|
||||||
)
|
|
||||||
|
|
||||||
photo_sizes.append(photo_size)
|
|
||||||
|
|
||||||
user_profile_photos.append(
|
|
||||||
pyrogram_types.Photo(
|
|
||||||
id=b64encode(
|
|
||||||
pack(
|
|
||||||
"<qq",
|
|
||||||
photo.id,
|
|
||||||
photo.access_hash
|
|
||||||
),
|
|
||||||
b"-_"
|
|
||||||
).decode().rstrip("="),
|
|
||||||
date=photo.date,
|
|
||||||
sizes=photo_sizes
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
return pyrogram_types.UserProfilePhotos(
|
|
||||||
total_count=total_count,
|
|
||||||
photos=user_profile_photos
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def parse_callback_query(client, callback_query, users):
|
|
||||||
peer = callback_query.peer
|
|
||||||
|
|
||||||
if isinstance(peer, types.PeerUser):
|
|
||||||
peer_id = peer.user_id
|
|
||||||
elif isinstance(peer, types.PeerChat):
|
|
||||||
peer_id = -peer.chat_id
|
|
||||||
else:
|
|
||||||
peer_id = int("-100" + str(peer.channel_id))
|
|
||||||
|
|
||||||
return pyrogram_types.CallbackQuery(
|
|
||||||
id=str(callback_query.query_id),
|
|
||||||
from_user=parse_user(users[callback_query.user_id]),
|
|
||||||
message=client.get_messages(peer_id, callback_query.msg_id),
|
|
||||||
chat_instance=str(callback_query.chat_instance),
|
|
||||||
data=callback_query.data.decode(),
|
|
||||||
game_short_name=callback_query.game_short_name
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def parse_inline_callback_query(callback_query, users):
|
|
||||||
return pyrogram_types.CallbackQuery(
|
|
||||||
id=str(callback_query.query_id),
|
|
||||||
from_user=parse_user(users[callback_query.user_id]),
|
|
||||||
chat_instance=str(callback_query.chat_instance),
|
|
||||||
inline_message_id=b64encode(
|
|
||||||
pack(
|
|
||||||
"<iqq",
|
|
||||||
callback_query.msg_id.dc_id,
|
|
||||||
callback_query.msg_id.id,
|
|
||||||
callback_query.msg_id.access_hash
|
|
||||||
),
|
|
||||||
b"-_"
|
|
||||||
).decode().rstrip("="),
|
|
||||||
game_short_name=callback_query.game_short_name
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def parse_chat_full(
|
|
||||||
client,
|
|
||||||
chat_full: types.messages.ChatFull or types.UserFull
|
|
||||||
) -> pyrogram_types.Chat:
|
|
||||||
if isinstance(chat_full, types.UserFull):
|
|
||||||
chat = parse_user_chat(chat_full.user)
|
|
||||||
chat.description = chat_full.about
|
|
||||||
else:
|
|
||||||
full_chat = chat_full.full_chat
|
|
||||||
chat = None
|
|
||||||
|
|
||||||
for i in chat_full.chats:
|
|
||||||
if full_chat.id == i.id:
|
|
||||||
chat = i
|
|
||||||
|
|
||||||
if isinstance(full_chat, types.ChatFull):
|
|
||||||
chat = parse_chat_chat(chat)
|
|
||||||
else:
|
|
||||||
chat = parse_channel_chat(chat)
|
|
||||||
chat.description = full_chat.about or None
|
|
||||||
# TODO: Add StickerSet type
|
|
||||||
chat.can_set_sticker_set = full_chat.can_set_stickers
|
|
||||||
chat.sticker_set_name = full_chat.stickerset
|
|
||||||
|
|
||||||
if full_chat.pinned_msg_id:
|
|
||||||
chat.pinned_message = client.get_messages(
|
|
||||||
int("-100" + str(full_chat.id)),
|
|
||||||
full_chat.pinned_msg_id
|
|
||||||
)
|
|
||||||
|
|
||||||
if isinstance(full_chat.exported_invite, types.ChatInviteExported):
|
|
||||||
chat.invite_link = full_chat.exported_invite.link
|
|
||||||
|
|
||||||
return chat
|
|
||||||
|
@ -19,10 +19,32 @@
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from .filter import Filter
|
from .filter import Filter
|
||||||
from ..types.reply_markup import InlineKeyboardMarkup, ReplyKeyboardMarkup
|
from ..types.bots import InlineKeyboardMarkup, ReplyKeyboardMarkup
|
||||||
|
|
||||||
|
|
||||||
def build(name: str, func: callable, **kwargs) -> type:
|
def create(name: str, func: callable, **kwargs) -> type:
|
||||||
|
"""Use this method to create a Filter.
|
||||||
|
|
||||||
|
Custom filters give you extra control over which updates are allowed or not to be processed by your handlers.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name (``str``):
|
||||||
|
Your filter's name. Can be anything you like.
|
||||||
|
|
||||||
|
func (``callable``):
|
||||||
|
A function that accepts two arguments *(filter, update)* and returns a Boolean: True if the update should be
|
||||||
|
handled, False otherwise.
|
||||||
|
The "update" argument type will vary depending on which `Handler <Handlers.html>`_ is coming from.
|
||||||
|
For example, in a :obj:`MessageHandler <pyrogram.MessageHandler>` the update type will be
|
||||||
|
a :obj:`Message <pyrogram.Message>`; in a :obj:`CallbackQueryHandler <pyrogram.CallbackQueryHandler>` the
|
||||||
|
update type will be a :obj:`CallbackQuery <pyrogram.CallbackQuery>`. Your function body can then access the
|
||||||
|
incoming update and decide whether to allow it or not.
|
||||||
|
|
||||||
|
**kwargs (``any``, *optional*):
|
||||||
|
Any keyword argument you would like to pass. Useful for custom filters that accept parameters (e.g.:
|
||||||
|
:meth:`Filters.command`, :meth:`Filters.regex`).
|
||||||
|
"""
|
||||||
|
# TODO: unpack kwargs using **kwargs into the dict itself. For Python 3.5+ only
|
||||||
d = {"__call__": func}
|
d = {"__call__": func}
|
||||||
d.update(kwargs)
|
d.update(kwargs)
|
||||||
|
|
||||||
@ -30,117 +52,164 @@ def build(name: str, func: callable, **kwargs) -> type:
|
|||||||
|
|
||||||
|
|
||||||
class Filters:
|
class Filters:
|
||||||
"""This class provides access to all Filters available in Pyrogram.
|
"""This class provides access to all library-defined Filters available in Pyrogram.
|
||||||
Filters are intended to be used with the :obj:`MessageHandler <pyrogram.MessageHandler>`."""
|
|
||||||
|
|
||||||
bot = build("Bot", lambda _, m: bool(m.from_user and m.from_user.is_bot))
|
The Filters listed here are intended to be used with the :obj:`MessageHandler <pyrogram.MessageHandler>` only.
|
||||||
|
At the moment, if you want to filter updates coming from different `Handlers <Handlers.html>`_ you have to create
|
||||||
|
your own filters with :meth:`Filters.create` and use them in the same way.
|
||||||
|
"""
|
||||||
|
|
||||||
|
create = create
|
||||||
|
|
||||||
|
bot = create("Bot", lambda _, m: bool(m.from_user and m.from_user.is_bot))
|
||||||
"""Filter messages coming from bots"""
|
"""Filter messages coming from bots"""
|
||||||
|
|
||||||
incoming = build("Incoming", lambda _, m: not m.outgoing)
|
incoming = create("Incoming", lambda _, m: not m.outgoing)
|
||||||
"""Filter incoming messages."""
|
"""Filter incoming messages."""
|
||||||
|
|
||||||
outgoing = build("Outgoing", lambda _, m: m.outgoing)
|
outgoing = create("Outgoing", lambda _, m: m.outgoing)
|
||||||
"""Filter outgoing messages."""
|
"""Filter outgoing messages."""
|
||||||
|
|
||||||
text = build("Text", lambda _, m: bool(m.text))
|
text = create("Text", lambda _, m: bool(m.text))
|
||||||
"""Filter text messages."""
|
"""Filter text messages."""
|
||||||
|
|
||||||
reply = build("Reply", lambda _, m: bool(m.reply_to_message))
|
reply = create("Reply", lambda _, m: bool(m.reply_to_message))
|
||||||
"""Filter messages that are replies to other messages."""
|
"""Filter messages that are replies to other messages."""
|
||||||
|
|
||||||
forwarded = build("Forwarded", lambda _, m: bool(m.forward_date))
|
forwarded = create("Forwarded", lambda _, m: bool(m.forward_date))
|
||||||
"""Filter messages that are forwarded."""
|
"""Filter messages that are forwarded."""
|
||||||
|
|
||||||
caption = build("Caption", lambda _, m: bool(m.caption))
|
caption = create("Caption", lambda _, m: bool(m.caption))
|
||||||
"""Filter media messages that contain captions."""
|
"""Filter media messages that contain captions."""
|
||||||
|
|
||||||
edited = build("Edited", lambda _, m: bool(m.edit_date))
|
edited = create("Edited", lambda _, m: bool(m.edit_date))
|
||||||
"""Filter edited messages."""
|
"""Filter edited messages."""
|
||||||
|
|
||||||
audio = build("Audio", lambda _, m: bool(m.audio))
|
audio = create("Audio", lambda _, m: bool(m.audio))
|
||||||
"""Filter messages that contain :obj:`Audio <pyrogram.api.types.pyrogram.Audio>` objects."""
|
"""Filter messages that contain :obj:`Audio <pyrogram.Audio>` objects."""
|
||||||
|
|
||||||
document = build("Document", lambda _, m: bool(m.document))
|
document = create("Document", lambda _, m: bool(m.document))
|
||||||
"""Filter messages that contain :obj:`Document <pyrogram.api.types.pyrogram.Document>` objects."""
|
"""Filter messages that contain :obj:`Document <pyrogram.Document>` objects."""
|
||||||
|
|
||||||
photo = build("Photo", lambda _, m: bool(m.photo))
|
photo = create("Photo", lambda _, m: bool(m.photo))
|
||||||
"""Filter messages that contain :obj:`Photo <pyrogram.api.types.pyrogram.PhotoSize>` objects."""
|
"""Filter messages that contain :obj:`Photo <pyrogram.PhotoSize>` objects."""
|
||||||
|
|
||||||
sticker = build("Sticker", lambda _, m: bool(m.sticker))
|
sticker = create("Sticker", lambda _, m: bool(m.sticker))
|
||||||
"""Filter messages that contain :obj:`Sticker <pyrogram.api.types.pyrogram.Sticker>` objects."""
|
"""Filter messages that contain :obj:`Sticker <pyrogram.Sticker>` objects."""
|
||||||
|
|
||||||
gif = build("GIF", lambda _, m: bool(m.gif))
|
animation = create("GIF", lambda _, m: bool(m.animation))
|
||||||
"""Filter messages that contain :obj:`GIF <pyrogram.api.types.pyrogram.GIF>` objects."""
|
"""Filter messages that contain :obj:`Animation <pyrogram.Animation>` objects."""
|
||||||
|
|
||||||
video = build("Video", lambda _, m: bool(m.video))
|
video = create("Video", lambda _, m: bool(m.video))
|
||||||
"""Filter messages that contain :obj:`Video <pyrogram.api.types.pyrogram.Video>` objects."""
|
"""Filter messages that contain :obj:`Video <pyrogram.Video>` objects."""
|
||||||
|
|
||||||
voice = build("Voice", lambda _, m: bool(m.voice))
|
voice = create("Voice", lambda _, m: bool(m.voice))
|
||||||
"""Filter messages that contain :obj:`Voice <pyrogram.api.types.pyrogram.Voice>` note objects."""
|
"""Filter messages that contain :obj:`Voice <pyrogram.Voice>` note objects."""
|
||||||
|
|
||||||
video_note = build("Voice", lambda _, m: bool(m.video_note))
|
video_note = create("Voice", lambda _, m: bool(m.video_note))
|
||||||
"""Filter messages that contain :obj:`VideoNote <pyrogram.api.types.pyrogram.VideoNote>` objects."""
|
"""Filter messages that contain :obj:`VideoNote <pyrogram.VideoNote>` objects."""
|
||||||
|
|
||||||
contact = build("Contact", lambda _, m: bool(m.contact))
|
contact = create("Contact", lambda _, m: bool(m.contact))
|
||||||
"""Filter messages that contain :obj:`Contact <pyrogram.api.types.pyrogram.Contact>` objects."""
|
"""Filter messages that contain :obj:`Contact <pyrogram.Contact>` objects."""
|
||||||
|
|
||||||
location = build("Location", lambda _, m: bool(m.location))
|
location = create("Location", lambda _, m: bool(m.location))
|
||||||
"""Filter messages that contain :obj:`Location <pyrogram.api.types.pyrogram.Location>` objects."""
|
"""Filter messages that contain :obj:`Location <pyrogram.Location>` objects."""
|
||||||
|
|
||||||
venue = build("Venue", lambda _, m: bool(m.venue))
|
venue = create("Venue", lambda _, m: bool(m.venue))
|
||||||
"""Filter messages that contain :obj:`Venue <pyrogram.api.types.pyrogram.Venue>` objects."""
|
"""Filter messages that contain :obj:`Venue <pyrogram.Venue>` objects."""
|
||||||
|
|
||||||
private = build("Private", lambda _, m: bool(m.chat and m.chat.type == "private"))
|
web_page = create("WebPage", lambda _, m: m.web_page)
|
||||||
|
"""Filter messages sent with a webpage preview."""
|
||||||
|
|
||||||
|
poll = create("Poll", lambda _, m: m.poll)
|
||||||
|
"""Filter messages that contain :obj:`Poll <pyrogram.Poll>` objects."""
|
||||||
|
|
||||||
|
private = create("Private", lambda _, m: bool(m.chat and m.chat.type == "private"))
|
||||||
"""Filter messages sent in private chats."""
|
"""Filter messages sent in private chats."""
|
||||||
|
|
||||||
group = build("Group", lambda _, m: bool(m.chat and m.chat.type in {"group", "supergroup"}))
|
group = create("Group", lambda _, m: bool(m.chat and m.chat.type in {"group", "supergroup"}))
|
||||||
"""Filter messages sent in group or supergroup chats."""
|
"""Filter messages sent in group or supergroup chats."""
|
||||||
|
|
||||||
channel = build("Channel", lambda _, m: bool(m.chat and m.chat.type == "channel"))
|
channel = create("Channel", lambda _, m: bool(m.chat and m.chat.type == "channel"))
|
||||||
"""Filter messages sent in channels."""
|
"""Filter messages sent in channels."""
|
||||||
|
|
||||||
new_chat_members = build("NewChatMembers", lambda _, m: bool(m.new_chat_members))
|
new_chat_members = create("NewChatMembers", lambda _, m: bool(m.new_chat_members))
|
||||||
"""Filter service messages for new chat members."""
|
"""Filter service messages for new chat members."""
|
||||||
|
|
||||||
left_chat_member = build("LeftChatMember", lambda _, m: bool(m.left_chat_member))
|
left_chat_member = create("LeftChatMember", lambda _, m: bool(m.left_chat_member))
|
||||||
"""Filter service messages for members that left the chat."""
|
"""Filter service messages for members that left the chat."""
|
||||||
|
|
||||||
new_chat_title = build("NewChatTitle", lambda _, m: bool(m.new_chat_title))
|
new_chat_title = create("NewChatTitle", lambda _, m: bool(m.new_chat_title))
|
||||||
"""Filter service messages for new chat titles."""
|
"""Filter service messages for new chat titles."""
|
||||||
|
|
||||||
new_chat_photo = build("NewChatPhoto", lambda _, m: bool(m.new_chat_photo))
|
new_chat_photo = create("NewChatPhoto", lambda _, m: bool(m.new_chat_photo))
|
||||||
"""Filter service messages for new chat photos."""
|
"""Filter service messages for new chat photos."""
|
||||||
|
|
||||||
delete_chat_photo = build("DeleteChatPhoto", lambda _, m: bool(m.delete_chat_photo))
|
delete_chat_photo = create("DeleteChatPhoto", lambda _, m: bool(m.delete_chat_photo))
|
||||||
"""Filter service messages for deleted photos."""
|
"""Filter service messages for deleted photos."""
|
||||||
|
|
||||||
group_chat_created = build("GroupChatCreated", lambda _, m: bool(m.group_chat_created))
|
group_chat_created = create("GroupChatCreated", lambda _, m: bool(m.group_chat_created))
|
||||||
"""Filter service messages for group chat creations."""
|
"""Filter service messages for group chat creations."""
|
||||||
|
|
||||||
supergroup_chat_created = build("SupergroupChatCreated", lambda _, m: bool(m.supergroup_chat_created))
|
supergroup_chat_created = create("SupergroupChatCreated", lambda _, m: bool(m.supergroup_chat_created))
|
||||||
"""Filter service messages for supergroup chat creations."""
|
"""Filter service messages for supergroup chat creations."""
|
||||||
|
|
||||||
channel_chat_created = build("ChannelChatCreated", lambda _, m: bool(m.channel_chat_created))
|
channel_chat_created = create("ChannelChatCreated", lambda _, m: bool(m.channel_chat_created))
|
||||||
"""Filter service messages for channel chat creations."""
|
"""Filter service messages for channel chat creations."""
|
||||||
|
|
||||||
migrate_to_chat_id = build("MigrateToChatId", lambda _, m: bool(m.migrate_to_chat_id))
|
migrate_to_chat_id = create("MigrateToChatId", lambda _, m: bool(m.migrate_to_chat_id))
|
||||||
"""Filter service messages that contain migrate_to_chat_id."""
|
"""Filter service messages that contain migrate_to_chat_id."""
|
||||||
|
|
||||||
migrate_from_chat_id = build("MigrateFromChatId", lambda _, m: bool(m.migrate_from_chat_id))
|
migrate_from_chat_id = create("MigrateFromChatId", lambda _, m: bool(m.migrate_from_chat_id))
|
||||||
"""Filter service messages that contain migrate_from_chat_id."""
|
"""Filter service messages that contain migrate_from_chat_id."""
|
||||||
|
|
||||||
pinned_message = build("PinnedMessage", lambda _, m: bool(m.pinned_message))
|
pinned_message = create("PinnedMessage", lambda _, m: bool(m.pinned_message))
|
||||||
"""Filter service messages for pinned messages."""
|
"""Filter service messages for pinned messages."""
|
||||||
|
|
||||||
reply_keyboard = build("ReplyKeyboard", lambda _, m: isinstance(m.reply_markup, ReplyKeyboardMarkup))
|
reply_keyboard = create("ReplyKeyboard", lambda _, m: isinstance(m.reply_markup, ReplyKeyboardMarkup))
|
||||||
"""Filter messages containing reply keyboard markups"""
|
"""Filter messages containing reply keyboard markups"""
|
||||||
|
|
||||||
inline_keyboard = build("InlineKeyboard", lambda _, m: isinstance(m.reply_markup, InlineKeyboardMarkup))
|
inline_keyboard = create("InlineKeyboard", lambda _, m: isinstance(m.reply_markup, InlineKeyboardMarkup))
|
||||||
"""Filter messages containing inline keyboard markups"""
|
"""Filter messages containing inline keyboard markups"""
|
||||||
|
|
||||||
|
mentioned = create("Mentioned", lambda _, m: bool(m.mentioned))
|
||||||
|
"""Filter messages containing mentions"""
|
||||||
|
|
||||||
|
via_bot = create("ViaBot", lambda _, m: bool(m.via_bot))
|
||||||
|
"""Filter messages sent via inline bots"""
|
||||||
|
|
||||||
|
service = create("Service", lambda _, m: bool(m.service))
|
||||||
|
"""Filter service messages. A service message contains any of the following fields set
|
||||||
|
|
||||||
|
- left_chat_member
|
||||||
|
- new_chat_title
|
||||||
|
- new_chat_photo
|
||||||
|
- delete_chat_photo
|
||||||
|
- group_chat_created
|
||||||
|
- supergroup_chat_created
|
||||||
|
- channel_chat_created
|
||||||
|
- migrate_to_chat_id
|
||||||
|
- migrate_from_chat_id
|
||||||
|
- pinned_message"""
|
||||||
|
|
||||||
|
media = create("Media", lambda _, m: bool(m.media))
|
||||||
|
"""Filter media messages. A media message contains any of the following fields set
|
||||||
|
|
||||||
|
- audio
|
||||||
|
- document
|
||||||
|
- photo
|
||||||
|
- sticker
|
||||||
|
- video
|
||||||
|
- animation
|
||||||
|
- voice
|
||||||
|
- video_note
|
||||||
|
- contact
|
||||||
|
- location
|
||||||
|
- venue"""
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def command(command: str or list,
|
def command(command: str or list,
|
||||||
prefix: str = "/",
|
prefix: str or list = "/",
|
||||||
separator: str = " ",
|
separator: str = " ",
|
||||||
case_sensitive: bool = False):
|
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.
|
||||||
@ -152,9 +221,10 @@ class Filters:
|
|||||||
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*
|
||||||
field of the :class:`Message <pyrogram.Message>`.
|
field of the :class:`Message <pyrogram.Message>`.
|
||||||
|
|
||||||
prefix (``str``, *optional*):
|
prefix (``str`` | ``list``, *optional*):
|
||||||
The command prefix. Defaults to "/" (slash).
|
A prefix or a list of prefixes as string the filter should look for.
|
||||||
Examples: /start, .help, !settings.
|
Defaults to "/" (slash). Examples: ".", "!", ["/", "!", "."].
|
||||||
|
Can be None or "" (empty string) to allow commands with no prefix at all.
|
||||||
|
|
||||||
separator (``str``, *optional*):
|
separator (``str``, *optional*):
|
||||||
The command arguments separator. Defaults to " " (white space).
|
The command arguments separator. Defaults to " " (white space).
|
||||||
@ -166,15 +236,18 @@ class Filters:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def f(_, m):
|
def f(_, m):
|
||||||
if m.text and m.text.startswith(_.p):
|
if m.text:
|
||||||
t = m.text.split(_.s)
|
for i in _.p:
|
||||||
c, a = t[0][len(_.p):], t[1:]
|
if m.text.startswith(i):
|
||||||
c = c if _.cs else c.lower()
|
t = m.text.split(_.s)
|
||||||
m.command = ([c] + a) if c in _.c else None
|
c, a = t[0][len(i):], t[1:]
|
||||||
|
c = c if _.cs else c.lower()
|
||||||
|
m.command = ([c] + a) if c in _.c else None
|
||||||
|
break
|
||||||
|
|
||||||
return bool(m.command)
|
return bool(m.command)
|
||||||
|
|
||||||
return build(
|
return create(
|
||||||
"Command",
|
"Command",
|
||||||
f,
|
f,
|
||||||
c={command if case_sensitive
|
c={command if case_sensitive
|
||||||
@ -183,7 +256,7 @@ class Filters:
|
|||||||
else {c if case_sensitive
|
else {c if case_sensitive
|
||||||
else c.lower()
|
else c.lower()
|
||||||
for c in command},
|
for c in command},
|
||||||
p=prefix,
|
p=set(prefix) if prefix else {""},
|
||||||
s=separator,
|
s=separator,
|
||||||
cs=case_sensitive
|
cs=case_sensitive
|
||||||
)
|
)
|
||||||
@ -206,82 +279,71 @@ class Filters:
|
|||||||
m.matches = [i for i in _.p.finditer(m.text or "")]
|
m.matches = [i for i in _.p.finditer(m.text or "")]
|
||||||
return bool(m.matches)
|
return bool(m.matches)
|
||||||
|
|
||||||
return build("Regex", f, p=re.compile(pattern, flags))
|
return create("Regex", f, p=re.compile(pattern, flags))
|
||||||
|
|
||||||
@staticmethod
|
# noinspection PyPep8Naming
|
||||||
def user(user: int or str or list):
|
class user(Filter, set):
|
||||||
"""Filter messages coming from specific users.
|
"""Filter messages coming from one or more users.
|
||||||
|
|
||||||
|
You can use `set bound methods <https://docs.python.org/3/library/stdtypes.html#set>`_ to manipulate the
|
||||||
|
users container.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
user (``int`` | ``str`` | ``list``):
|
users (``int`` | ``str`` | ``list``):
|
||||||
The user or list of user IDs (int) or usernames (str) the filter should look for.
|
Pass one or more user ids/usernames to filter users.
|
||||||
|
For you yourself, "me" or "self" can be used as well.
|
||||||
|
Defaults to None (no users).
|
||||||
"""
|
"""
|
||||||
return build(
|
|
||||||
"User",
|
|
||||||
lambda _, m: bool(m.from_user
|
|
||||||
and (m.from_user.id in _.u
|
|
||||||
or (m.from_user.username
|
|
||||||
and m.from_user.username.lower() in _.u))),
|
|
||||||
u=(
|
|
||||||
{user.lower().strip("@") if type(user) is str else user}
|
|
||||||
if not isinstance(user, list)
|
|
||||||
else {i.lower().strip("@") if type(i) is str else i for i in user}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
@staticmethod
|
def __init__(self, users: int or str or list = None):
|
||||||
def chat(chat: int or str or list):
|
users = [] if users is None else users if type(users) is list else [users]
|
||||||
"""Filter messages coming from specific chats.
|
super().__init__(
|
||||||
|
{"me" if i in ["me", "self"] else i.lower().strip("@") if type(i) is str else i for i in users}
|
||||||
|
if type(users) is list else
|
||||||
|
{"me" if users in ["me", "self"] else users.lower().strip("@") if type(users) is str else users}
|
||||||
|
)
|
||||||
|
|
||||||
|
def __call__(self, message):
|
||||||
|
return bool(
|
||||||
|
message.from_user
|
||||||
|
and (message.from_user.id in self
|
||||||
|
or (message.from_user.username
|
||||||
|
and message.from_user.username.lower() in self)
|
||||||
|
or ("me" in self
|
||||||
|
and message.from_user.is_self))
|
||||||
|
)
|
||||||
|
|
||||||
|
# noinspection PyPep8Naming
|
||||||
|
class chat(Filter, set):
|
||||||
|
"""Filter messages coming from one or more chats.
|
||||||
|
|
||||||
|
You can use `set bound methods <https://docs.python.org/3/library/stdtypes.html#set>`_ to manipulate the
|
||||||
|
chats container.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
chat (``int`` | ``str`` | ``list``):
|
chats (``int`` | ``str`` | ``list``):
|
||||||
The chat or list of chat IDs (int) or usernames (str) the filter should look for.
|
Pass one or more chat ids/usernames to filter chats.
|
||||||
|
For your personal cloud (Saved Messages) you can simply use "me" or "self".
|
||||||
|
Defaults to None (no chats).
|
||||||
"""
|
"""
|
||||||
return build(
|
|
||||||
"Chat",
|
def __init__(self, chats: int or str or list = None):
|
||||||
lambda _, m: bool(m.chat
|
chats = [] if chats is None else chats if type(chats) is list else [chats]
|
||||||
and (m.chat.id in _.c
|
super().__init__(
|
||||||
or (m.chat.username
|
{"me" if i in ["me", "self"] else i.lower().strip("@") if type(i) is str else i for i in chats}
|
||||||
and m.chat.username.lower() in _.c))),
|
if type(chats) is list else
|
||||||
c=(
|
{"me" if chats in ["me", "self"] else chats.lower().strip("@") if type(chats) is str else chats}
|
||||||
{chat.lower().strip("@") if type(chat) is str else chat}
|
|
||||||
if not isinstance(chat, list)
|
|
||||||
else {i.lower().strip("@") if type(i) is str else i for i in chat}
|
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
service = build(
|
def __call__(self, message):
|
||||||
"Service",
|
return bool(
|
||||||
lambda _, m: bool(
|
message.chat
|
||||||
Filters.new_chat_members(m)
|
and (message.chat.id in self
|
||||||
or Filters.left_chat_member(m)
|
or (message.chat.username
|
||||||
or Filters.new_chat_title(m)
|
and message.chat.username.lower() in self)
|
||||||
or Filters.new_chat_photo(m)
|
or ("me" in self and message.from_user
|
||||||
or Filters.delete_chat_photo(m)
|
and message.from_user.is_self
|
||||||
or Filters.group_chat_created(m)
|
and not message.outgoing))
|
||||||
or Filters.supergroup_chat_created(m)
|
)
|
||||||
or Filters.channel_chat_created(m)
|
|
||||||
or Filters.migrate_to_chat_id(m)
|
|
||||||
or Filters.migrate_from_chat_id(m)
|
|
||||||
or Filters.pinned_message(m)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
"""Filter all service messages."""
|
|
||||||
|
|
||||||
media = build(
|
dan = create("Dan", lambda _, m: bool(m.from_user and m.from_user.id == 23122162))
|
||||||
"Media",
|
|
||||||
lambda _, m: bool(
|
|
||||||
Filters.audio(m)
|
|
||||||
or Filters.document(m)
|
|
||||||
or Filters.photo(m)
|
|
||||||
or Filters.sticker(m)
|
|
||||||
or Filters.video(m)
|
|
||||||
or Filters.gif(m)
|
|
||||||
or Filters.voice(m)
|
|
||||||
or Filters.video_note(m)
|
|
||||||
or Filters.contact(m)
|
|
||||||
or Filters.location(m)
|
|
||||||
or Filters.venue(m)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
"""Filter all media messages."""
|
|
||||||
|
@ -17,7 +17,8 @@
|
|||||||
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
|
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from .callback_query_handler import CallbackQueryHandler
|
from .callback_query_handler import CallbackQueryHandler
|
||||||
|
from .deleted_messages_handler import DeletedMessagesHandler
|
||||||
from .disconnect_handler import DisconnectHandler
|
from .disconnect_handler import DisconnectHandler
|
||||||
from .message_handler import MessageHandler
|
from .message_handler import MessageHandler
|
||||||
from .deleted_messages_handler import DeletedMessagesHandler
|
|
||||||
from .raw_update_handler import RawUpdateHandler
|
from .raw_update_handler import RawUpdateHandler
|
||||||
|
from .user_status_handler import UserStatusHandler
|
||||||
|
@ -49,6 +49,6 @@ class CallbackQueryHandler(Handler):
|
|||||||
def check(self, callback_query):
|
def check(self, callback_query):
|
||||||
return (
|
return (
|
||||||
self.filters(callback_query)
|
self.filters(callback_query)
|
||||||
if self.filters
|
if callable(self.filters)
|
||||||
else True
|
else True
|
||||||
)
|
)
|
||||||
|
@ -50,6 +50,6 @@ class DeletedMessagesHandler(Handler):
|
|||||||
def check(self, messages):
|
def check(self, messages):
|
||||||
return (
|
return (
|
||||||
self.filters(messages.messages[0])
|
self.filters(messages.messages[0])
|
||||||
if self.filters
|
if callable(self.filters)
|
||||||
else True
|
else True
|
||||||
)
|
)
|
||||||
|
@ -50,6 +50,6 @@ class MessageHandler(Handler):
|
|||||||
def check(self, message):
|
def check(self, message):
|
||||||
return (
|
return (
|
||||||
self.filters(message)
|
self.filters(message)
|
||||||
if self.filters
|
if callable(self.filters)
|
||||||
else True
|
else True
|
||||||
)
|
)
|
||||||
|
54
pyrogram/client/handlers/user_status_handler.py
Normal file
54
pyrogram/client/handlers/user_status_handler.py
Normal 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 UserStatusHandler(Handler):
|
||||||
|
"""The UserStatus handler class. Used to handle user status updates (user going online or offline).
|
||||||
|
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_user_status() <pyrogram.Client.on_user_status>` decorator.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
callback (``callable``):
|
||||||
|
Pass a function that will be called when a new UserStatus update arrives. It takes *(client, user_status)*
|
||||||
|
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 messages 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 user status handler.
|
||||||
|
|
||||||
|
user_status (:obj:`UserStatus <pyrogram.UserStatus>`):
|
||||||
|
The received UserStatus update.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, callback: callable, filters=None):
|
||||||
|
super().__init__(callback, filters)
|
||||||
|
|
||||||
|
def check(self, user_status):
|
||||||
|
return (
|
||||||
|
self.filters(user_status)
|
||||||
|
if callable(self.filters)
|
||||||
|
else True
|
||||||
|
)
|
@ -20,10 +20,10 @@ from .bots import Bots
|
|||||||
from .chats import Chats
|
from .chats import Chats
|
||||||
from .contacts import Contacts
|
from .contacts import Contacts
|
||||||
from .decorators import Decorators
|
from .decorators import Decorators
|
||||||
from .download_media import DownloadMedia
|
|
||||||
from .messages import Messages
|
from .messages import Messages
|
||||||
from .password import Password
|
from .password import Password
|
||||||
from .users import Users
|
from .users import Users
|
||||||
|
from .utilities import Utilities
|
||||||
|
|
||||||
|
|
||||||
class Methods(
|
class Methods(
|
||||||
@ -32,7 +32,7 @@ class Methods(
|
|||||||
Password,
|
Password,
|
||||||
Chats,
|
Chats,
|
||||||
Users,
|
Users,
|
||||||
DownloadMedia,
|
Utilities,
|
||||||
Messages,
|
Messages,
|
||||||
Decorators
|
Decorators
|
||||||
):
|
):
|
||||||
|
@ -50,6 +50,12 @@ class AnswerCallbackQuery(BaseClient):
|
|||||||
cache_time (``int``):
|
cache_time (``int``):
|
||||||
The maximum amount of time in seconds that the result of the callback query may be cached client-side.
|
The maximum amount of time in seconds that the result of the callback query may be cached client-side.
|
||||||
Telegram apps will support caching starting in version 3.14. Defaults to 0.
|
Telegram apps will support caching starting in version 3.14. Defaults to 0.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True, on success.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
:class:`Error <pyrogram.Error>` in case of a Telegram RPC error.
|
||||||
"""
|
"""
|
||||||
return self.send(
|
return self.send(
|
||||||
functions.messages.SetBotCallbackAnswer(
|
functions.messages.SetBotCallbackAnswer(
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user