Merge branch 'develop' into develop

This commit is contained in:
Dan 2019-08-07 16:05:01 +02:00 committed by GitHub
commit f33a192162
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
250 changed files with 6508 additions and 4665 deletions

2
.github/FUNDING.yml vendored
View File

@ -1,2 +1,2 @@
github: delivrance
# github: delivrance
custom: https://docs.pyrogram.org/support-pyrogram

View File

@ -10,6 +10,6 @@ labels: "question"
# Important
This place is for issues about Pyrogram, it's **not a forum**.
If you'd like to post a question, please move to https://stackoverflow.com or join the Telegram community at https://t.me/pyrogram.
If you'd like to post a question, please move to https://stackoverflow.com or join the Telegram community at https://t.me/pyrogram. Useful information on how to ask good questions can be found here: https://stackoverflow.com/help/how-to-ask.
Thanks.

4
.gitignore vendored
View File

@ -11,6 +11,9 @@ pyrogram/api/all.py
# PyCharm stuff
.idea/
# VS Code
.vscode/
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
@ -78,6 +81,7 @@ instance/
# Sphinx documentation
docs/_build/
docs/source/_build
# PyBuilder
target/

View File

@ -1,10 +1,10 @@
## Include
include README.md COPYING COPYING.lesser NOTICE requirements.txt
recursive-include compiler *.py *.tl *.tsv *.txt
recursive-include pyrogram mime.types
recursive-include pyrogram mime.types schema.sql
## Exclude
prune pyrogram/api/errors/exceptions
prune pyrogram/errors/exceptions
prune pyrogram/api/functions
prune pyrogram/api/types
exclude pyrogram/api/all.py

View File

@ -28,7 +28,7 @@ app = Client("my_account")
@app.on_message(Filters.private)
def hello(client, message):
message.reply("Hello {}".format(message.from_user.first_name))
message.reply_text("Hello {}".format(message.from_user.first_name))
app.run()

View File

@ -478,7 +478,6 @@ def start():
f.write("\n 0xbc799737: \"pyrogram.api.core.BoolFalse\",")
f.write("\n 0x997275b5: \"pyrogram.api.core.BoolTrue\",")
f.write("\n 0x56730bcc: \"pyrogram.api.core.Null\",")
f.write("\n 0x1cb5c415: \"pyrogram.api.core.Vector\",")
f.write("\n 0x73f1f8dc: \"pyrogram.api.core.MsgContainer\",")
f.write("\n 0xae500895: \"pyrogram.api.core.FutureSalts\",")

View File

@ -101,11 +101,11 @@ userStatusLastMonth#77ebc742 = UserStatus;
chatEmpty#9ba2d800 id:int = Chat;
chat#3bda1bde flags:# creator:flags.0?true kicked:flags.1?true left:flags.2?true deactivated:flags.5?true id:int title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel admin_rights:flags.14?ChatAdminRights default_banned_rights:flags.18?ChatBannedRights = Chat;
chatForbidden#7328bdb id:int title:string = Chat;
channel#4df30834 flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true id:int access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?string admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int = Chat;
channel#4df30834 flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true id:int access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?string admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int = Chat;
channelForbidden#289da732 flags:# broadcast:flags.5?true megagroup:flags.8?true id:int access_hash:long title:string until_date:flags.16?int = Chat;
chatFull#1b7c9db3 flags:# can_set_username:flags.7?true id:int about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int = ChatFull;
channelFull#9882e516 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 folder_id:flags.11?int linked_chat_id:flags.13?int pts:int = ChatFull;
channelFull#10916653 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_view_stats:flags.12?true can_set_location:flags.16?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector<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 folder_id:flags.11?int linked_chat_id:flags.14?int location:flags.15?ChannelLocation pts:int = ChatFull;
chatParticipant#c8d7493e user_id:int inviter_id:int date:int = ChatParticipant;
chatParticipantCreator#da13538a user_id:int = ChatParticipant;
@ -189,7 +189,7 @@ inputPeerNotifySettings#9c3d198e flags:# show_previews:flags.0?Bool silent:flags
peerNotifySettings#af509d20 flags:# show_previews:flags.0?Bool silent:flags.1?Bool mute_until:flags.2?int sound:flags.3?string = PeerNotifySettings;
peerSettings#818426cd flags:# report_spam:flags.0?true = PeerSettings;
peerSettings#818426cd flags:# report_spam:flags.0?true add_contact:flags.1?true block_contact:flags.2?true share_contact:flags.3?true need_contacts_exception:flags.4?true report_geo:flags.5?true = PeerSettings;
wallPaper#a437c3ed id:long flags:# creator:flags.0?true default:flags.1?true pattern:flags.3?true dark:flags.4?true access_hash:long slug:string document:Document settings:flags.2?WallPaperSettings = WallPaper;
@ -199,8 +199,9 @@ inputReportReasonPornography#2e59d922 = ReportReason;
inputReportReasonChildAbuse#adf44ee3 = ReportReason;
inputReportReasonOther#e1746d0a text:string = ReportReason;
inputReportReasonCopyright#9b89f93a = ReportReason;
inputReportReasonGeoIrrelevant#dbd4feed = ReportReason;
userFull#745559cc 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 folder_id:flags.11?int = UserFull;
userFull#edf17c12 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true user:User about:flags.1?string settings:PeerSettings profile_photo:flags.2?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int = UserFull;
contact#f911c994 user_id:int mutual:Bool = Contact;
@ -210,8 +211,6 @@ contactBlocked#561bc879 user_id:int date:int = ContactBlocked;
contactStatus#d3680c61 user_id:int status:UserStatus = ContactStatus;
contacts.link#3ace484c my_link:ContactLink foreign_link:ContactLink user:User = contacts.Link;
contacts.contactsNotModified#b74ba9d2 = contacts.Contacts;
contacts.contacts#eae87e42 contacts:Vector<Contact> saved_count:int users:Vector<User> = contacts.Contacts;
@ -262,7 +261,6 @@ updateChatParticipants#7761198 participants:ChatParticipants = Update;
updateUserStatus#1bfbd823 user_id:int status:UserStatus = 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;
updateContactLink#9d2e67c5 user_id:int my_link:ContactLink foreign_link:ContactLink = Update;
updateNewEncryptedMessage#12bcbd9a message:EncryptedMessage qts:int = Update;
updateEncryptedChatTyping#1710f156 chat_id:int = Update;
updateEncryption#b4a2e88d chat:EncryptedChat date:int = Update;
@ -323,6 +321,8 @@ updateChatPinnedMessage#e10db349 chat_id:int id:int version:int = Update;
updateMessagePoll#aca1657b flags:# poll_id:long poll:flags.0?Poll results:PollResults = Update;
updateChatDefaultBannedRights#54c01850 peer:Peer default_banned_rights:ChatBannedRights version:int = Update;
updateFolderPeers#19360dc0 folder_peers:Vector<FolderPeer> pts:int pts_count:int = Update;
updatePeerSettings#6a7e7366 peer:Peer settings:PeerSettings = Update;
updatePeerLocated#b4afcfb0 peers:Vector<PeerLocated> = Update;
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
@ -468,10 +468,6 @@ messages.allStickers#edfd405f hash:int sets:Vector<StickerSet> = messages.AllSti
messages.affectedMessages#84d19185 pts:int pts_count:int = messages.AffectedMessages;
contactLinkUnknown#5f4f9247 = ContactLink;
contactLinkNone#feedd3ad = ContactLink;
contactLinkContact#d502c2d0 = ContactLink;
webPageEmpty#eb1477e8 id:long = WebPage;
webPagePending#c586da1c id:long date:int = WebPage;
webPage#5f07b4bc flags:# id:long url:string display_url:string hash:int type:flags.0?string site_name:flags.1?string title:flags.2?string description:flags.3?string photo:flags.4?Photo embed_url:flags.5?string embed_type:flags.5?string embed_width:flags.6?int embed_height:flags.6?int duration:flags.7?int author:flags.8?string document:flags.9?Document cached_page:flags.10?Page = WebPage;
@ -501,7 +497,7 @@ inputStickerSetEmpty#ffb62b95 = InputStickerSet;
inputStickerSetID#9de7a269 id:long access_hash:long = InputStickerSet;
inputStickerSetShortName#861cc8a0 short_name:string = InputStickerSet;
stickerSet#eeb46f27 flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string thumb:flags.4?PhotoSize thumb_dc_id:flags.4?int count:int hash:int = StickerSet;
stickerSet#eeb46f27 flags:# archived:flags.1?true official:flags.2?true masks:flags.3?true animated:flags.5?true installed_date:flags.0?int id:long access_hash:long title:string short_name:string thumb:flags.4?PhotoSize thumb_dc_id:flags.4?int count:int hash:int = StickerSet;
messages.stickerSet#b60a24a6 set:StickerSet packs:Vector<StickerPack> documents:Vector<Document> = messages.StickerSet;
@ -542,6 +538,9 @@ messageEntityMentionName#352dca58 offset:int length:int user_id:int = MessageEnt
inputMessageEntityMentionName#208e68c9 offset:int length:int user_id:InputUser = MessageEntity;
messageEntityPhone#9b69e34b offset:int length:int = MessageEntity;
messageEntityCashtag#4c4e743f offset:int length:int = MessageEntity;
messageEntityUnderline#9c4e7e8b offset:int length:int = MessageEntity;
messageEntityStrike#bf0693d4 offset:int length:int = MessageEntity;
messageEntityBlockquote#20df5d0 offset:int length:int = MessageEntity;
inputChannelEmpty#ee8c1e86 = InputChannel;
inputChannel#afeb712e channel_id:int access_hash:long = InputChannel;
@ -828,6 +827,7 @@ channelAdminLogEventActionTogglePreHistoryHidden#5f5c95f1 new_value:Bool = Chann
channelAdminLogEventActionDefaultBannedRights#2df5fc0a prev_banned_rights:ChatBannedRights new_banned_rights:ChatBannedRights = ChannelAdminLogEventAction;
channelAdminLogEventActionStopPoll#8f079643 message:Message = ChannelAdminLogEventAction;
channelAdminLogEventActionChangeLinkedChat#a26f881b prev_value:int new_value:int = ChannelAdminLogEventAction;
channelAdminLogEventActionChangeLocation#e6b76ae prev_value:ChannelLocation new_value:ChannelLocation = ChannelAdminLogEventAction;
channelAdminLogEvent#3b5a3e40 id:long date:int user_id:int action:ChannelAdminLogEventAction = ChannelAdminLogEvent;
@ -1034,6 +1034,11 @@ urlAuthResultRequest#92d33a0e flags:# request_write_access:flags.0?true bot:User
urlAuthResultAccepted#8f8c0e4e url:string = UrlAuthResult;
urlAuthResultDefault#a9d6db1f = UrlAuthResult;
channelLocationEmpty#bfb5ad8b = ChannelLocation;
channelLocation#209b82db geo_point:GeoPoint address:string = ChannelLocation;
peerLocated#ca461b5d peer:Peer expires:int distance:int = PeerLocated;
---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
@ -1060,7 +1065,7 @@ auth.resendCode#3ef1a9bf phone_number:string phone_code_hash:string = auth.SentC
auth.cancelCode#1f040578 phone_number:string phone_code_hash:string = Bool;
auth.dropTempAuthKeys#8e48a188 except_auth_keys:Vector<long> = Bool;
account.registerDevice#5cbea590 token_type:int token:string app_sandbox:Bool secret:bytes other_uids:Vector<int> = Bool;
account.registerDevice#68976c6f flags:# no_muted:flags.0?true token_type:int token:string app_sandbox:Bool secret:bytes other_uids:Vector<int> = Bool;
account.unregisterDevice#3076c4bf token_type:int token:string other_uids:Vector<int> = Bool;
account.updateNotifySettings#84be5b93 peer:InputNotifyPeer settings:InputPeerNotifySettings = Bool;
account.getNotifySettings#12b3ad31 peer:InputNotifyPeer = PeerNotifySettings;
@ -1124,8 +1129,7 @@ contacts.getContactIDs#2caa4a42 hash:int = Vector<int>;
contacts.getStatuses#c4a353ee = Vector<ContactStatus>;
contacts.getContacts#c023849f hash:int = contacts.Contacts;
contacts.importContacts#2c800be5 contacts:Vector<InputContact> = contacts.ImportedContacts;
contacts.deleteContact#8e953744 id:InputUser = contacts.Link;
contacts.deleteContacts#59ab389e id:Vector<InputUser> = Bool;
contacts.deleteContacts#96a0e00 id:Vector<InputUser> = Updates;
contacts.deleteByPhones#1013fd9e phones:Vector<string> = Bool;
contacts.block#332b49fc id:InputUser = Bool;
contacts.unblock#e54100bd id:InputUser = Bool;
@ -1137,6 +1141,9 @@ contacts.resetTopPeerRating#1ae373ac category:TopPeerCategory peer:InputPeer = B
contacts.resetSaved#879537f1 = Bool;
contacts.getSaved#82f1e39f = Vector<SavedContact>;
contacts.toggleTopPeers#8514bdda enabled:Bool = Bool;
contacts.addContact#e8f463d0 flags:# add_phone_privacy_exception:flags.0?true id:InputUser first_name:string last_name:string phone:string = Updates;
contacts.acceptContact#f831a20f id:InputUser = Updates;
contacts.getLocated#a356056 geo_point:InputGeoPoint = Updates;
messages.getMessages#63c66506 id:Vector<InputMessage> = messages.Messages;
messages.getDialogs#a0ee3b73 flags:# exclude_pinned:flags.0?true folder_id:flags.1?int offset_date:int offset_id:int offset_peer:InputPeer limit:int hash:int = messages.Dialogs;
@ -1151,7 +1158,6 @@ messages.sendMessage#fa88427a flags:# no_webpage:flags.1?true silent:flags.5?tru
messages.sendMedia#b8d1262b flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> = Updates;
messages.forwardMessages#708e0195 flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true grouped:flags.9?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer = Updates;
messages.reportSpam#cf1592db peer:InputPeer = Bool;
messages.hideReportSpam#a8f1709b peer:InputPeer = Bool;
messages.getPeerSettings#3672e09c peer:InputPeer = PeerSettings;
messages.report#bd82b658 peer:InputPeer id:Vector<int> reason:ReportReason = Bool;
messages.getChats#3c6aa187 id:Vector<int> = messages.Chats;
@ -1186,7 +1192,7 @@ messages.startBot#e6df7378 bot:InputUser peer:InputPeer random_id:long start_par
messages.getMessagesViews#c4c8a55d peer:InputPeer id:Vector<int> increment:Bool = Vector<int>;
messages.editChatAdmin#a9e69f2e chat_id:int user_id:InputUser is_admin:Bool = Bool;
messages.migrateChat#15a3b8e3 chat_id:int = Updates;
messages.searchGlobal#f79c611 q:string offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages;
messages.searchGlobal#bf7225a4 flags:# folder_id:flags.0?int q:string offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages;
messages.reorderStickerSets#78337739 flags:# masks:flags.0?true order:Vector<long> = Bool;
messages.getDocumentByHash#338e2464 sha256:bytes size:int mime_type:string = Document;
messages.searchGifs#bf9a776b q:string offset:int = messages.FoundGifs;
@ -1251,6 +1257,7 @@ messages.getEmojiURL#d5b10c26 lang_code:string = EmojiURL;
messages.getSearchCounters#732eef00 peer:InputPeer filters:Vector<MessagesFilter> = Vector<messages.SearchCounter>;
messages.requestUrlAuth#e33f5613 peer:InputPeer msg_id:int button_id:int = UrlAuthResult;
messages.acceptUrlAuth#f729ea98 flags:# write_allowed:flags.0?true peer:InputPeer msg_id:int button_id:int = UrlAuthResult;
messages.hidePeerSettingsBar#4facb138 peer:InputPeer = Bool;
updates.getState#edd4882a = updates.State;
updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference;
@ -1299,7 +1306,7 @@ channels.getParticipants#123e05e9 channel:InputChannel filter:ChannelParticipant
channels.getParticipant#546dd7a6 channel:InputChannel user_id:InputUser = channels.ChannelParticipant;
channels.getChannels#a7f6bbb id:Vector<InputChannel> = messages.Chats;
channels.getFullChannel#8736a09 channel:InputChannel = messages.ChatFull;
channels.createChannel#f4893d7f flags:# broadcast:flags.0?true megagroup:flags.1?true title:string about:string = Updates;
channels.createChannel#3d5fb10f flags:# broadcast:flags.0?true megagroup:flags.1?true title:string about:string geo_point:flags.2?InputGeoPoint address:flags.2?string = Updates;
channels.editAdmin#70f893ba channel:InputChannel user_id:InputUser admin_rights:ChatAdminRights = Updates;
channels.editTitle#566decd0 channel:InputChannel title:string = Updates;
channels.editPhoto#f12e57c9 channel:InputChannel photo:InputChatPhoto = Updates;
@ -1311,7 +1318,7 @@ channels.inviteToChannel#199f3a6c channel:InputChannel users:Vector<InputUser> =
channels.deleteChannel#c0111fe3 channel:InputChannel = Updates;
channels.exportMessageLink#ceb77163 channel:InputChannel id:int grouped:Bool = ExportedMessageLink;
channels.toggleSignatures#1f69b606 channel:InputChannel enabled:Bool = Updates;
channels.getAdminedPublicChannels#8d8d82d7 = messages.Chats;
channels.getAdminedPublicChannels#f8b036af flags:# by_location:flags.0?true check_limit:flags.1?true = messages.Chats;
channels.editBanned#72796912 channel:InputChannel user_id:InputUser banned_rights:ChatBannedRights = Updates;
channels.getAdminLog#33ddf480 flags:# channel:InputChannel q:string events_filter:flags.0?ChannelAdminLogEventsFilter admins:flags.1?Vector<InputUser> max_id:long min_id:long limit:int = channels.AdminLogResults;
channels.setStickers#ea8ca4f9 channel:InputChannel stickerset:InputStickerSet = Bool;
@ -1320,8 +1327,9 @@ channels.deleteHistory#af369d42 channel:InputChannel max_id:int = Bool;
channels.togglePreHistoryHidden#eabbb94c channel:InputChannel enabled:Bool = Updates;
channels.getLeftChannels#8341ecc0 offset:int = messages.Chats;
channels.getGroupsForDiscussion#f5dad378 = messages.Chats;
channels.getBroadcastsForDiscussion#1a87f304 = messages.Chats;
channels.setDiscussionGroup#40582bb2 broadcast:InputChannel group:InputChannel = Bool;
channels.editCreator#8f38cd1f channel:InputChannel user_id:InputUser password:InputCheckPasswordSRP = Updates;
channels.editLocation#58e63f6d channel:InputChannel geo_point:InputGeoPoint address:string = Bool;
bots.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON;
bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool;
@ -1356,7 +1364,4 @@ langpack.getLanguage#6a596502 lang_pack:string lang_code:string = LangPackLangua
folders.editPeerFolders#6847d0ab folder_peers:Vector<InputFolderPeer> = Updates;
folders.deleteFolder#1c295881 folder_id:int = Updates;
// LAYER 100
// Ports
channels.exportInvite#c7560885 channel:InputChannel = ExportedChatInvite;
// LAYER 103

View File

@ -53,6 +53,15 @@ ipPortSecret#37982646 ipv4:int port:int secret:bytes = IpPort;
accessPointRule#4679b65f phone_prefix_rules:string dc_id:int ips:vector<IpPort> = AccessPointRule;
help.configSimple#5a592a6c date:int expires:int rules:vector<AccessPointRule> = help.ConfigSimple;
// tlsClientHello blocks:vector<TlsBlock> = TlsClientHello;
//
// tlsBlockString data:string = TlsBlock;
// tlsBlockRandom length:int = TlsBlock;
// tlsBlockZero length:int = TlsBlock;
// tlsBlockDomain = TlsBlock;
// tlsBlockGrease seed:int = TlsBlock;
// tlsBlockScope entries:Vector<TlsBlock> = TlsBlock;
---functions---
rpc_drop_answer#58e4a740 req_msg_id:long = RpcDropAnswer;

View File

@ -23,6 +23,7 @@ import shutil
HOME = "compiler/docs"
DESTINATION = "docs/source/telegram"
PYROGRAM_API_DEST = "docs/source/api"
FUNCTIONS_PATH = "pyrogram/api/functions"
TYPES_PATH = "pyrogram/api/types"
@ -117,6 +118,362 @@ def generate(source_path, base):
f.write("\n")
def pyrogram_api():
def get_title_list(s: str) -> list:
return [i.strip() for i in [j.strip() for j in s.split("\n") if j] if i]
# Methods
categories = dict(
utilities="""
Utilities
start
stop
restart
idle
run
add_handler
remove_handler
stop_transmission
export_session_string
set_parse_mode
""",
messages="""
Messages
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_cached_media
edit_message_text
edit_message_caption
edit_message_media
edit_message_reply_markup
edit_inline_text
edit_inline_caption
edit_inline_media
edit_inline_reply_markup
send_chat_action
delete_messages
get_messages
get_history
get_history_count
read_history
iter_history
send_poll
vote_poll
stop_poll
retract_vote
download_media
""",
chats="""
Chats
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
set_chat_permissions
pin_chat_message
unpin_chat_message
get_chat
get_chat_member
get_chat_members
get_chat_members_count
iter_chat_members
get_dialogs
iter_dialogs
get_dialogs_count
update_chat_username
archive_chats
unarchive_chats
add_chat_members
create_channel
create_group
create_supergroup
delete_channel
delete_supergroup
""",
users="""
Users
get_me
get_users
get_profile_photos
get_profile_photos_count
iter_profile_photos
set_profile_photo
delete_profile_photos
update_username
block_user
unblock_user
""",
contacts="""
Contacts
add_contacts
get_contacts
get_contacts_count
delete_contacts
""",
password="""
Pssword
enable_cloud_password
change_cloud_password
remove_cloud_password
""",
bots="""
Bots
get_inline_bot_results
send_inline_bot_result
answer_callback_query
answer_inline_query
request_callback_answer
send_game
set_game_score
get_game_high_scores
""",
advanced="""
Advanced
send
resolve_peer
save_file
"""
)
root = PYROGRAM_API_DEST + "/methods"
shutil.rmtree(root, ignore_errors=True)
os.mkdir(root)
with open(HOME + "/template/methods.rst") as f:
template = f.read()
with open(root + "/index.rst", "w") as f:
fmt_keys = {}
for k, v in categories.items():
name, *methods = get_title_list(v)
fmt_keys.update({k: "\n ".join("{0} <{0}>".format(m) for m in methods)})
for method in methods:
with open(root + "/{}.rst".format(method), "w") as f2:
title = "{}()".format(method)
f2.write(title + "\n" + "=" * len(title) + "\n\n")
f2.write(".. automethod:: pyrogram.Client.{}()".format(method))
f.write(template.format(**fmt_keys))
# Types
categories = dict(
users_chats="""
Users & Chats
User
Chat
ChatPreview
ChatPhoto
ChatMember
ChatPermissions
Dialog
""",
messages_media="""
Messages & Media
Message
MessageEntity
Photo
Thumbnail
Audio
Document
Animation
Video
Voice
VideoNote
Contact
Location
Venue
Sticker
Game
WebPage
Poll
PollOption
""",
bots_keyboard="""
Bots & Keyboards
ReplyKeyboardMarkup
KeyboardButton
ReplyKeyboardRemove
InlineKeyboardMarkup
InlineKeyboardButton
ForceReply
CallbackQuery
GameHighScore
CallbackGame
""",
input_media="""
Input Media
InputMedia
InputMediaPhoto
InputMediaVideo
InputMediaAudio
InputMediaAnimation
InputMediaDocument
InputPhoneContact
""",
inline_mode="""
Inline Mode
InlineQuery
InlineQueryResult
InlineQueryResultArticle
InlineQueryResultPhoto
InlineQueryResultAnimation
""",
input_message_content="""
InputMessageContent
InputMessageContent
InputTextMessageContent
"""
)
root = PYROGRAM_API_DEST + "/types"
shutil.rmtree(root, ignore_errors=True)
os.mkdir(root)
with open(HOME + "/template/types.rst") as f:
template = f.read()
with open(root + "/index.rst", "w") as f:
fmt_keys = {}
for k, v in categories.items():
name, *types = get_title_list(v)
fmt_keys.update({k: "\n ".join(types)})
# noinspection PyShadowingBuiltins
for type in types:
with open(root + "/{}.rst".format(type), "w") as f2:
title = "{}".format(type)
f2.write(title + "\n" + "=" * len(title) + "\n\n")
f2.write(".. autoclass:: pyrogram.{}()".format(type))
f.write(template.format(**fmt_keys))
# Bound Methods
categories = dict(
message="""
Message
Message.click
Message.delete
Message.download
Message.forward
Message.pin
Message.edit_text
Message.edit_caption
Message.edit_media
Message.edit_reply_markup
Message.reply_text
Message.reply_animation
Message.reply_audio
Message.reply_cached_media
Message.reply_chat_action
Message.reply_contact
Message.reply_document
Message.reply_game
Message.reply_inline_bot_result
Message.reply_location
Message.reply_media_group
Message.reply_photo
Message.reply_poll
Message.reply_sticker
Message.reply_venue
Message.reply_video
Message.reply_video_note
Message.reply_voice
""",
chat="""
Chat
Chat.archive
Chat.unarchive
Chat.set_title
Chat.set_description
Chat.set_photo
Chat.kick_member
Chat.unban_member
Chat.restrict_member
Chat.promote_member
Chat.join
Chat.leave
""",
user="""
User
User.archive
User.unarchive
User.block
User.unblock
""",
callback_query="""
Callback Query
CallbackQuery.answer
CallbackQuery.edit_message_text
CallbackQuery.edit_message_caption
CallbackQuery.edit_message_media
CallbackQuery.edit_message_reply_markup
""",
inline_query="""
InlineQuery
InlineQuery.answer
"""
)
root = PYROGRAM_API_DEST + "/bound-methods"
shutil.rmtree(root, ignore_errors=True)
os.mkdir(root)
with open(HOME + "/template/bound-methods.rst") as f:
template = f.read()
with open(root + "/index.rst", "w") as f:
fmt_keys = {}
for k, v in categories.items():
name, *bound_methods = get_title_list(v)
fmt_keys.update({"{}_hlist".format(k): "\n ".join("- :meth:`~{}`".format(bm) for bm in bound_methods)})
fmt_keys.update(
{"{}_toctree".format(k): "\n ".join("{} <{}>".format(bm.split(".")[1], bm) for bm in bound_methods)})
# noinspection PyShadowingBuiltins
for bm in bound_methods:
with open(root + "/{}.rst".format(bm), "w") as f2:
title = "{}()".format(bm)
f2.write(title + "\n" + "=" * len(title) + "\n\n")
f2.write(".. automethod:: pyrogram.{}()".format(bm))
f.write(template.format(**fmt_keys))
def start():
global page_template
global toctree
@ -131,6 +488,7 @@ def start():
generate(TYPES_PATH, TYPES_BASE)
generate(FUNCTIONS_PATH, FUNCTIONS_BASE)
pyrogram_api()
if "__main__" == __name__:
@ -138,5 +496,6 @@ if "__main__" == __name__:
TYPES_PATH = "../../pyrogram/api/types"
HOME = "."
DESTINATION = "../../docs/source/telegram"
PYROGRAM_API_DEST = "../../docs/source/api"
start()

View File

@ -0,0 +1,88 @@
Bound Methods
=============
Some Pyrogram types define what are called bound methods. Bound methods are functions attached to a class which are
accessed via an instance of that class. They make it even easier to call specific methods by automatically inferring
some of the required arguments.
.. code-block:: python
:emphasize-lines: 8
from pyrogram import Client
app = Client("my_account")
@app.on_message()
def hello(client, message)
message.reply("hi")
app.run()
.. currentmodule:: pyrogram
Message
-------
.. hlist::
:columns: 3
{message_hlist}
.. toctree::
:hidden:
{message_toctree}
Chat
----
.. hlist::
:columns: 4
{chat_hlist}
.. toctree::
:hidden:
{chat_toctree}
User
----
.. hlist::
:columns: 2
{user_hlist}
.. toctree::
:hidden:
{user_toctree}
CallbackQuery
-------------
.. hlist::
:columns: 3
{callback_query_hlist}
.. toctree::
:hidden:
{callback_query_toctree}
InlineQuery
-----------
.. hlist::
:columns: 2
{inline_query_hlist}
.. toctree::
:hidden:
{inline_query_toctree}

122
compiler/docs/template/methods.rst vendored Normal file
View File

@ -0,0 +1,122 @@
Available Methods
=================
This page is about Pyrogram methods. All the methods listed here are bound to a :class:`~pyrogram.Client` instance.
.. code-block:: python
:emphasize-lines: 6
from pyrogram import Client
app = Client("my_account")
with app:
app.send_message("haskell", "hi")
.. currentmodule:: pyrogram.Client
Utilities
---------
.. autosummary::
:nosignatures:
{utilities}
.. toctree::
:hidden:
{utilities}
Messages
--------
.. autosummary::
:nosignatures:
{messages}
.. toctree::
:hidden:
{messages}
Chats
-----
.. autosummary::
:nosignatures:
{chats}
.. toctree::
:hidden:
{chats}
Users
-----
.. autosummary::
:nosignatures:
{users}
.. toctree::
:hidden:
{users}
Contacts
--------
.. autosummary::
:nosignatures:
{contacts}
.. toctree::
:hidden:
{contacts}
Password
--------
.. autosummary::
:nosignatures:
{password}
.. toctree::
:hidden:
{password}
Bots
----
.. autosummary::
:nosignatures:
{bots}
.. toctree::
:hidden:
{bots}
Advanced
--------
Learn more about these methods at :doc:`Advanced Usage <../../topics/advanced-usage>`.
.. autosummary::
:nosignatures:
{advanced}
.. toctree::
:hidden:
{advanced}

95
compiler/docs/template/types.rst vendored Normal file
View File

@ -0,0 +1,95 @@
Available Types
===============
This page is about Pyrogram types. All types listed here are accessible through the main package directly.
.. code-block:: python
:emphasize-lines: 1
from pyrogram import User, Message, ...
.. note::
**Optional** fields may not exist when irrelevant -- i.e.: they will contain the value of ``None`` and aren't shown
when, for example, using ``print()``.
.. currentmodule:: pyrogram
Users & Chats
-------------
.. autosummary::
:nosignatures:
{users_chats}
.. toctree::
:hidden:
{users_chats}
Messages & Media
----------------
.. autosummary::
:nosignatures:
{messages_media}
.. toctree::
:hidden:
{messages_media}
Bots & Keyboards
----------------
.. autosummary::
:nosignatures:
{bots_keyboard}
.. toctree::
:hidden:
{bots_keyboard}
Input Media
-----------
.. autosummary::
:nosignatures:
{input_media}
.. toctree::
:hidden:
{input_media}
Inline Mode
-----------
.. autosummary::
:nosignatures:
{inline_mode}
.. toctree::
:hidden:
{inline_mode}
InputMessageContent
-------------------
.. autosummary::
:nosignatures:
{input_message_content}
.. toctree::
:hidden:
{input_message_content}

View File

@ -81,6 +81,8 @@ def start():
sub_classes = []
f_all.write(" \"_\": \"{}\",\n".format(super_class))
for j, row in enumerate(reader):
if j == 0:
continue
@ -90,13 +92,13 @@ def start():
if not row: # Row is empty (blank line)
continue
id, message = row
error_id, error_message = row
sub_class = caml(re.sub(r"_X", "_", id))
sub_class = caml(re.sub(r"_X", "_", error_id))
f_all.write(" \"{}\": \"{}\",\n".format(id, sub_class))
f_all.write(" \"{}\": \"{}\",\n".format(error_id, sub_class))
sub_classes.append((sub_class, id, message))
sub_classes.append((sub_class, error_id, error_message))
with open("{}/template/class.txt".format(HOME), "r", encoding="utf-8") as f_class_template:
class_template = f_class_template.read()

View File

@ -12,10 +12,10 @@ PHONE_NUMBER_UNOCCUPIED The phone number is not yet being used
USERS_TOO_FEW Not enough users (to create a chat, for example)
USERS_TOO_MUCH The maximum number of users has been exceeded (to create a chat, for example)
TYPE_CONSTRUCTOR_INVALID The type constructor is invalid
FILE_PART_INVALID The file part number is invalid
FILE_PARTS_INVALID The number of file parts is invalid
FILE_PART_INVALID The file part number is invalid. The value is not between 0 and 2999
FILE_PARTS_INVALID Invalid number of parts. The value is not between 1 and 3000
FILE_PART_X_MISSING Part {x} of the file is missing from storage
MD5_CHECKSUM_INVALID The MD5 checksums do not match
MD5_CHECKSUM_INVALID The file's checksum did not match the md5_checksum parameter
PHOTO_INVALID_DIMENSIONS The photo dimensions are invalid
FIELD_NAME_INVALID The field with the name FIELD_NAME is invalid
FIELD_NAME_EMPTY The field with the name FIELD_NAME is missing
@ -41,7 +41,7 @@ PERSISTENT_TIMESTAMP_EMPTY The pts is empty
CDN_METHOD_INVALID The method can't be used on CDN DCs
VOLUME_LOC_NOT_FOUND The volume location can't be found
FILE_ID_INVALID The file id is invalid
LOCATION_INVALID The file location is invalid
LOCATION_INVALID The file address is invalid
CHAT_ADMIN_REQUIRED The method requires chat admin privileges
PHONE_NUMBER_BANNED The phone number is banned
ABOUT_TOO_LONG The about text is too long
@ -106,4 +106,21 @@ CHAT_LINK_EXISTS The action failed because the supergroup is linked to a channel
LINK_NOT_MODIFIED The chat link was not modified because you tried to link to the same target
BROADCAST_ID_INVALID The channel is invalid
MEGAGROUP_ID_INVALID The supergroup is invalid
BUTTON_DATA_INVALID The button callback data contains invalid data or exceeds 64 bytes
START_PARAM_INVALID The start parameter is invalid
ARTICLE_TITLE_EMPTY The article title is empty
FILE_PART_TOO_BIG The size limit (512 KB) for the content of the file part has been exceeded
FILE_PART_EMPTY The file part sent is empty
FILE_PART_SIZE_INVALID 512 KB cannot be evenly divided by part_size
FILE_PART_SIZE_CHANGED The part size is different from the size of one of the previous parts in the same file
FILE_MIGRATE_X The file is in Data Center No. {x}
RESULT_TYPE_INVALID The result type is invalid
PHOTO_THUMB_URL_EMPTY The photo thumb URL is empty
PHOTO_THUMB_URL_INVALID The photo thumb URL is invalid
PHOTO_CONTENT_URL_EMPTY The photo content URL is empty
PHOTO_CONTENT_TYPE_INVALID The photo content type is invalid
WEBDOCUMENT_INVALID The web document is invalid
WEBDOCUMENT_URL_EMPTY The web document URL is empty
WEBDOCUMENT_URL_INVALID The web document URL is invalid
WEBDOCUMENT_MIME_INVALID The web document mime type is invalid
BUTTON_URL_INVALID The button url is invalid
1 id message
12 USERS_TOO_FEW Not enough users (to create a chat, for example)
13 USERS_TOO_MUCH The maximum number of users has been exceeded (to create a chat, for example)
14 TYPE_CONSTRUCTOR_INVALID The type constructor is invalid
15 FILE_PART_INVALID The file part number is invalid The file part number is invalid. The value is not between 0 and 2999
16 FILE_PARTS_INVALID The number of file parts is invalid Invalid number of parts. The value is not between 1 and 3000
17 FILE_PART_X_MISSING Part {x} of the file is missing from storage
18 MD5_CHECKSUM_INVALID The MD5 checksums do not match The file's checksum did not match the md5_checksum parameter
19 PHOTO_INVALID_DIMENSIONS The photo dimensions are invalid
20 FIELD_NAME_INVALID The field with the name FIELD_NAME is invalid
21 FIELD_NAME_EMPTY The field with the name FIELD_NAME is missing
41 CDN_METHOD_INVALID The method can't be used on CDN DCs
42 VOLUME_LOC_NOT_FOUND The volume location can't be found
43 FILE_ID_INVALID The file id is invalid
44 LOCATION_INVALID The file location is invalid The file address is invalid
45 CHAT_ADMIN_REQUIRED The method requires chat admin privileges
46 PHONE_NUMBER_BANNED The phone number is banned
47 ABOUT_TOO_LONG The about text is too long
106 LINK_NOT_MODIFIED The chat link was not modified because you tried to link to the same target
107 BROADCAST_ID_INVALID The channel is invalid
108 MEGAGROUP_ID_INVALID The supergroup is invalid
109 BUTTON_DATA_INVALID The button callback data contains invalid data or exceeds 64 bytes
110 START_PARAM_INVALID The start parameter is invalid
111 ARTICLE_TITLE_EMPTY The article title is empty
112 FILE_PART_TOO_BIG The size limit (512 KB) for the content of the file part has been exceeded
113 FILE_PART_EMPTY The file part sent is empty
114 FILE_PART_SIZE_INVALID 512 KB cannot be evenly divided by part_size
115 FILE_PART_SIZE_CHANGED The part size is different from the size of one of the previous parts in the same file
116 FILE_MIGRATE_X The file is in Data Center No. {x}
117 RESULT_TYPE_INVALID The result type is invalid
118 PHOTO_THUMB_URL_EMPTY The photo thumb URL is empty
119 PHOTO_THUMB_URL_INVALID The photo thumb URL is invalid
120 PHOTO_CONTENT_URL_EMPTY The photo content URL is empty
121 PHOTO_CONTENT_TYPE_INVALID The photo content type is invalid
122 WEBDOCUMENT_INVALID The web document is invalid
123 WEBDOCUMENT_URL_EMPTY The web document URL is empty
124 WEBDOCUMENT_URL_INVALID The web document URL is invalid
125 WEBDOCUMENT_MIME_INVALID The web document mime type is invalid
126 BUTTON_URL_INVALID The button url is invalid

View File

@ -1,3 +1,4 @@
id message
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
STICKERSET_INVALID The sticker set is invalid
1 id message
2 AUTH_KEY_DUPLICATED Authorization error - you must delete your session file and log in again with your phone number
3 FILEREF_UPGRADE_NEEDED The file reference has expired - you must obtain the original media message
4 STICKERSET_INVALID The sticker set is invalid

View File

@ -10,3 +10,4 @@ WORKER_BUSY_TOO_LONG_RETRY Telegram is having internal problems. Please try agai
INTERDC_X_CALL_ERROR Telegram is having internal problems at DC{x}. Please try again later
INTERDC_X_CALL_RICH_ERROR Telegram is having internal problems at DC{x}. Please try again later
FOLDER_DEAC_AUTOFIX_ALL Telegram is having internal problems. Please try again later
MSGID_DECREASE_RETRY Telegram is having internal problems. Please try again later
1 id message
10 INTERDC_X_CALL_ERROR Telegram is having internal problems at DC{x}. Please try again later
11 INTERDC_X_CALL_RICH_ERROR Telegram is having internal problems at DC{x}. Please try again later
12 FOLDER_DEAC_AUTOFIX_ALL Telegram is having internal problems. Please try again later
13 MSGID_DECREASE_RETRY Telegram is having internal problems. Please try again later

View File

@ -35,8 +35,7 @@ backwards-incompatible changes made in that version.
When upgrading to a new version of Pyrogram, you will need to check all the breaking changes in order to find
incompatible code in your application, but also to take advantage of new features and improvements.
Releases
--------
**Contents**
""".lstrip("\n")

View File

@ -2,7 +2,7 @@ User-agent: *
Allow: /
Disallow: /dev/*
Disallow: /old/*
Disallow: /dev*
Disallow: /v0*
Sitemap: https://docs.pyrogram.org/sitemap.xml

View File

@ -1,142 +0,0 @@
Bound Methods
=============
Some Pyrogram types define what are called bound methods. Bound methods are functions attached to a class which are
accessed via an instance of that class. They make it even easier to call specific methods by automatically inferring
some of the required arguments.
.. code-block:: python
:emphasize-lines: 8
from pyrogram import Client
app = Client("my_account")
@app.on_message()
def hello(client, message)
message.reply("hi")
app.run()
.. currentmodule:: pyrogram
Index
-----
Message
^^^^^^^
.. hlist::
:columns: 3
- :meth:`~Message.click`
- :meth:`~Message.delete`
- :meth:`~Message.download`
- :meth:`~Message.edit`
- :meth:`~Message.edit_caption`
- :meth:`~Message.edit_media`
- :meth:`~Message.edit_reply_markup`
- :meth:`~Message.forward`
- :meth:`~Message.pin`
- :meth:`~Message.reply`
- :meth:`~Message.reply_animation`
- :meth:`~Message.reply_audio`
- :meth:`~Message.reply_cached_media`
- :meth:`~Message.reply_chat_action`
- :meth:`~Message.reply_contact`
- :meth:`~Message.reply_document`
- :meth:`~Message.reply_game`
- :meth:`~Message.reply_inline_bot_result`
- :meth:`~Message.reply_location`
- :meth:`~Message.reply_media_group`
- :meth:`~Message.reply_photo`
- :meth:`~Message.reply_poll`
- :meth:`~Message.reply_sticker`
- :meth:`~Message.reply_venue`
- :meth:`~Message.reply_video`
- :meth:`~Message.reply_video_note`
- :meth:`~Message.reply_voice`
Chat
^^^^
.. hlist::
:columns: 2
- :meth:`~Chat.archive`
- :meth:`~Chat.unarchive`
User
^^^^
.. hlist::
:columns: 2
- :meth:`~User.archive`
- :meth:`~User.unarchive`
CallbackQuery
^^^^^^^^^^^^^
.. hlist::
:columns: 2
- :meth:`~CallbackQuery.answer`
InlineQuery
^^^^^^^^^^^
.. hlist::
:columns: 2
- :meth:`~InlineQuery.answer`
-----
Details
-------
.. Message
.. automethod:: Message.click()
.. automethod:: Message.delete()
.. automethod:: Message.download()
.. automethod:: Message.edit()
.. automethod:: Message.edit_caption()
.. automethod:: Message.edit_media()
.. automethod:: Message.edit_reply_markup()
.. automethod:: Message.forward()
.. automethod:: Message.pin()
.. automethod:: Message.reply()
.. automethod:: Message.reply_animation()
.. automethod:: Message.reply_audio()
.. automethod:: Message.reply_cached_media()
.. automethod:: Message.reply_chat_action()
.. automethod:: Message.reply_contact()
.. automethod:: Message.reply_document()
.. automethod:: Message.reply_game()
.. automethod:: Message.reply_inline_bot_result()
.. automethod:: Message.reply_location()
.. automethod:: Message.reply_media_group()
.. automethod:: Message.reply_photo()
.. automethod:: Message.reply_poll()
.. automethod:: Message.reply_sticker()
.. automethod:: Message.reply_venue()
.. automethod:: Message.reply_video()
.. automethod:: Message.reply_video_note()
.. automethod:: Message.reply_voice()
.. Chat
.. automethod:: Chat.archive()
.. automethod:: Chat.unarchive()
.. User
.. automethod:: User.archive()
.. automethod:: User.unarchive()
.. CallbackQuery
.. automethod:: CallbackQuery.answer()
.. InlineQuery
.. automethod:: InlineQuery.answer()

View File

@ -1,7 +1,11 @@
Pyrogram Client
===============
This is the Client class. It exposes high-level methods for an easy access to the API.
You have entered the API Reference section where you can find detailed information about Pyrogram's API. The main Client
class, all available methods and types, filters, handlers, decorators and bound-methods detailed descriptions can be
found starting from this page.
This page is about the Client class, which exposes high-level methods for an easy access to the API.
.. code-block:: python
:emphasize-lines: 1-3

View File

@ -6,7 +6,7 @@ deserve a dedicated page.
Decorators are able to register callback functions for handling updates in a much easier and cleaner way compared to
:doc:`Handlers <handlers>`; they do so by instantiating the correct handler and calling
:meth:`~pyrogram.Client.add_handler`, automatically. All you need to do is adding the decorators on top of your
:meth:`~pyrogram.Client.add_handler` automatically. All you need to do is adding the decorators on top of your
functions.
.. code-block:: python

View File

@ -2,10 +2,7 @@ Update Handlers
===============
Handlers are used to instruct Pyrogram about which kind of updates you'd like to handle with your callback functions.
For a much more convenient way of registering callback functions have a look at :doc:`Decorators <decorators>` instead.
In case you decided to manually create a handler, use :class:`~pyrogram.Client.add_handler` to register
it.
.. code-block:: python
:emphasize-lines: 1, 10

View File

@ -1,282 +0,0 @@
Available Methods
=================
All Pyrogram methods listed here are bound to a :class:`~pyrogram.Client` instance.
.. code-block:: python
:emphasize-lines: 6
from pyrogram import Client
app = Client("my_account")
with app:
app.send_message("haskell", "hi")
.. currentmodule:: pyrogram
Index
-----
Utilities
^^^^^^^^^
.. hlist::
:columns: 4
- :meth:`~Client.start`
- :meth:`~Client.stop`
- :meth:`~Client.restart`
- :meth:`~Client.idle`
- :meth:`~Client.run`
- :meth:`~Client.add_handler`
- :meth:`~Client.remove_handler`
- :meth:`~Client.stop_transmission`
Messages
^^^^^^^^
.. hlist::
:columns: 3
- :meth:`~Client.send_message`
- :meth:`~Client.forward_messages`
- :meth:`~Client.send_photo`
- :meth:`~Client.send_audio`
- :meth:`~Client.send_document`
- :meth:`~Client.send_sticker`
- :meth:`~Client.send_animated_sticker`
- :meth:`~Client.send_video`
- :meth:`~Client.send_animation`
- :meth:`~Client.send_voice`
- :meth:`~Client.send_video_note`
- :meth:`~Client.send_media_group`
- :meth:`~Client.send_location`
- :meth:`~Client.send_venue`
- :meth:`~Client.send_contact`
- :meth:`~Client.send_cached_media`
- :meth:`~Client.send_chat_action`
- :meth:`~Client.edit_message_text`
- :meth:`~Client.edit_message_caption`
- :meth:`~Client.edit_message_reply_markup`
- :meth:`~Client.edit_message_media`
- :meth:`~Client.delete_messages`
- :meth:`~Client.get_messages`
- :meth:`~Client.get_history`
- :meth:`~Client.get_history_count`
- :meth:`~Client.read_history`
- :meth:`~Client.iter_history`
- :meth:`~Client.send_poll`
- :meth:`~Client.vote_poll`
- :meth:`~Client.stop_poll`
- :meth:`~Client.retract_vote`
- :meth:`~Client.download_media`
Chats
^^^^^
.. hlist::
:columns: 3
- :meth:`~Client.join_chat`
- :meth:`~Client.leave_chat`
- :meth:`~Client.kick_chat_member`
- :meth:`~Client.unban_chat_member`
- :meth:`~Client.restrict_chat_member`
- :meth:`~Client.promote_chat_member`
- :meth:`~Client.export_chat_invite_link`
- :meth:`~Client.set_chat_photo`
- :meth:`~Client.delete_chat_photo`
- :meth:`~Client.set_chat_title`
- :meth:`~Client.set_chat_description`
- :meth:`~Client.pin_chat_message`
- :meth:`~Client.unpin_chat_message`
- :meth:`~Client.get_chat`
- :meth:`~Client.get_chat_member`
- :meth:`~Client.get_chat_members`
- :meth:`~Client.get_chat_members_count`
- :meth:`~Client.iter_chat_members`
- :meth:`~Client.get_dialogs`
- :meth:`~Client.iter_dialogs`
- :meth:`~Client.get_dialogs_count`
- :meth:`~Client.restrict_chat`
- :meth:`~Client.update_chat_username`
- :meth:`~Client.archive_chats`
- :meth:`~Client.unarchive_chats`
Users
^^^^^
.. hlist::
:columns: 3
- :meth:`~Client.get_me`
- :meth:`~Client.get_users`
- :meth:`~Client.get_profile_photos`
- :meth:`~Client.get_profile_photos_count`
- :meth:`~Client.iter_profile_photos`
- :meth:`~Client.set_profile_photo`
- :meth:`~Client.delete_profile_photos`
- :meth:`~Client.update_username`
- :meth:`~Client.get_user_dc`
Contacts
^^^^^^^^
.. hlist::
:columns: 3
- :meth:`~Client.add_contacts`
- :meth:`~Client.get_contacts`
- :meth:`~Client.get_contacts_count`
- :meth:`~Client.delete_contacts`
Password
^^^^^^^^
.. hlist::
:columns: 3
- :meth:`~Client.enable_cloud_password`
- :meth:`~Client.change_cloud_password`
- :meth:`~Client.remove_cloud_password`
Bots
^^^^
.. hlist::
:columns: 3
- :meth:`~Client.get_inline_bot_results`
- :meth:`~Client.send_inline_bot_result`
- :meth:`~Client.answer_callback_query`
- :meth:`~Client.answer_inline_query`
- :meth:`~Client.request_callback_answer`
- :meth:`~Client.send_game`
- :meth:`~Client.set_game_score`
- :meth:`~Client.get_game_high_scores`
Advanced Usage (Raw API)
^^^^^^^^^^^^^^^^^^^^^^^^
Learn more about these methods at :doc:`Advanced Usage <../topics/advanced-usage>`.
.. hlist::
:columns: 4
- :meth:`~Client.send`
- :meth:`~Client.resolve_peer`
- :meth:`~Client.save_file`
-----
Details
-------
.. Utilities
.. automethod:: Client.start()
.. automethod:: Client.stop()
.. automethod:: Client.restart()
.. automethod:: Client.idle()
.. automethod:: Client.run()
.. automethod:: Client.add_handler()
.. automethod:: Client.remove_handler()
.. automethod:: Client.stop_transmission()
.. Messages
.. automethod:: Client.send_message()
.. automethod:: Client.forward_messages()
.. automethod:: Client.send_photo()
.. automethod:: Client.send_audio()
.. automethod:: Client.send_document()
.. automethod:: Client.send_sticker()
.. automethod:: Client.send_animated_sticker()
.. automethod:: Client.send_video()
.. automethod:: Client.send_animation()
.. automethod:: Client.send_voice()
.. automethod:: Client.send_video_note()
.. automethod:: Client.send_media_group()
.. automethod:: Client.send_location()
.. automethod:: Client.send_venue()
.. automethod:: Client.send_contact()
.. automethod:: Client.send_cached_media()
.. automethod:: Client.send_chat_action()
.. automethod:: Client.edit_message_text()
.. automethod:: Client.edit_message_caption()
.. automethod:: Client.edit_message_reply_markup()
.. automethod:: Client.edit_message_media()
.. automethod:: Client.delete_messages()
.. automethod:: Client.get_messages()
.. automethod:: Client.get_history()
.. automethod:: Client.get_history_count()
.. automethod:: Client.read_history()
.. automethod:: Client.iter_history()
.. automethod:: Client.send_poll()
.. automethod:: Client.vote_poll()
.. automethod:: Client.stop_poll()
.. automethod:: Client.retract_vote()
.. automethod:: Client.download_media()
.. Chats
.. automethod:: Client.join_chat()
.. automethod:: Client.leave_chat()
.. automethod:: Client.kick_chat_member()
.. automethod:: Client.unban_chat_member()
.. automethod:: Client.restrict_chat_member()
.. automethod:: Client.promote_chat_member()
.. automethod:: Client.export_chat_invite_link()
.. automethod:: Client.set_chat_photo()
.. automethod:: Client.delete_chat_photo()
.. automethod:: Client.set_chat_title()
.. automethod:: Client.set_chat_description()
.. automethod:: Client.pin_chat_message()
.. automethod:: Client.unpin_chat_message()
.. automethod:: Client.get_chat()
.. automethod:: Client.get_chat_member()
.. automethod:: Client.get_chat_members()
.. automethod:: Client.get_chat_members_count()
.. automethod:: Client.iter_chat_members()
.. automethod:: Client.get_dialogs()
.. automethod:: Client.iter_dialogs()
.. automethod:: Client.get_dialogs_count()
.. automethod:: Client.restrict_chat()
.. automethod:: Client.update_chat_username()
.. automethod:: Client.archive_chats()
.. automethod:: Client.unarchive_chats()
.. Users
.. automethod:: Client.get_me()
.. automethod:: Client.get_users()
.. automethod:: Client.get_profile_photos()
.. automethod:: Client.get_profile_photos_count()
.. automethod:: Client.iter_profile_photos()
.. automethod:: Client.set_profile_photo()
.. automethod:: Client.delete_profile_photos()
.. automethod:: Client.update_username()
.. automethod:: Client.get_user_dc()
.. Contacts
.. automethod:: Client.add_contacts()
.. automethod:: Client.get_contacts()
.. automethod:: Client.get_contacts_count()
.. automethod:: Client.delete_contacts()
.. Password
.. automethod:: Client.enable_cloud_password()
.. automethod:: Client.change_cloud_password()
.. automethod:: Client.remove_cloud_password()
.. Bots
.. automethod:: Client.get_inline_bot_results()
.. automethod:: Client.send_inline_bot_result()
.. automethod:: Client.answer_callback_query()
.. automethod:: Client.answer_inline_query()
.. automethod:: Client.request_callback_answer()
.. automethod:: Client.send_game()
.. automethod:: Client.set_game_score()
.. automethod:: Client.get_game_high_scores()
.. Advanced Usage
.. automethod:: Client.send()
.. automethod:: Client.resolve_peer()
.. automethod:: Client.save_file()

View File

@ -1,170 +0,0 @@
Available Types
===============
All Pyrogram types listed here are accessible through the main package directly.
.. code-block:: python
:emphasize-lines: 1
from pyrogram import User, Message, ...
.. note::
**Optional** fields may not exist when irrelevant -- i.e.: they will contain the value of ``None`` and aren't shown
when, for example, using ``print()``.
.. currentmodule:: pyrogram
Index
-----
Users & Chats
^^^^^^^^^^^^^
.. hlist::
:columns: 5
- :class:`User`
- :class:`UserStatus`
- :class:`Chat`
- :class:`ChatPreview`
- :class:`ChatPhoto`
- :class:`ChatMember`
- :class:`ChatPermissions`
- :class:`Dialog`
Messages & Media
^^^^^^^^^^^^^^^^
.. hlist::
:columns: 5
- :class:`Message`
- :class:`MessageEntity`
- :class:`Photo`
- :class:`Thumbnail`
- :class:`Audio`
- :class:`Document`
- :class:`Animation`
- :class:`Video`
- :class:`Voice`
- :class:`VideoNote`
- :class:`Contact`
- :class:`Location`
- :class:`Venue`
- :class:`Sticker`
- :class:`Game`
- :class:`Poll`
- :class:`PollOption`
Bots & Keyboards
^^^^^^^^^^^^^^^^
.. hlist::
:columns: 4
- :class:`ReplyKeyboardMarkup`
- :class:`KeyboardButton`
- :class:`ReplyKeyboardRemove`
- :class:`InlineKeyboardMarkup`
- :class:`InlineKeyboardButton`
- :class:`ForceReply`
- :class:`CallbackQuery`
- :class:`GameHighScore`
- :class:`CallbackGame`
Input Media
^^^^^^^^^^^
.. hlist::
:columns: 4
- :class:`InputMedia`
- :class:`InputMediaPhoto`
- :class:`InputMediaVideo`
- :class:`InputMediaAudio`
- :class:`InputMediaAnimation`
- :class:`InputMediaDocument`
- :class:`InputPhoneContact`
Inline Mode
^^^^^^^^^^^
.. hlist::
:columns: 3
- :class:`InlineQuery`
- :class:`InlineQueryResult`
- :class:`InlineQueryResultArticle`
InputMessageContent
^^^^^^^^^^^^^^^^^^^
.. hlist::
:columns: 3
- :class:`InputMessageContent`
- :class:`InputTextMessageContent`
-----
Details
-------
.. User & Chats
.. autoclass:: User()
.. autoclass:: UserStatus()
.. autoclass:: Chat()
.. autoclass:: ChatPreview()
.. autoclass:: ChatPhoto()
.. autoclass:: ChatMember()
.. autoclass:: ChatPermissions()
.. autoclass:: Dialog()
.. Messages & Media
.. autoclass:: Message()
.. autoclass:: MessageEntity()
.. autoclass:: Photo()
.. autoclass:: Thumbnail()
.. autoclass:: Audio()
.. autoclass:: Document()
.. autoclass:: Animation()
.. autoclass:: Video()
.. autoclass:: Voice()
.. autoclass:: VideoNote()
.. autoclass:: Contact()
.. autoclass:: Location()
.. autoclass:: Venue()
.. autoclass:: Sticker()
.. autoclass:: Game()
.. autoclass:: Poll()
.. autoclass:: PollOption()
.. Bots & Keyboards
.. autoclass:: ReplyKeyboardMarkup()
.. autoclass:: KeyboardButton()
.. autoclass:: ReplyKeyboardRemove()
.. autoclass:: InlineKeyboardMarkup()
.. autoclass:: InlineKeyboardButton()
.. autoclass:: ForceReply()
.. autoclass:: CallbackQuery()
.. autoclass:: GameHighScore()
.. autoclass:: CallbackGame()
.. Input Media
.. autoclass:: InputMedia()
.. autoclass:: InputMediaPhoto()
.. autoclass:: InputMediaVideo()
.. autoclass:: InputMediaAudio()
.. autoclass:: InputMediaAnimation()
.. autoclass:: InputMediaDocument()
.. autoclass:: InputPhoneContact()
.. Inline Mode
.. autoclass:: InlineQuery()
.. autoclass:: InlineQueryResult()
.. autoclass:: InlineQueryResultArticle()
.. InputMessageContent
.. autoclass:: InputMessageContent()
.. autoclass:: InputTextMessageContent()

View File

@ -66,3 +66,15 @@ html_theme_options = {
html_logo = "_images/pyrogram.png"
html_favicon = "_images/favicon.ico"
latex_engine = "xelatex"
latex_logo = "_images/pyrogram.png"
latex_elements = {
"pointsize": "12pt",
"fontpkg": r"""
\setmainfont{Noto Sans}
\setsansfont{Roboto Slab}
\setmonofont{Ubuntu Mono}
"""
}

View File

@ -1,6 +1,9 @@
Pyrogram FAQ
============
.. role:: strike
:class: strike
This FAQ page provides answers to common questions about Pyrogram and, to some extent, Telegram in general.
.. tip::
@ -65,7 +68,7 @@ To challenge the framework, the creator is constantly keeping a public
`welcome bot <https://github.com/pyrogram/pyrogram/blob/develop/examples/welcomebot.py>`_ online 24/7 on his own,
relatively-busy account for well over a year now.
In addition to that, about six months ago, one of the most popular Telegram bot has been rewritten
In addition to that, about six months ago, one of the most popular Telegram bot has been rewritten from scratch
:doc:`using Pyrogram <powered-by>` and is serving more than 200,000 Monthly Active Users since
then, uninterruptedly and without any need for restarting it.
@ -102,9 +105,11 @@ one: ``CAADBAADyg4AAvLQYAEYD4F7vcZ43AI``.
Can I use Bot API's file_ids in Pyrogram?
-----------------------------------------
Definitely! All file ids you might have taken from the Bot API are 100% compatible and re-usable in Pyrogram...
:strike:`Definitely! All file ids you might have taken from the Bot API are 100% compatible and re-usable in Pyrogram.`
...at least for now.
Starting from :doc:`Pyrogram v0.14.1 (Layer 100) <releases/v0.14.1>`, the file_id format of all photo-like objects has
changed. Types affected are: :obj:`~pyrogram.Thumbnail`, :obj:`~pyrogram.ChatPhoto` and :obj:`~pyrogram.Photo`. Any
other file id remains compatible with the Bot API.
Telegram is slowly changing some server's internals and it's doing it in such a way that file ids are going to break
inevitably. Not only this, but it seems that the new, hypothetical, file ids could also possibly expire at anytime, thus
@ -129,8 +134,8 @@ If you -- even accidentally -- fail to do so, all the previous session copies wi
and eventually the server will start throwing the error ``[406 AUTH_KEY_DUPLICATED]``, inviting you to login again.
Why is that so? Because the server has recognized two identical sessions are running in two different locations, and
concludes it could possibly be due to a cloned/stolen device. Having the session ended in such occasions will protect
the user's privacy.
concludes it could possibly be due to a cloned/stolen device. Having the session terminated in such occasions will
protect the user's privacy.
So, the only correct way to run multiple clients on the same account is authorizing your account (either user or bot)
from the beginning every time, and use one separate session for each parallel client you are going to use.
@ -139,7 +144,8 @@ I started a client and nothing happens!
---------------------------------------
If you are connecting from Russia, China or Iran :doc:`you need a proxy <topics/proxy>`, because Telegram could be
partially or totally blocked in those countries.
partially or totally blocked in those countries. More information about this block can be found at
`Wikipedia <https://en.wikipedia.org/wiki/Blocking_Telegram_in_Russia>`_.
Another possible cause might be network issues, either yours or Telegram's. To confirm this, add the following code on
the top of your script and run it again. You should see some error mentioning a socket timeout or an unreachable network
@ -156,20 +162,20 @@ fails or not.
What are the IP addresses of Telegram Data Centers?
---------------------------------------------------
The Telegram cloud is currently composed by a decentralized, multi-DC infrastructure (each of which can work
independently) spread in 5 different locations. However, some of the less busy DCs have been lately dismissed and their
IP addresses are now kept as aliases.
The Telegram cloud is currently composed by a decentralized, multi-DC infrastructure (currently 5 DCs, each of which can
work independently) spread in different locations worldwide. However, some of the less busy DCs have been lately
dismissed and their IP addresses are now kept as aliases to the nearest one.
.. csv-table:: Production Environment
:header: ID, Location, IPv4, IPv6
:widths: auto
:align: center
DC1, "MIA, Miami FL, USA", ``149.154.175.50``, ``2001:b28:f23d:f001::a``
DC1, "MIA, Miami FL, USA", ``149.154.175.53``, ``2001:b28:f23d:f001::a``
DC2, "AMS, Amsterdam, NL", ``149.154.167.51``, ``2001:67c:4e8:f002::a``
DC3*, "MIA, Miami FL, USA", ``149.154.175.100``, ``2001:b28:f23d:f003::a``
DC4, "AMS, Amsterdam, NL", ``149.154.167.91``, ``2001:67c:4e8:f004::a``
DC5, "SIN, Singapore, SG", ``91.108.56.149``, ``2001:b28:f23f:f005::a``
DC5, "SIN, Singapore, SG", ``91.108.56.130``, ``2001:b28:f23f:f005::a``
.. csv-table:: Test Environment
:header: ID, Location, IPv4, IPv6
@ -180,9 +186,11 @@ IP addresses are now kept as aliases.
DC2, "AMS, Amsterdam, NL", ``149.154.167.40``, ``2001:67c:4e8:f002::e``
DC3*, "MIA, Miami FL, USA", ``149.154.175.117``, ``2001:b28:f23d:f003::e``
.. centered:: More info about the Test Environment can be found :doc:`here <topics/test-servers>`.
***** Alias DC
More info about the Test Environment can be found :doc:`here <topics/test-servers>`.
Thanks to `@FrayxRulez <https://t.me/tgbetachat/104921>`_ for telling about alias DCs.
I want to migrate my account from DCX to DCY.
---------------------------------------------
@ -200,6 +208,36 @@ mechanism is also `confirmed <https://twitter.com/telegram/status/42713144665519
it's currently not possible to have your account migrated, in any way, simply because the feature was once planned but
not yet implemented.
Thanks to `@gabriel <https://t.me/AnotherGroup/217699>`_ for confirming the feature was not implemented yet.
Why is my client reacting slowly in supergroups?
------------------------------------------------
This issue affects only some supergroups or only some members within the same supergroup. Mostly, it affects supergroups
whose creator's account (and thus the supergroup itself) lives inside a **different DC**, far away from yours, but could
also depend on where a member is connecting from.
Because of how Telegram works internally, every single message you receive from and send to other members must pass
through the creator's DC, and in the worst case where you, the creator and another member all belong to three different
DCs, the other member messages have to go through from its DC to the creator's DC and finally to your DC. This process
will inevitably take its time.
To confirm this theory and see it by yourself, you can test in a supergroup where you are sure all parties live
inside the same DC. In this case the responses will be faster.
Another reason that makes responses come slowly is that messages are **dispatched by priority**. Depending on the kind
of member, some users receive messages faster than others and for big and busy supergroups the delay might become
noticeable, especially if you are among the lower end of the priority list:
1. Creator.
2. Administrators.
3. Bots.
4. Mentioned users.
5. Recent online users.
6. Everyone else.
Thanks to `@Manuel15 <https://t.me/PyrogramChat/76990>`_ for the priority list.
I keep getting PEER_ID_INVALID error!
-------------------------------------
@ -207,9 +245,13 @@ The error in question is ``[400 PEER_ID_INVALID]``, and could mean several thing
- The chat id you tried to use is simply wrong, double check it.
- The chat id refers to a group or channel you are not a member of.
- The chat id refers to a user you have't seen yet (from contacts, groups in common, forwarded messages or private
chats).
- The chat id argument you passed is in form of a string; you have to convert it into an integer with ``int(chat_id)``.
- The chat id refers to a user your current session haven't met yet.
About the last point: in order for you to meet a user and thus communicate with them, you should ask yourself how to
contact people using official apps. The answer is the same for Pyrogram too and involves normal usages such as searching
for usernames, meeting them in a common group, have their phone contacts saved or getting a message mentioning them,
either a forward or a mention in the message text.
UnicodeEncodeError: '<encoding>' codec can't encode …
-----------------------------------------------------
@ -217,7 +259,15 @@ UnicodeEncodeError: '<encoding>' codec can't encode …
Where ``<encoding>`` might be *ascii*, *cp932*, *charmap* or anything else other than **utf-8**. This error usually
shows up when you try to print something and has very little to do with Pyrogram itself as it is strictly related to
your own terminal. To fix it, either find a way to change the encoding settings of your terminal to UTF-8 or switch to a
better one.
better terminal altogether.
Uploading with URLs gives error WEBPAGE_CURL_FAILED
---------------------------------------------------
When uploading media files using an URL, the server automatically tries to download the media and uploads it to the
Telegram cloud. This error usually happens in case the provided URL is not publicly accessible by Telegram itself or the
media exceeds 20 MB in size. In such cases, your only option is to download the media yourself and upload from your
local machine.
My verification code expires immediately!
-----------------------------------------
@ -243,7 +293,7 @@ Having said that, here's a list of what Telegram definitely doesn't like:
- Spam, sending unsolicited messages or adding people to unwanted groups and channels.
- Virtual/VoIP and cheap real numbers, because they are relatively easy to get and likely used for spam/flood.
And here's a good explanation of how, probably, the system works:
And thanks to `@koteeq <https://t.me/koteeq>`_, here's a good explanation of how, probably, the system works:
.. raw:: html
@ -252,8 +302,7 @@ And here's a good explanation of how, probably, the system works:
data-telegram-post="PyrogramChat/69424"
data-width="100%">
</script>
.. centered:: Join the discussion at `@Pyrogram <https://t.me/pyrogram>`_
<br><br>
However, you might be right, and your account was deactivated/limited without any good reason. This could happen because
of mistakes by either the automatic systems or a moderator. In such cases you can kindly email Telegram at

View File

@ -29,7 +29,7 @@ Terms
achieve high quality and availability for services.
RPC
Acronym for Remote Procedure call, that is, a function which gets executed at some remote place (i.e. Telegram
Acronym for Remote Procedure Call, that is, a function which gets executed at some remote place (i.e. Telegram
server) and not in your local machine.
RPCError
@ -58,7 +58,7 @@ Terms
Pyrogram --- to automate some behaviours, like sending messages or reacting to text commands or any other event.
Session
Also known as *login session*, is a strictly personal piece of information created and held by both parties
Also known as *login session*, is a strictly personal piece of data created and held by both parties
(client and server) which is used to grant permission into a single account without having to start a new
authorization process from scratch.

View File

@ -1,6 +1,78 @@
Welcome to Pyrogram
===================
.. toctree::
:hidden:
:caption: Introduction
intro/quickstart
intro/install
intro/setup
.. toctree::
:hidden:
:caption: Getting Started
start/auth
start/invoking
start/updates
start/errors
.. toctree::
:hidden:
:caption: API Reference
api/client
api/methods/index
api/types/index
api/bound-methods/index
api/handlers
api/decorators
api/errors
api/filters
.. toctree::
:hidden:
:caption: Topic Guides
topics/use-filters
topics/create-filters
topics/more-on-updates
topics/config-file
topics/smart-plugins
topics/auto-auth
topics/session-settings
topics/tgcrypto
topics/storage-engines
topics/text-formatting
topics/serializing
topics/proxy
topics/scheduling
topics/bots-interaction
topics/mtproto-vs-botapi
topics/debugging
topics/test-servers
topics/advanced-usage
topics/voice-calls
.. toctree::
:hidden:
:caption: Meta
faq
glossary
powered-by
support-pyrogram
license
releases/index
.. toctree::
:hidden:
:caption: Telegram API
telegram/functions/index
telegram/types/index
.. raw:: html
<div align="center">
@ -35,7 +107,7 @@ Welcome to Pyrogram
@app.on_message(Filters.private)
def hello(client, message):
message.reply("Hello {}".format(message.from_user.first_name))
message.reply_text("Hello {}".format(message.from_user.first_name))
app.run()
@ -54,7 +126,7 @@ order using the :guilabel:`Next` button at the end of each page. Here below you
relevant pages for a quick access.
First Steps
-----------
^^^^^^^^^^^
.. hlist::
:columns: 2
@ -65,18 +137,18 @@ First Steps
- :doc:`Error Handling <start/errors>`: How to handle API errors correctly.
API Reference
-------------
^^^^^^^^^^^^^
.. hlist::
:columns: 2
- :doc:`Pyrogram Client <api/client>`: Reference details about the Client class.
- :doc:`Available Methods <api/methods>`: List of available high-level methods.
- :doc:`Available Types <api/types>`: List of available high-level types.
- :doc:`Bound Methods <api/bound-methods>`: List of convenient bound methods.
- :doc:`Available Methods <api/methods/index>`: List of available high-level methods.
- :doc:`Available Types <api/types/index>`: List of available high-level types.
- :doc:`Bound Methods <api/bound-methods/index>`: List of convenient bound methods.
Meta
----
^^^^
.. hlist::
:columns: 2
@ -88,74 +160,4 @@ Meta
- :doc:`About the License <license>`: Information about the Project license.
- :doc:`Release Notes <releases/index>`: Release notes for Pyrogram releases.
.. toctree::
:hidden:
:caption: Introduction
intro/quickstart
intro/install
intro/setup
.. toctree::
:hidden:
:caption: Getting Started
start/auth
start/invoking
start/updates
start/errors
.. toctree::
:hidden:
:caption: API Reference
api/client
api/methods
api/types
api/bound-methods
api/handlers
api/decorators
api/filters
api/errors
.. toctree::
:hidden:
:caption: Topic Guides
topics/use-filters
topics/create-filters
topics/more-on-updates
topics/config-file
topics/smart-plugins
topics/auto-auth
topics/session-settings
topics/tgcrypto
topics/text-formatting
topics/serialize
topics/proxy
topics/bots-interaction
topics/mtproto-vs-botapi
topics/debugging
topics/test-servers
topics/advanced-usage
topics/voice-calls
.. toctree::
:hidden:
:caption: Meta
faq
glossary
powered-by
support-pyrogram
license
releases/index
.. toctree::
:hidden:
:caption: Telegram API
telegram/functions/index
telegram/types/index
Last updated on |today|

View File

@ -47,7 +47,7 @@ Pyrogram heavily depends on IO-bound network code (it's a cloud-based messaging
where asyncio shines the most by providing extra performance and efficiency while running on a single OS-level thread
only.
**A fully asynchronous variant of Pyrogram is therefore available** (Python 3.5.3+ required).
**A fully asynchronous variant of Pyrogram is therefore available** (Python 3.5.3 or higher is required).
Use this command to install (note "asyncio.zip" in the link):
.. code-block:: text

View File

@ -46,4 +46,4 @@ In the next few pages of the introduction, we'll take a much more in-depth look
Feeling eager to continue? You can take a shortcut to :doc:`Calling Methods <../start/invoking>` and come back later to
learn some more details.
.. _community: //t.me/Pyrogram
.. _community: https://t.me/Pyrogram

View File

@ -29,9 +29,9 @@ Configuration
Having the API key from the previous step in handy, we can now begin to configure a Pyrogram project.
There are two ways to do so, and you can choose what fits better for you:
- First option (recommended): create a new ``config.ini`` file at the root of your working directory, copy-paste the
following and replace the **api_id** and **api_hash** values with your own. This is the preferred method because
allows you to keep your credentials out of your code without having to deal with how to load them:
- First option (recommended): create a new ``config.ini`` file next to your main script, copy-paste the following and
replace the **api_id** and **api_hash** values with your own. This is the preferred method because allows you to
keep your credentials out of your code without having to deal with how to load them:
.. code-block:: ini

View File

@ -2,7 +2,7 @@ About the License
=================
.. image:: https://www.gnu.org/graphics/lgplv3-with-text-154x68.png
:align: right
:align: left
Pyrogram is free software and is currently licensed under the terms of the
`GNU Lesser General Public License v3 or later (LGPLv3+)`_. In short: you may use, redistribute and/or modify it

View File

@ -28,7 +28,7 @@ Error Categories
----------------
The ``RPCError`` packs together all the possible errors Telegram could raise, but to make things tidier, Pyrogram
provides categories of errors, which are named after the common HTTP errors and subclass-ed from the RPCError:
provides categories of errors, which are named after the common HTTP errors and are subclass-ed from the RPCError:
.. code-block:: python
@ -71,14 +71,22 @@ RPCError, thus building a class of error hierarchy such as this:
Unknown Errors
--------------
In case Pyrogram does not know anything yet about a specific error, it raises a special ``520 - UnknownError`` exception
and logs it in the ``unknown_errors.txt`` file. Users are invited to report these unknown errors.
In case Pyrogram does not know anything about a specific error yet, it raises a generic error from its known category,
for example, an unknown error with error code ``400``, will be raised as a ``BadRequest``. This way you can catch the
whole category of errors and be sure to also handle these unknown errors.
In case a whole class of errors is unknown (that is, an error code that is unknown), Pyrogram will raise a special
``520 UnknownError`` exception.
In both cases, Pyrogram will log them in the ``unknown_errors.txt`` file. Users are invited to report
these unknown errors in the `discussion group <https://t.me/pyrogram>`_.
Errors with Values
------------------
Exception objects may also contain some informative values. For example, ``FloodWait`` holds the amount of seconds you
have to wait before you can try again. The value is always stored in the ``x`` field of the returned exception object:
have to wait before you can try again, some other errors contain the DC number on which the request must be repeated on.
The value is stored in the ``x`` attribute of the exception object:
.. code-block:: python
@ -88,4 +96,4 @@ have to wait before you can try again. The value is always stored in the ``x`` f
try:
...
except FloodWait as e:
time.sleep(e.x) # Wait before trying again
time.sleep(e.x) # Wait "x" seconds before continuing

View File

@ -45,7 +45,9 @@ arrives:
app.run()
#. Let's examine these four new pieces. First one: a callback function we defined which accepts two arguments -
Let's examine these four new pieces.
#. A callback function we defined which accepts two arguments -
*(client, message)*. This will be the function that gets executed every time a new message arrives and Pyrogram will
call that function by passing the client instance and the new message instance as argument.
@ -54,14 +56,14 @@ arrives:
def my_function(client, message):
print(message)
#. Second one: the :class:`~pyrogram.MessageHandler`. This object tells Pyrogram the function we defined above must
only handle updates that are in form of a :class:`~pyrogram.Message`:
#. The :class:`~pyrogram.MessageHandler`. This object tells Pyrogram the function we defined above must only handle
updates that are in form of a :class:`~pyrogram.Message`:
.. code-block:: python
my_handler = MessageHandler(my_function)
#. Third: the method :meth:`~pyrogram.Client.add_handler`. This method is used to actually register the handler and let
#. The method :meth:`~pyrogram.Client.add_handler`. This method is used to actually register the handler and let
Pyrogram know it needs to be taken into consideration when new updates arrive and the internal dispatching phase
begins.
@ -69,7 +71,7 @@ arrives:
app.add_handler(my_handler)
#. Last one, the :meth:`~pyrogram.Client.run` method. What this does is simply call :meth:`~pyrogram.Client.start` and
#. The :meth:`~pyrogram.Client.run` method. What this does is simply call :meth:`~pyrogram.Client.start` and
a special method :meth:`~pyrogram.Client.idle` that keeps your main scripts alive until you press ``CTRL+C``; the
client will be automatically stopped after that.
@ -96,14 +98,3 @@ to do so is by decorating your callback function with the :meth:`~pyrogram.Clien
app.run()
.. note::
Due to how these decorators work in Pyrogram, they will wrap your defined callback function in a tuple consisting of
``(handler, group)``; this will be the value held by your function identifier (e.g.: *my_function* from the example
above).
In case, for some reason, you want to get your own function back after it has been decorated, you need to access
``my_function[0].callback``, that is, the *callback* field of the *handler* object which is the first element in the
tuple, accessed by bracket notation *[0]*.

View File

@ -1,9 +1,9 @@
Advanced Usage
==============
Pyrogram's API, which consists of well documented convenience :doc:`methods <../api/methods>` and facade
:doc:`types <../api/types>`, exists to provide a much easier interface to the undocumented and often confusing Telegram
API.
Pyrogram's API, which consists of well documented convenience :doc:`methods <../api/methods/index>` and facade
:doc:`types <../api/types/index>`, exists to provide a much easier interface to the undocumented and often confusing
Telegram API.
In this section, you'll be shown the alternative way of communicating with Telegram using Pyrogram: the main "raw"
Telegram API with its functions and types.
@ -23,21 +23,21 @@ some pitfalls to take into consideration when working with the raw API.
Every available high-level methods in Pyrogram is built on top of these raw functions.
Nothing stops you from using the raw functions only, but they are rather complex and
:doc:`plenty of them <../api/methods>` are already re-implemented by providing a much simpler and cleaner interface
which is very similar to the Bot API (yet much more powerful).
:doc:`plenty of them <../api/methods/index>` are already re-implemented by providing a much simpler and cleaner
interface which is very similar to the Bot API (yet much more powerful).
If you think a raw function should be wrapped and added as a high-level method, feel free to ask in our Community_!
Invoking Functions
^^^^^^^^^^^^^^^^^^
Unlike the :doc:`methods <../api/methods>` found in Pyrogram's API, which can be called in the usual simple way,
Unlike the :doc:`methods <../api/methods/index>` found in Pyrogram's API, which can be called in the usual simple way,
functions to be invoked from the raw Telegram API have a different way of usage and are more complex.
First of all, both :doc:`raw functions <../telegram/functions/index>` and :doc:`raw types <../telegram/types/index>` live in their
respective packages (and sub-packages): ``pyrogram.api.functions``, ``pyrogram.api.types``. They all exist as Python
classes, meaning you need to create an instance of each every time you need them and fill them in with the correct
values using named arguments.
First of all, both :doc:`raw functions <../telegram/functions/index>` and :doc:`raw types <../telegram/types/index>`
live in their respective packages (and sub-packages): ``pyrogram.api.functions``, ``pyrogram.api.types``. They all exist
as Python classes, meaning you need to create an instance of each every time you need them and fill them in with the
correct values using named arguments.
Next, to actually invoke the raw function you have to use the :meth:`~pyrogram.Client.send` method provided by the
Client class and pass the function object you created.

View File

@ -24,7 +24,7 @@ button:
app.send_message(
"username", # Change this to your username or id
"Pyrogram's custom filter test",
"Pyrogram custom filter test",
reply_markup=InlineKeyboardMarkup(
[[InlineKeyboardButton("Press me", "pyrogram")]]
)
@ -33,61 +33,54 @@ button:
Basic Filters
-------------
For this basic filter we will be using only the first two parameters of :meth:`~pyrogram.Filters.create`.
For this basic filter we will be using only the first parameter of :meth:`~pyrogram.Filters.create`.
The code below creates a simple filter for hardcoded, static callback data. This filter will only allow callback queries
containing "Pyrogram" as data, that is, the function *func* you pass returns True in case the callback query data
equals to ``"Pyrogram"``.
containing "pyrogram" as data, that is, the function *func* you pass returns True in case the callback query data
equals to ``"pyrogram"``.
.. code-block:: python
static_data = Filters.create(
name="StaticdData",
func=lambda flt, query: query.data == "Pyrogram"
)
static_data_filter = Filters.create(lambda _, query: query.data == "pyrogram")
The ``lambda`` operator in python is used to create small anonymous functions and is perfect for this example, the same
could be achieved with a normal function, but we don't really need it as it makes sense only inside the filter's scope:
could be achieved with a normal function, but we don't really need it as it makes sense only inside the filter scope:
.. code-block:: python
def func(flt, query):
return query.data == "Pyrogram"
def func(_, query):
return query.data == "pyrogram"
static_data = Filters.create(
name="StaticData",
func=func
)
static_data_filter = Filters.create(func)
The filter usage remains the same:
.. code-block:: python
@app.on_callback_query(static_data)
@app.on_callback_query(static_data_filter)
def pyrogram_data(_, query):
query.answer("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:`~pyrogram.Filters.create`.
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 named arguments for the :meth:`~pyrogram.Filters.create` method.
This is how a dynamic custom filter looks like:
.. code-block:: python
def dynamic_data(data):
def dynamic_data_filter(data):
return Filters.create(
name="DynamicData",
func=lambda flt, query: flt.data == query.data,
data=data # "data" kwarg is accessed with "flt.data"
lambda flt, query: flt.data == query.data,
data=data # "data" kwarg is accessed with "flt.data" above
)
And its usage:
.. code-block:: python
@app.on_callback_query(dynamic_data("Pyrogram"))
@app.on_callback_query(dynamic_data_filter("pyrogram"))
def pyrogram_data(_, query):
query.answer("it works!")

View File

@ -0,0 +1,87 @@
Scheduling Tasks
================
Scheduling tasks means executing one or more functions periodically at pre-defined intervals or after a delay. This is
useful, for example, to send recurring messages to specific chats or users.
Since there's no built-in task scheduler in Pyrogram, this page will only show examples on how to integrate Pyrogram
with the main Python schedule libraries such as ``schedule`` and ``apscheduler``. For more detailed information, you can
visit and learn from each library documentation.
Using ``schedule``
------------------
- Install with ``pip3 install schedule``
- Documentation: https://schedule.readthedocs.io
.. code-block:: python
import time
import schedule
from pyrogram import Client
app = Client("my_account")
def job():
app.send_message("me", "Hi!")
schedule.every(3).seconds.do(job)
with app:
while True:
schedule.run_pending()
time.sleep(1)
Using ``apscheduler``
---------------------
- Install with ``pip3 install apscheduler``
- Documentation: https://apscheduler.readthedocs.io
.. code-block:: python
from apscheduler.schedulers.background import BackgroundScheduler
from pyrogram import Client
app = Client("my_account")
def job():
app.send_message("me", "Hi!")
scheduler = BackgroundScheduler()
scheduler.add_job(job, "interval", seconds=3)
scheduler.start()
app.run()
``apscheduler`` does also support async code, here's an example with
`Pyrogram Asyncio <https://docs.pyrogram.org/intro/install.html#asynchronous>`_:
.. code-block:: python
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from pyrogram import Client
app = Client("my_account")
async def job():
await app.send_message("me", "Hi!")
scheduler = AsyncIOScheduler()
scheduler.add_job(job, "interval", seconds=3)
scheduler.start()
app.run()

View File

@ -9,7 +9,7 @@ For Humans - str(obj)
---------------------
If you want a nicely formatted, human readable JSON representation of any object in the API -- namely, any object from
:doc:`Pyrogram types <../api/types>`, :doc:`raw functions <../telegram/functions/index>` and
:doc:`Pyrogram types <../api/types/index>`, :doc:`raw functions <../telegram/functions/index>` and
:doc:`raw types <../telegram/types/index>` -- you can use use ``str(obj)``.
.. code-block:: python

View File

@ -316,9 +316,9 @@ attribute. Here's an example:
Unloading
^^^^^^^^^
In order to unload a plugin, or any other handler, all you need to do is obtain a reference to it by importing the
relevant module and call :meth:`~pyrogram.Client.remove_handler` Client's method with your function
name preceded by the star ``*`` operator as argument. Example:
In order to unload a plugin, all you need to do is obtain a reference to it by importing the relevant module and call
:meth:`~pyrogram.Client.remove_handler` Client's method with your function's *handler* special attribute preceded by the
star ``*`` operator as argument. Example:
- ``main.py``
@ -328,14 +328,14 @@ name preceded by the star ``*`` operator as argument. Example:
...
app.remove_handler(*echo)
app.remove_handler(*echo.handler)
The star ``*`` operator is used to unpack the tuple into positional arguments so that *remove_handler* will receive
exactly what is needed. The same could have been achieved with:
.. code-block:: python
handler, group = echo
handler, group = echo.handler
app.remove_handler(handler, group)
Loading
@ -352,4 +352,4 @@ using :meth:`~pyrogram.Client.add_handler` instead. Example:
...
app.add_handler(*echo)
app.add_handler(*echo.handler)

View File

@ -0,0 +1,99 @@
Storage Engines
===============
Every time you login to Telegram, some personal piece of data are created and held by both parties (the client, Pyrogram
and the server, Telegram). This session data is uniquely bound to your own account, indefinitely (until you logout or
decide to manually terminate it) and is used to authorize a client to execute API calls on behalf of your identity.
Persisting Sessions
-------------------
In order to make a client reconnect successfully between restarts, that is, without having to start a new
authorization process from scratch each time, Pyrogram needs to store the generated session data somewhere.
Other useful data being stored is peers' cache. In short, peers are all those entities you can chat with, such as users
or bots, basic groups, but also channels and supergroups. Because of how Telegram works, a unique pair of **id** and
**access_hash** is needed to contact a peer. This, plus other useful info such as the peer type, is what is stored
inside a session storage.
So, if you ever wondered how is Pyrogram able to contact peers just by asking for their ids, it's because of this very
reason: the peer *id* is looked up in the internal database and the available *access_hash* is retrieved, which is then
used to correctly invoke API methods.
Different Storage Engines
-------------------------
Let's now talk about how Pyrogram actually stores all the relevant data. Pyrogram offers two different types of storage
engines: a **File Storage** and a **Memory Storage**. These engines are well integrated in the library and require a
minimal effort to set up. Here's how they work:
File Storage
^^^^^^^^^^^^
This is the most common storage engine. It is implemented by using **SQLite**, which will store the session and peers
details. The database will be saved to disk as a single portable file and is designed to efficiently store and retrieve
peers whenever they are needed.
To use this type of engine, simply pass any name of your choice to the ``session_name`` parameter of the
:obj:`~pyrogram.Client` constructor, as usual:
.. code-block:: python
from pyrogram import Client
with Client("my_account") as app:
print(app.get_me())
Once you successfully log in (either with a user or a bot identity), a session file will be created and saved to disk as
``my_account.session``. Any subsequent client restart will make Pyrogram search for a file named that way and the
session database will be automatically loaded.
Memory Storage
^^^^^^^^^^^^^^
In case you don't want to have any session file saved to disk, you can use an in-memory storage by passing the special
session name "**:memory:**" to the ``session_name`` parameter of the :obj:`~pyrogram.Client` constructor:
.. code-block:: python
from pyrogram import Client
with Client(":memory:") as app:
print(app.get_me())
This storage engine is still backed by SQLite, but the database exists purely in memory. This means that, once you stop a
client, the entire database is discarded and the session details used for logging in again will be lost forever.
Session Strings
---------------
In case you want to use an in-memory storage, but also want to keep access to the session you created, call
:meth:`~pyrogram.Client.export_session_string` anytime before stopping the client...
.. code-block:: python
from pyrogram import Client
with Client(":memory:") as app:
print(app.export_session_string())
...and save the resulting (quite long) string somewhere. You can use this string as session name the next time you want
to login using the same session; the storage used will still be completely in-memory:
.. code-block:: python
from pyrogram import Client
session_string = "...ZnUIFD8jsjXTb8g_vpxx48k1zkov9sapD-tzjz-S4WZv70M..."
with Client(session_string) as app:
print(app.get_me())
Session strings are useful when you want to run authorized Pyrogram clients on platforms like
`Heroku <https://www.heroku.com/>`_, where their ephemeral filesystems makes it much harder for a file-based storage
engine to properly work as intended.
But, why is the session string so long? Can't it be shorter? No, it can't. The session string already packs the bare
minimum data Pyrogram needs to successfully reconnect to an authorized session, and the 2048-bits auth key is the major
contributor to the overall length. Needless to say that this string, as well as any other session storage, represent
strictly personal data. Keep them safe.

View File

@ -1,40 +1,99 @@
Text Formatting
===============
Pyrogram, just like the `Telegram Bot API`_, natively supports basic Markdown and HTML formatting styles for text
messages and media captions.
.. role:: strike
:class: strike
Markdown style uses the same syntax as Telegram Desktop's and is enabled by default.
.. role:: underline
:class: underline
Beside bold, italic, and pre-formatted code, **Pyrogram does also support inline URLs and inline mentions of users**.
.. role:: bold-underline
:class: bold-underline
.. role:: strike-italic
:class: strike-italic
Pyrogram uses a custom Markdown dialect for text formatting which adds some unique features that make writing styled
texts easier in both Markdown and HTML. You can send sophisticated text messages and media captions using a great
variety of decorations that can also be nested in order to combine multiple styles together.
Basic Styles
------------
When formatting your messages, you can choose between Markdown-style, HTML-style or both (default). The following is a
list of the basic styles currently supported by Pyrogram.
- **bold**
- *italic*
- :strike:`strike`
- :underline:`underline`
- `text URL <https://pyrogram.org>`_
- `user text mention <https://t.me/haskell>`_
- ``inline fixed-width code``
- .. code-block:: text
pre-formatted
fixed-width
code block
.. note::
User text mentions are only guaranteed to work if you have already met the user (in groups or private chats).
Markdown Style
--------------
To use this mode, pass "markdown" in the *parse_mode* field when using
To strictly use this mode, pass "markdown" to the *parse_mode* parameter when using
:meth:`~pyrogram.Client.send_message`. Use the following syntax in your message:
.. code-block:: text
**bold text**
**bold**
__italic text__
__italic__
[inline URL](https://docs.pyrogram.org/)
--underline--
[inline mention of a user](tg://user?id=23122162)
~~strike~~
[text URL](https://docs.pyrogram.org/)
[text user mention](tg://user?id=23122162)
`inline fixed-width code`
```block_language
pre-formatted fixed-width code block
```
pre-formatted
fixed-width
code block
```
**Example**:
.. code-block:: python
app.send_message(
"haskell",
(
"**bold**, "
"__italic__, "
"--underline--, "
"~~strike~~, "
"[mention](tg://user?id=23122162), "
"[URL](https://pyrogram.org), "
"`code`, "
"```"
"for i in range(10):\n"
" print(i)"
"```"
),
parse_mode="markdown"
)
HTML Style
----------
To use this mode, pass "html" in the *parse_mode* field when using :meth:`~pyrogram.Client.send_message`.
To strictly use this mode, pass "html" to the *parse_mode* parameter when using :meth:`~pyrogram.Client.send_message`.
The following tags are currently supported:
.. code-block:: text
@ -43,49 +102,36 @@ The following tags are currently supported:
<i>italic</i>, <em>italic</em>
<a href="http://docs.pyrogram.org/">inline URL</a>
<u>underline</u>
<a href="tg://user?id=23122162">inline mention of a user</a>
<s>strike</s>, <del>strike</del>, <strike>strike</strike>
<a href="http://docs.pyrogram.org/">text URL</a>
<a href="tg://user?id=23122162">inline mention</a>
<code>inline fixed-width code</code>
<pre>pre-formatted fixed-width code block</pre>
<pre>
pre-formatted
fixed-width
code block
</pre>
.. note:: Mentions are only guaranteed to work if you have already met the user (in groups or private chats).
Examples
--------
- Markdown:
**Example**:
.. code-block:: python
app.send_message(
chat_id="haskell",
text=(
"**bold**, "
"__italic__, "
"[mention](tg://user?id=23122162), "
"[URL](https://docs.pyrogram.org), "
"`code`, "
"```"
"for i in range(10):\n"
" print(i)```"
)
)
- HTML:
.. code-block:: python
app.send_message(
chat_id="haskell",
text=(
"haskell",
(
"<b>bold</b>, "
"<i>italic</i>, "
"<u>underline</u>, "
"<s>strike</s>, "
"<a href=\"tg://user?id=23122162\">mention</a>, "
"<a href=\"https://pyrogram.org/\">URL</a>, "
"<code>code</code>, "
"<code>code</code>\n\n"
"<pre>"
"for i in range(10):\n"
" print(i)"
@ -94,4 +140,86 @@ Examples
parse_mode="html"
)
.. _Telegram Bot API: https://core.telegram.org/bots/api#formatting-options
.. note::
All ``<``, ``>`` and ``&`` symbols that are not a part of a tag or an HTML entity must be replaced with the
corresponding HTML entities (``<`` with ``&lt;``, ``>`` with ``&gt;`` and ``&`` with ``&amp;``). You can use this
snippet to quickly escape those characters:
.. code-block:: python
import html
text = "<my text>"
text = html.escape(text)
print(text)
.. code-block:: text
&lt;my text&gt;
Different Styles
----------------
By default, when ignoring the *parse_mode* parameter, both Markdown and HTML styles are enabled together.
This means you can combine together both syntaxes in the same text:
.. code-block:: python
app.send_message("haskell", "**bold**, <i>italic</i>")
Result:
**bold**, *italic*
If you don't like this behaviour you can always choose to only enable either Markdown or HTML in strict mode by passing
"markdown" or "html" as argument to the *parse_mode* parameter.
.. code-block::
app.send_message("haskell", "**bold**, <i>italic</i>", parse_mode="markdown")
app.send_message("haskell", "**bold**, <i>italic</i>", parse_mode="html")
Result:
**bold**, <i>italic</i>
\*\*bold**, *italic*
In case you want to completely turn off the style parser, simply pass ``None`` to *parse_mode*. The text will be sent
as-is.
.. code-block:: python
app.send_message("haskell", "**bold**, <i>italic</i>", parse_mode=None)
Result:
\*\*bold**, <i>italic</i>
Nested and Overlapping Entities
-------------------------------
You can also style texts with more than one decoration at once by nesting entities together. For example, you can send
a text message with both :bold-underline:`bold and underline` styles, or a text that has both :strike-italic:`italic and
strike` styles, and you can still combine both Markdown and HTML together.
Here there are some example texts you can try sending:
**Markdown**:
- ``**bold, --underline--**``
- ``**bold __italic --underline ~~strike~~--__**``
- ``**bold __and** italic__``
**HTML**:
- ``<b>bold, <u>underline</u></b>``
- ``<b>bold <i>italic <u>underline <s>strike</s></u></i></b>``
- ``<b>bold <i>and</b> italic</i>``
**Combined**:
- ``--you can combine <i>HTML</i> with **Markdown**--``
- ``**and also <i>overlap** --entities</i> this way--``

View File

@ -1,8 +1,8 @@
Using Filters
=============
So far we've seen how to register a callback function that executes every time a specific update comes from the server,
but there's much more than that to come.
So far we've seen :doc:`how to register a callback function <../start/updates>` that executes every time a specific update
comes from the server, but there's much more than that to come.
Here we'll discuss about :class:`~pyrogram.Filters`. Filters enable a fine-grain control over what kind of
updates are allowed or not to be passed in your callback functions, based on their inner details.

View File

@ -24,7 +24,7 @@ if sys.version_info[:3] in [(3, 5, 0), (3, 5, 1), (3, 5, 2)]:
# Monkey patch the standard "typing" module because Python versions from 3.5.0 to 3.5.2 have a broken one.
sys.modules["typing"] = typing
__version__ = "0.15.0-develop"
__version__ = "0.16.0.dev"
__license__ = "GNU Lesser General Public License v3 or later (LGPLv3+)"
__copyright__ = "Copyright (C) 2017-2019 Dan <https://github.com/delivrance>"

View File

@ -19,8 +19,7 @@
from importlib import import_module
from .all import objects
from .core.tl_object import TLObject
for k, v in objects.items():
path, name = v.rsplit(".", 1)
TLObject.all[k] = getattr(import_module(path), name)
objects[k] = getattr(import_module(path), name)

View File

@ -22,8 +22,5 @@ from .gzip_packed import GzipPacked
from .list import List
from .message import Message
from .msg_container import MsgContainer
from .primitives import (
Bool, BoolTrue, BoolFalse, Bytes, Double,
Int, Long, Int128, Int256, Null, String, Vector
)
from .primitives import *
from .tl_object import TLObject

View File

@ -16,10 +16,11 @@
# 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 .bool import Bool, BoolTrue, BoolFalse
from .bool import Bool, BoolFalse, BoolTrue
from .bytes import Bytes
from .double import Double
from .int import Int, Long, Int128, Int256
from .null import Null
from .string import String
from .vector import Vector
__all__ = ["Bool", "BoolFalse", "BoolTrue", "Bytes", "Double", "Int", "Long", "Int128", "Int256", "String", "Vector"]

View File

@ -20,17 +20,17 @@ from collections import OrderedDict
from io import BytesIO
from json import dumps
from ..all import objects
class TLObject:
all = {}
__slots__ = []
QUALNAME = "Base"
@staticmethod
def read(b: BytesIO, *args): # TODO: Rename b -> data
return TLObject.all[int.from_bytes(b.read(4), "little")].read(b, *args)
return objects[int.from_bytes(b.read(4), "little")].read(b, *args)
def write(self, *args) -> bytes:
pass

File diff suppressed because it is too large Load Diff

View File

@ -19,11 +19,13 @@
import os
import platform
import re
import sys
from pathlib import Path
from queue import Queue
from threading import Lock
from pyrogram import __version__
from ..style import Markdown, HTML
from ..parser import Parser
from ...session.internals import MsgId
@ -45,6 +47,8 @@ class BaseClient:
LANG_CODE = "en"
PARENT_DIR = Path(sys.argv[0]).parent
INVITE_LINK_RE = re.compile(r"^(?:https?://)?(?:www\.)?(?:t(?:elegram)?\.(?:org|me|dog)/joinchat/)([\w-]+)$")
BOT_TOKEN_RE = re.compile(r"^\d+:[\w-]+$")
DIALOGS_AT_ONCE = 100
@ -52,8 +56,10 @@ class BaseClient:
DOWNLOAD_WORKERS = 1
OFFLINE_SLEEP = 900
WORKERS = 4
WORKDIR = "."
CONFIG_FILE = "./config.ini"
WORKDIR = PARENT_DIR
CONFIG_FILE = PARENT_DIR / "config.ini"
PARSE_MODES = ["combined", "markdown", "md", "html", None]
MEDIA_TYPE_ID = {
0: "photo_thumbnail",
@ -83,28 +89,21 @@ class BaseClient:
mime_types_to_extensions[mime_type] = " ".join(extensions)
is_idling = False
def __init__(self):
self.is_bot = None
self.dc_id = None
self.auth_key = None
self.user_id = None
self.date = None
self.storage = None
self.rnd_id = MsgId
self.peers_by_id = {}
self.peers_by_username = {}
self.peers_by_phone = {}
self.markdown = Markdown(self)
self.html = HTML(self)
self.parser = Parser(self)
self.parse_mode = "combined"
self.session = None
self.media_sessions = {}
self.media_sessions_lock = Lock()
self.is_started = None
self.is_idle = None
self.takeout_id = None
@ -159,3 +158,18 @@ class BaseClient:
def edit_message_text(self, *args, **kwargs):
pass
def edit_inline_text(self, *args, **kwargs):
pass
def edit_message_media(self, *args, **kwargs):
pass
def edit_inline_media(self, *args, **kwargs):
pass
def edit_message_reply_markup(self, *args, **kwargs):
pass
def edit_inline_reply_markup(self, *args, **kwargs):
pass

View File

@ -20,7 +20,7 @@ import logging
import threading
from collections import OrderedDict
from queue import Queue
from threading import Thread
from threading import Thread, Lock
import pyrogram
from pyrogram.api import types
@ -61,6 +61,8 @@ class Dispatcher:
self.workers = workers
self.workers_list = []
self.locks_list = []
self.updates_queue = Queue()
self.groups = OrderedDict()
@ -75,9 +77,7 @@ class Dispatcher:
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
),
lambda upd, usr, cht: (pyrogram.User._parse_user_status(self.client, upd), UserStatusHandler),
(types.UpdateBotInlineQuery,):
lambda upd, usr, cht: (pyrogram.InlineQuery._parse(self.client, upd, usr), InlineQueryHandler),
@ -90,10 +90,13 @@ class Dispatcher:
def start(self):
for i in range(self.workers):
self.locks_list.append(Lock())
self.workers_list.append(
Thread(
target=self.update_worker,
name="UpdateWorker#{}".format(i + 1)
name="UpdateWorker#{}".format(i + 1),
args=(self.locks_list[-1],)
)
)
@ -107,22 +110,37 @@ class Dispatcher:
worker.join()
self.workers_list.clear()
self.locks_list.clear()
self.groups.clear()
def add_handler(self, handler, group: int):
for lock in self.locks_list:
lock.acquire()
try:
if group not in self.groups:
self.groups[group] = []
self.groups = OrderedDict(sorted(self.groups.items()))
self.groups[group].append(handler)
finally:
for lock in self.locks_list:
lock.release()
def remove_handler(self, handler, group: int):
for lock in self.locks_list:
lock.acquire()
try:
if group not in self.groups:
raise ValueError("Group {} does not exist. Handler was not removed.".format(group))
self.groups[group].remove(handler)
finally:
for lock in self.locks_list:
lock.release()
def update_worker(self):
def update_worker(self, lock):
name = threading.current_thread().name
log.debug("{} started".format(name))
@ -142,13 +160,19 @@ class Dispatcher:
else (None, type(None))
)
with lock:
for group in self.groups.values():
for handler in group:
args = None
if isinstance(handler, handler_type):
try:
if handler.check(parsed_update):
args = (parsed_update,)
except Exception as e:
log.error(e, exc_info=True)
continue
elif isinstance(handler, RawUpdateHandler):
args = (update, users, chats)

View File

@ -16,16 +16,10 @@
# 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 base64
import json
import logging
import os
import shutil
import time
from threading import Thread, Event, Lock
from . import utils
log = logging.getLogger(__name__)
@ -81,48 +75,13 @@ class Syncer:
@classmethod
def sync(cls, client):
temporary = os.path.join(client.workdir, "{}.sync".format(client.session_name))
persistent = os.path.join(client.workdir, "{}.session".format(client.session_name))
try:
auth_key = base64.b64encode(client.auth_key).decode()
auth_key = [auth_key[i: i + 43] for i in range(0, len(auth_key), 43)]
data = dict(
dc_id=client.dc_id,
test_mode=client.test_mode,
auth_key=auth_key,
user_id=client.user_id,
date=int(time.time()),
is_bot=bool(client.is_bot),
peers_by_id={
k: getattr(v, "access_hash", None)
for k, v in client.peers_by_id.copy().items()
},
peers_by_username={
k: utils.get_peer_id(v)
for k, v in client.peers_by_username.copy().items()
},
peers_by_phone={
k: utils.get_peer_id(v)
for k, v in client.peers_by_phone.copy().items()
}
)
os.makedirs(client.workdir, exist_ok=True)
with open(temporary, "w", encoding="utf-8") as f:
json.dump(data, f, indent=4)
f.flush()
os.fsync(f.fileno())
start = time.time()
client.storage.save()
except Exception as e:
log.critical(e, exc_info=True)
else:
shutil.move(temporary, persistent)
log.info("Synced {}".format(client.session_name))
finally:
try:
os.remove(temporary)
except OSError:
pass
log.info('Synced "{}" in {:.6} ms'.format(
client.storage.name,
(time.time() - start) * 1000
))

View File

@ -18,22 +18,30 @@
import base64
import struct
from base64 import b64decode, b64encode
from typing import Union, List
from typing import List
from typing import Union
import pyrogram
from pyrogram.api.types import PeerUser, PeerChat, PeerChannel
from . import BaseClient
from ...api import types
def decode(s: str) -> bytes:
s = b64decode(s + "=" * (-len(s) % 4), "-_")
s = base64.urlsafe_b64decode(s + "=" * (-len(s) % 4))
r = b""
try:
assert s[-1] == 2
skip = 1
except AssertionError:
assert s[-2] == 22
assert s[-1] == 4
skip = 2
i = 0
while i < len(s) - 1:
while i < len(s) - skip:
if s[i] != 0:
r += bytes([s[i]])
else:
@ -49,7 +57,7 @@ def encode(s: bytes) -> str:
r = b""
n = 0
for i in s + bytes([2]):
for i in s + bytes([22]) + bytes([4]):
if i == 0:
n += 1
else:
@ -59,24 +67,7 @@ def encode(s: bytes) -> str:
r += bytes([i])
return b64encode(r, b"-_").decode().rstrip("=")
def get_peer_id(input_peer) -> int:
return (
input_peer.user_id if isinstance(input_peer, types.InputPeerUser)
else -input_peer.chat_id if isinstance(input_peer, types.InputPeerChat)
else int("-100" + str(input_peer.channel_id))
)
def get_input_peer(peer_id: int, access_hash: int):
return (
types.InputPeerUser(user_id=peer_id, access_hash=access_hash) if peer_id > 0
else types.InputPeerChannel(channel_id=int(str(peer_id)[4:]), access_hash=access_hash)
if (str(peer_id).startswith("-100") and access_hash)
else types.InputPeerChat(chat_id=-peer_id)
)
return base64.urlsafe_b64encode(r).decode().rstrip("=")
def get_offset_date(dialogs):
@ -183,7 +174,7 @@ def parse_deleted_messages(client, update) -> List["pyrogram.Message"]:
pyrogram.Message(
message_id=message,
chat=pyrogram.Chat(
id=int("-100" + str(channel_id)),
id=get_channel_id(channel_id),
type="channel",
client=client
) if channel_id is not None else None,
@ -203,3 +194,39 @@ def unpack_inline_message_id(inline_message_id: str) -> types.InputBotInlineMess
id=r[1],
access_hash=r[2]
)
MIN_CHANNEL_ID = -1002147483647
MAX_CHANNEL_ID = -1000000000000
MIN_CHAT_ID = -2147483647
MAX_USER_ID = 2147483647
def get_peer_id(peer: Union[PeerUser, PeerChat, PeerChannel]) -> int:
if isinstance(peer, PeerUser):
return peer.user_id
if isinstance(peer, PeerChat):
return -peer.chat_id
if isinstance(peer, PeerChannel):
return MAX_CHANNEL_ID - peer.channel_id
raise ValueError("Peer type invalid: {}".format(peer))
def get_type(peer_id: int) -> str:
if peer_id < 0:
if MIN_CHAT_ID <= peer_id:
return "chat"
if MIN_CHANNEL_ID <= peer_id < MAX_CHANNEL_ID:
return "channel"
elif 0 < peer_id <= MAX_USER_ID:
return "user"
raise ValueError("Peer id invalid: {}".format(peer_id))
def get_channel_id(peer_id: int) -> int:
return MAX_CHANNEL_ID - peer_id

View File

@ -17,180 +17,183 @@
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
import re
from typing import Callable
from .filter import Filter
from ..types.bots_and_keyboards import InlineKeyboardMarkup, ReplyKeyboardMarkup
CUSTOM_FILTER_NAME = "CustomFilter"
def create(name: str, func: callable, **kwargs) -> type:
"""Create a Filter.
def create(func: Callable, name: str = None, **kwargs) -> Filter:
"""Easily create a custom filter.
Custom filters give you extra control over which updates are allowed or not to be processed by your handlers.
Parameters:
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` the update type will be
a :obj:`Message`; in a :obj:`CallbackQueryHandler` the
update type will be a :obj:`CallbackQuery`. Your function body can then access the
incoming update and decide whether to allow it or not.
A function that accepts two positional arguments *(filter, update)* and returns a boolean: True if the
update should be handled, False otherwise. The *filter* argument refers to the filter itself and can be used
to access keyword arguments (read below). The *update* argument type will vary depending on which
`Handler <handlers>`_ is coming from. For example, in a :obj:`MessageHandler` the *update* argument will be
a :obj:`Message`; in a :obj:`CallbackQueryHandler` the *update* will be a :obj:`CallbackQuery`. Your
function body can then access the incoming update attributes and decide whether to allow it or not.
name (``str``, *optional*):
Your filter's name. Can be anything you like.
Defaults to "CustomFilter".
**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`).
Any keyword argument you would like to pass. Useful when creating parameterized custom filters, such as
:meth:`~Filters.command` or :meth:`~Filters.regex`.
"""
# TODO: unpack kwargs using **kwargs into the dict itself. For Python 3.5+ only
d = {"__call__": func}
d.update(kwargs)
return type(name, (Filter,), d)()
return type(name or CUSTOM_FILTER_NAME, (Filter,), d)()
class Filters:
"""This class provides access to all library-defined Filters available in Pyrogram.
The Filters listed here are intended to be used with the :obj:`MessageHandler` only.
The Filters listed here are currently intended to be used with the :obj:`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
me = create("Me", lambda _, m: bool(m.from_user and m.from_user.is_self))
me = create(lambda _, m: bool(m.from_user and m.from_user.is_self), "MeFilter")
"""Filter messages generated by you yourself."""
bot = create("Bot", lambda _, m: bool(m.from_user and m.from_user.is_bot))
bot = create(lambda _, m: bool(m.from_user and m.from_user.is_bot), "BotFilter")
"""Filter messages coming from bots."""
incoming = create("Incoming", lambda _, m: not m.outgoing)
incoming = create(lambda _, m: not m.outgoing, "IncomingFilter")
"""Filter incoming messages. Messages sent to your own chat (Saved Messages) are also recognised as incoming."""
outgoing = create("Outgoing", lambda _, m: m.outgoing)
outgoing = create(lambda _, m: m.outgoing, "OutgoingFilter")
"""Filter outgoing messages. Messages sent to your own chat (Saved Messages) are not recognized as outgoing."""
text = create("Text", lambda _, m: bool(m.text))
text = create(lambda _, m: bool(m.text), "TextFilter")
"""Filter text messages."""
reply = create("Reply", lambda _, m: bool(m.reply_to_message))
reply = create(lambda _, m: bool(m.reply_to_message), "ReplyFilter")
"""Filter messages that are replies to other messages."""
forwarded = create("Forwarded", lambda _, m: bool(m.forward_date))
forwarded = create(lambda _, m: bool(m.forward_date), "ForwardedFilter")
"""Filter messages that are forwarded."""
caption = create("Caption", lambda _, m: bool(m.caption))
caption = create(lambda _, m: bool(m.caption), "CaptionFilter")
"""Filter media messages that contain captions."""
edited = create("Edited", lambda _, m: bool(m.edit_date))
edited = create(lambda _, m: bool(m.edit_date), "EditedFilter")
"""Filter edited messages."""
audio = create("Audio", lambda _, m: bool(m.audio))
audio = create(lambda _, m: bool(m.audio), "AudioFilter")
"""Filter messages that contain :obj:`Audio` objects."""
document = create("Document", lambda _, m: bool(m.document))
document = create(lambda _, m: bool(m.document), "DocumentFilter")
"""Filter messages that contain :obj:`Document` objects."""
photo = create("Photo", lambda _, m: bool(m.photo))
photo = create(lambda _, m: bool(m.photo), "PhotoFilter")
"""Filter messages that contain :obj:`Photo` objects."""
sticker = create("Sticker", lambda _, m: bool(m.sticker))
sticker = create(lambda _, m: bool(m.sticker), "StickerFilter")
"""Filter messages that contain :obj:`Sticker` objects."""
animation = create("Animation", lambda _, m: bool(m.animation))
animation = create(lambda _, m: bool(m.animation), "AnimationFilter")
"""Filter messages that contain :obj:`Animation` objects."""
game = create("Game", lambda _, m: bool(m.game))
game = create(lambda _, m: bool(m.game), "GameFilter")
"""Filter messages that contain :obj:`Game` objects."""
video = create("Video", lambda _, m: bool(m.video))
video = create(lambda _, m: bool(m.video), "VideoFilter")
"""Filter messages that contain :obj:`Video` objects."""
media_group = create("MediaGroup", lambda _, m: bool(m.media_group_id))
media_group = create(lambda _, m: bool(m.media_group_id), "MediaGroupFilter")
"""Filter messages containing photos or videos being part of an album."""
voice = create("Voice", lambda _, m: bool(m.voice))
voice = create(lambda _, m: bool(m.voice), "VoiceFilter")
"""Filter messages that contain :obj:`Voice` note objects."""
video_note = create("VideoNote", lambda _, m: bool(m.video_note))
video_note = create(lambda _, m: bool(m.video_note), "VideoNoteFilter")
"""Filter messages that contain :obj:`VideoNote` objects."""
contact = create("Contact", lambda _, m: bool(m.contact))
contact = create(lambda _, m: bool(m.contact), "ContactFilter")
"""Filter messages that contain :obj:`Contact` objects."""
location = create("Location", lambda _, m: bool(m.location))
location = create(lambda _, m: bool(m.location), "LocationFilter")
"""Filter messages that contain :obj:`Location` objects."""
venue = create("Venue", lambda _, m: bool(m.venue))
venue = create(lambda _, m: bool(m.venue), "VenueFilter")
"""Filter messages that contain :obj:`Venue` objects."""
web_page = create("WebPage", lambda _, m: m.web_page)
web_page = create(lambda _, m: m.web_page, "WebPageFilter")
"""Filter messages sent with a webpage preview."""
poll = create("Poll", lambda _, m: m.poll)
poll = create(lambda _, m: m.poll, "PollFilter")
"""Filter messages that contain :obj:`Poll` objects."""
private = create("Private", lambda _, m: bool(m.chat and m.chat.type == "private"))
private = create(lambda _, m: bool(m.chat and m.chat.type in {"private", "bot"}), "PrivateFilter")
"""Filter messages sent in private chats."""
group = create("Group", lambda _, m: bool(m.chat and m.chat.type in {"group", "supergroup"}))
group = create(lambda _, m: bool(m.chat and m.chat.type in {"group", "supergroup"}), "GroupFilter")
"""Filter messages sent in group or supergroup chats."""
channel = create("Channel", lambda _, m: bool(m.chat and m.chat.type == "channel"))
channel = create(lambda _, m: bool(m.chat and m.chat.type == "channel"), "ChannelFilter")
"""Filter messages sent in channels."""
new_chat_members = create("NewChatMembers", lambda _, m: bool(m.new_chat_members))
new_chat_members = create(lambda _, m: bool(m.new_chat_members), "NewChatMembersFilter")
"""Filter service messages for new chat members."""
left_chat_member = create("LeftChatMember", lambda _, m: bool(m.left_chat_member))
left_chat_member = create(lambda _, m: bool(m.left_chat_member), "LeftChatMemberFilter")
"""Filter service messages for members that left the chat."""
new_chat_title = create("NewChatTitle", lambda _, m: bool(m.new_chat_title))
new_chat_title = create(lambda _, m: bool(m.new_chat_title), "NewChatTitleFilter")
"""Filter service messages for new chat titles."""
new_chat_photo = create("NewChatPhoto", lambda _, m: bool(m.new_chat_photo))
new_chat_photo = create(lambda _, m: bool(m.new_chat_photo), "NewChatPhotoFilter")
"""Filter service messages for new chat photos."""
delete_chat_photo = create("DeleteChatPhoto", lambda _, m: bool(m.delete_chat_photo))
delete_chat_photo = create(lambda _, m: bool(m.delete_chat_photo), "DeleteChatPhotoFilter")
"""Filter service messages for deleted photos."""
group_chat_created = create("GroupChatCreated", lambda _, m: bool(m.group_chat_created))
group_chat_created = create(lambda _, m: bool(m.group_chat_created), "GroupChatCreatedFilter")
"""Filter service messages for group chat creations."""
supergroup_chat_created = create("SupergroupChatCreated", lambda _, m: bool(m.supergroup_chat_created))
supergroup_chat_created = create(lambda _, m: bool(m.supergroup_chat_created), "SupergroupChatCreatedFilter")
"""Filter service messages for supergroup chat creations."""
channel_chat_created = create("ChannelChatCreated", lambda _, m: bool(m.channel_chat_created))
channel_chat_created = create(lambda _, m: bool(m.channel_chat_created), "ChannelChatCreatedFilter")
"""Filter service messages for channel chat creations."""
migrate_to_chat_id = create("MigrateToChatId", lambda _, m: bool(m.migrate_to_chat_id))
migrate_to_chat_id = create(lambda _, m: bool(m.migrate_to_chat_id), "MigrateToChatIdFilter")
"""Filter service messages that contain migrate_to_chat_id."""
migrate_from_chat_id = create("MigrateFromChatId", lambda _, m: bool(m.migrate_from_chat_id))
migrate_from_chat_id = create(lambda _, m: bool(m.migrate_from_chat_id), "MigrateFromChatIdFilter")
"""Filter service messages that contain migrate_from_chat_id."""
pinned_message = create("PinnedMessage", lambda _, m: bool(m.pinned_message))
pinned_message = create(lambda _, m: bool(m.pinned_message), "PinnedMessageFilter")
"""Filter service messages for pinned messages."""
game_high_score = create("GameHighScore", lambda _, m: bool(m.game_high_score))
game_high_score = create(lambda _, m: bool(m.game_high_score), "GameHighScoreFilter")
"""Filter service messages for game high scores."""
reply_keyboard = create("ReplyKeyboard", lambda _, m: isinstance(m.reply_markup, ReplyKeyboardMarkup))
reply_keyboard = create(lambda _, m: isinstance(m.reply_markup, ReplyKeyboardMarkup), "ReplyKeyboardFilter")
"""Filter messages containing reply keyboard markups"""
inline_keyboard = create("InlineKeyboard", lambda _, m: isinstance(m.reply_markup, InlineKeyboardMarkup))
inline_keyboard = create(lambda _, m: isinstance(m.reply_markup, InlineKeyboardMarkup), "InlineKeyboardFilter")
"""Filter messages containing inline keyboard markups"""
mentioned = create("Mentioned", lambda _, m: bool(m.mentioned))
mentioned = create(lambda _, m: bool(m.mentioned), "MentionedFilter")
"""Filter messages containing mentions"""
via_bot = create("ViaBot", lambda _, m: bool(m.via_bot))
via_bot = create(lambda _, m: bool(m.via_bot), "ViaBotFilter")
"""Filter messages sent via inline bots"""
service = create("Service", lambda _, m: bool(m.service))
service = create(lambda _, m: bool(m.service), "ServiceFilter")
"""Filter service messages.
A service message contains any of the following fields set: *left_chat_member*,
@ -198,7 +201,7 @@ class Filters:
*channel_chat_created*, *migrate_to_chat_id*, *migrate_from_chat_id*, *pinned_message*, *game_score*.
"""
media = create("Media", lambda _, m: bool(m.media))
media = create(lambda _, m: bool(m.media), "MediaFilter")
"""Filter media messages.
A media message contains any of the following fields set: *audio*, *document*, *photo*, *sticker*, *video*,
@ -237,6 +240,7 @@ class Filters:
def func(flt, message):
text = message.text or message.caption
message.command = None
if text:
for p in flt.p:
@ -253,27 +257,31 @@ class Filters:
commands = {c if case_sensitive else c.lower() for c in commands}
prefixes = set(prefix) if prefix else {""}
return create("Command", func=func, c=commands, p=prefixes, s=separator, cs=case_sensitive)
return create(func, "CommandFilter", c=commands, p=prefixes, s=separator, cs=case_sensitive)
@staticmethod
def regex(pattern, flags: int = 0):
"""Filter messages that match a given RegEx pattern.
"""Filter message texts or captions that match a given regular expression pattern.
Parameters:
pattern (``str``):
The RegEx pattern as string, it will be applied to the text of a message. When a pattern matches,
all the `Match Objects <https://docs.python.org/3/library/re.html#match-objects>`_
are stored in the *matches* field of the :obj:`Message` itself.
The RegEx pattern as string, it will be applied to the text or the caption of a message. When a pattern
matches, all the `Match Objects <https://docs.python.org/3/library/re.html#match-objects>`_ are stored
in the *matches* field of the :obj:`Message` itself.
flags (``int``, *optional*):
RegEx flags.
"""
def f(_, m):
m.matches = [i for i in _.p.finditer(m.text or m.caption or "")]
return bool(m.matches)
def func(flt, message):
text = message.text or message.caption
return create("Regex", f, p=re.compile(pattern, flags))
if text:
message.matches = list(flt.p.finditer(text)) or None
return bool(message.matches)
return create(func, "RegexFilter", p=re.compile(pattern, flags))
# noinspection PyPep8Naming
class user(Filter, set):
@ -348,6 +356,6 @@ class Filters:
Pass the data you want to filter for.
"""
return create("CallbackData", lambda flt, cb: cb.data == flt.data, data=data)
return create(lambda flt, cb: cb.data == flt.data, "CallbackDataFilter", data=data)
dan = create("Dan", lambda _, m: bool(m.from_user and m.from_user.id == 23122162))
dan = create(lambda _, m: bool(m.from_user and m.from_user.id == 23122162), "DanFilter")

View File

@ -21,26 +21,24 @@ 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:`~Client.add_handler`
It is intended to be used with :meth:`~Client.add_handler`.
For a nicer way to register this handler, have a look at the
:meth:`~Client.on_user_status` decorator.
For a nicer way to register this handler, have a look at the :meth:`~Client.on_user_status` decorator.
Parameters:
callback (``callable``):
Pass a function that will be called when a new UserStatus update arrives. It takes *(client, user_status)*
Pass a function that will be called when a new user status update arrives. It takes *(client, user)*
as positional arguments (look at the section below for a detailed description).
filters (:obj:`Filters`):
Pass one or more filters to allow only a subset of messages to be passed
in your callback function.
Pass one or more filters to allow only a subset of users to be passed in your callback function.
Other parameters:
client (:obj:`Client`):
The Client itself, useful when you want to call other API methods inside the user status handler.
user_status (:obj:`UserStatus`):
The received UserStatus update.
user (:obj:`User`):
The user containing the updated status.
"""
def __init__(self, callback: callable, filters=None):

View File

@ -56,8 +56,14 @@ class AnswerCallbackQuery(BaseClient):
Returns:
``bool``: True, on success.
Raises:
RPCError: In case of a Telegram RPC error.
Example:
.. code-block:: python
# Answer without alert
app.answer_callback_query(query_id, text=text)
# Answer with alert
app.answer_callback_query(query_id, text=text, show_alert=True)
"""
return self.send(
functions.messages.SetBotCallbackAnswer(

View File

@ -29,28 +29,34 @@ class AnswerInlineQuery(BaseClient):
inline_query_id: str,
results: List[InlineQueryResult],
cache_time: int = 300,
is_personal: bool = None,
is_gallery: bool = False,
is_personal: bool = False,
next_offset: str = "",
switch_pm_text: str = "",
switch_pm_parameter: str = ""
):
"""Send answers to an inline query.
No more than 50 results per query are allowed.
A maximum of 50 results per query is allowed.
Parameters:
inline_query_id (``str``):
Unique identifier for the answered query.
results (List of :obj:`InlineQueryResult <pyrogram.InlineQueryResult>`):
results (List of :obj:`InlineQueryResult`):
A list of results for the inline query.
cache_time (``int``, *optional*):
The maximum amount of time in seconds that the result of the inline query may be cached on the server.
Defaults to 300.
is_gallery (``bool``, *optional*):
Pass True, if results should be displayed in gallery mode instead of list mode.
Defaults to False.
is_personal (``bool``, *optional*):
Pass True, if results may be cached on the server side only for the user that sent the query.
By default, results may be returned to any user who sends the same query.
By default (False), results may be returned to any user who sends the same query.
next_offset (``str``, *optional*):
Pass the offset that a client should send in the next query with the same text to receive more results.
@ -75,15 +81,24 @@ class AnswerInlineQuery(BaseClient):
Returns:
``bool``: True, on success.
Raises:
RPCError: In case of a Telegram RPC error.
Example:
.. code-block:: python
from pyrogram import InlineQueryResultArticle, InputTextMessageContent
app.answer_inline_query(
inline_query_id,
results=[
InlineQueryResultArticle(
"Title",
InputTextMessageContent("Message content"))])
"""
return self.send(
functions.messages.SetInlineBotResults(
query_id=int(inline_query_id),
results=[r.write() for r in results],
cache_time=cache_time,
gallery=None,
gallery=is_gallery or None,
private=is_personal or None,
next_offset=next_offset or None,
switch_pm=types.InlineBotSwitchPM(

View File

@ -51,8 +51,11 @@ class GetGameHighScores(BaseClient):
Returns:
List of :obj:`GameHighScore`: On success.
Raises:
RPCError: In case of a Telegram RPC error.
Example:
.. code-block:: python
scores = app.get_game_high_scores(user_id, chat_id, message_id)
print(scores)
"""
# TODO: inline_message_id

View File

@ -27,7 +27,7 @@ class GetInlineBotResults(BaseClient):
def get_inline_bot_results(
self,
bot: Union[int, str],
query: str,
query: str = "",
offset: str = "",
latitude: float = None,
longitude: float = None
@ -40,8 +40,9 @@ class GetInlineBotResults(BaseClient):
Unique identifier of the inline bot you want to get results from. You can specify
a @username (str) or a bot ID (int).
query (``str``):
query (``str``, *optional*):
Text of the query (up to 512 characters).
Defaults to "" (empty string).
offset (``str``, *optional*):
Offset of the results to be returned.
@ -58,8 +59,13 @@ class GetInlineBotResults(BaseClient):
:obj:`BotResults <pyrogram.api.types.messages.BotResults>`: On Success.
Raises:
RPCError: In case of a Telegram RPC error.
TimeoutError: In case the bot fails to answer within 10 seconds.
Example:
.. code-block:: python
results = app.get_inline_bot_results("pyrogrambot")
print(results)
"""
# TODO: Don't return the raw type

View File

@ -53,8 +53,12 @@ class RequestCallbackAnswer(BaseClient):
or as an alert.
Raises:
RPCError: In case of a Telegram RPC error.
TimeoutError: In case the bot fails to answer within 10 seconds.
Example:
.. code-block:: python
app.request_callback_answer(chat_id, message_id, "callback_data")
"""
# Telegram only wants bytes, but we are allowed to pass strings too.

View File

@ -62,8 +62,10 @@ class SendGame(BaseClient):
Returns:
:obj:`Message`: On success, the sent game message is returned.
Raises:
RPCError: In case of a Telegram RPC error.
Example:
.. code-block:: python
app.send_game(chat_id, "gamename")
"""
r = self.send(
functions.messages.SendMedia(

View File

@ -60,8 +60,10 @@ class SendInlineBotResult(BaseClient):
Returns:
:obj:`Message`: On success, the sent inline result message is returned.
Raises:
RPCError: In case of a Telegram RPC error.
Example:
.. code-block:: python
app.send_inline_bot_result(chat_id, query_id, result_id)
"""
return self.send(
functions.messages.SendInlineBotResult(

View File

@ -66,8 +66,14 @@ class SetGameScore(BaseClient):
:obj:`Message` | ``bool``: On success, if the message was sent by the bot, the edited message is returned,
True otherwise.
Raises:
RPCError: In case of a Telegram RPC error.
Example:
.. code-block:: python
# Set new score
app.set_game_score(user_id, 1000)
# Force set new score
app.set_game_score(user_id, 25, force=True)
"""
r = self.send(
functions.messages.SetGameScore(

View File

@ -16,8 +16,14 @@
# 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 .add_chat_members import AddChatMembers
from .archive_chats import ArchiveChats
from .create_channel import CreateChannel
from .create_group import CreateGroup
from .create_supergroup import CreateSupergroup
from .delete_channel import DeleteChannel
from .delete_chat_photo import DeleteChatPhoto
from .delete_supergroup import DeleteSupergroup
from .export_chat_invite_link import ExportChatInviteLink
from .get_chat import GetChat
from .get_chat_member import GetChatMember
@ -32,9 +38,9 @@ from .kick_chat_member import KickChatMember
from .leave_chat import LeaveChat
from .pin_chat_message import PinChatMessage
from .promote_chat_member import PromoteChatMember
from .restrict_chat import RestrictChat
from .restrict_chat_member import RestrictChatMember
from .set_chat_description import SetChatDescription
from .set_chat_permissions import SetChatPermissions
from .set_chat_photo import SetChatPhoto
from .set_chat_title import SetChatTitle
from .unarchive_chats import UnarchiveChats
@ -65,9 +71,15 @@ class Chats(
IterDialogs,
IterChatMembers,
UpdateChatUsername,
RestrictChat,
SetChatPermissions,
GetDialogsCount,
ArchiveChats,
UnarchiveChats
UnarchiveChats,
CreateGroup,
CreateSupergroup,
CreateChannel,
AddChatMembers,
DeleteChannel,
DeleteSupergroup
):
pass

View File

@ -0,0 +1,88 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2019 Dan Tès <https://github.com/delivrance>
#
# This file is part of Pyrogram.
#
# Pyrogram is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Pyrogram is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
from typing import Union, List
from pyrogram.api import functions, types
from ...ext import BaseClient
class AddChatMembers(BaseClient):
def add_chat_members(
self,
chat_id: Union[int, str],
user_ids: Union[Union[int, str], List[Union[int, str]]],
forward_limit: int = 100
) -> bool:
"""Add new chat members to a group, supergroup or channel
Parameters:
chat_id (``int`` | ``str``):
The group, supergroup or channel id
user_ids (``int`` | ``str`` | List of ``int`` or ``str``):
Users to add in the chat
You can pass an ID (int), username (str) or phone number (str).
Multiple users can be added by passing a list of IDs, usernames or phone numbers.
forward_limit (``int``, *optional*):
How many of the latest messages you want to forward to the new members. Pass 0 to forward none of them.
Only applicable to basic groups (the argument is ignored for supergroups or channels).
Defaults to 100 (max amount).
Returns:
``bool``: On success, True is returned.
Example:
.. code-block:: python
# Add one member to a group or channel
app.add_chat_members(chat_id, user_id)
# Add multiple members to a group or channel
app.add_chat_members(chat_id, [user_id1, user_id2, user_id3])
# Change forward_limit (for basic groups only)
app.add_chat_members(chat_id, user_id, forward_limit=25)
"""
peer = self.resolve_peer(chat_id)
if not isinstance(user_ids, list):
user_ids = [user_ids]
if isinstance(peer, types.InputPeerChat):
for user_id in user_ids:
self.send(
functions.messages.AddChatUser(
chat_id=peer.chat_id,
user_id=self.resolve_peer(user_id),
fwd_limit=forward_limit
)
)
else:
self.send(
functions.channels.InviteToChannel(
channel=peer,
users=[
self.resolve_peer(user_id)
for user_id in user_ids
]
)
)
return True

View File

@ -37,8 +37,14 @@ class ArchiveChats(BaseClient):
Returns:
``bool``: On success, True is returned.
Raises:
RPCError: In case of a Telegram RPC error.
Example:
.. code-block:: python
# Archive chat
app.archive_chats(chat_id)
# Archive multiple chats at once
app.archive_chats([chat_id1, chat_id2, chat_id3])
"""
if not isinstance(chat_ids, list):

View File

@ -0,0 +1,55 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2019 Dan Tès <https://github.com/delivrance>
#
# This file is part of Pyrogram.
#
# Pyrogram is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Pyrogram is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
import pyrogram
from pyrogram.api import functions
from ...ext import BaseClient
class CreateChannel(BaseClient):
def create_channel(
self,
title: str,
description: str = ""
) -> "pyrogram.Chat":
"""Create a new broadcast channel.
Parameters:
title (``title``):
The channel title.
description (``str``, *optional*):
The channel description.
Returns:
:obj:`Chat`: On success, a chat object is returned.
Example:
.. code-block:: python
app.create_channel("Channel Title", "Channel Description")
"""
r = self.send(
functions.channels.CreateChannel(
title=title,
about=description,
broadcast=True
)
)
return pyrogram.Chat._parse_chat(self, r.chats[0])

View File

@ -0,0 +1,65 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2019 Dan Tès <https://github.com/delivrance>
#
# This file is part of Pyrogram.
#
# Pyrogram is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Pyrogram is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
from typing import Union, List
import pyrogram
from pyrogram.api import functions
from ...ext import BaseClient
class CreateGroup(BaseClient):
def create_group(
self,
title: str,
users: Union[Union[int, str], List[Union[int, str]]]
) -> "pyrogram.Chat":
"""Create a new basic group.
.. note::
If you want to create a new supergroup, use :meth:`~pyrogram.Client.create_supergroup` instead.
Parameters:
title (``title``):
The group title.
users (``int`` | ``str`` | List of ``int`` or ``str``):
Users to create a chat with.
You must pass at least one user using their IDs (int), usernames (str) or phone numbers (str).
Multiple users can be invited by passing a list of IDs, usernames or phone numbers.
Returns:
:obj:`Chat`: On success, a chat object is returned.
Example:
.. code-block:: python
app.create_group("Group Title", user_id)
"""
if not isinstance(users, list):
users = [users]
r = self.send(
functions.messages.CreateChat(
title=title,
users=[self.resolve_peer(u) for u in users]
)
)
return pyrogram.Chat._parse_chat(self, r.chats[0])

View File

@ -0,0 +1,59 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2019 Dan Tès <https://github.com/delivrance>
#
# This file is part of Pyrogram.
#
# Pyrogram is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Pyrogram is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
import pyrogram
from pyrogram.api import functions
from ...ext import BaseClient
class CreateSupergroup(BaseClient):
def create_supergroup(
self,
title: str,
description: str = ""
) -> "pyrogram.Chat":
"""Create a new supergroup.
.. note::
If you want to create a new basic group, use :meth:`~pyrogram.Client.create_group` instead.
Parameters:
title (``title``):
The supergroup title.
description (``str``, *optional*):
The supergroup description.
Returns:
:obj:`Chat`: On success, a chat object is returned.
Example:
.. code-block:: python
app.create_supergroup("Supergroup Title", "Supergroup Description")
"""
r = self.send(
functions.channels.CreateChannel(
title=title,
about=description,
megagroup=True
)
)
return pyrogram.Chat._parse_chat(self, r.chats[0])

View File

@ -0,0 +1,48 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2019 Dan Tès <https://github.com/delivrance>
#
# This file is part of Pyrogram.
#
# Pyrogram is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Pyrogram is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
from typing import Union
from pyrogram.api import functions
from ...ext import BaseClient
class DeleteChannel(BaseClient):
def delete_channel(self, chat_id: Union[int, str]) -> bool:
"""Delete a channel.
Parameters:
chat_id (``int`` | ``str``):
The id of the channel to be deleted.
Returns:
``bool``: On success, True is returned.
Example:
.. code-block:: python
app.delete_channel(channel_id)
"""
self.send(
functions.channels.DeleteChannel(
channel=self.resolve_peer(chat_id)
)
)
return True

View File

@ -28,12 +28,8 @@ class DeleteChatPhoto(BaseClient):
chat_id: Union[int, str]
) -> bool:
"""Delete a chat photo.
Photos can't be changed for private chats.
You must be an administrator in the chat for this to work and must have the appropriate admin rights.
Note:
In regular groups (non-supergroups), this method will only work if the "All Members Are Admins"
setting is off.
You must be an administrator in the chat for this to work and must have the appropriate admin rights.
Parameters:
chat_id (``int`` | ``str``):
@ -43,8 +39,12 @@ class DeleteChatPhoto(BaseClient):
``bool``: True on success.
Raises:
RPCError: In case of a Telegram RPC error.
``ValueError`` if a chat_id belongs to user.
ValueError: if a chat_id belongs to user.
Example:
.. code-block:: python
app.delete_chat_photo(chat_id)
"""
peer = self.resolve_peer(chat_id)

View File

@ -0,0 +1,48 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2019 Dan Tès <https://github.com/delivrance>
#
# This file is part of Pyrogram.
#
# Pyrogram is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Pyrogram is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
from typing import Union
from pyrogram.api import functions
from ...ext import BaseClient
class DeleteSupergroup(BaseClient):
def delete_supergroup(self, chat_id: Union[int, str]) -> bool:
"""Delete a supergroup.
Parameters:
chat_id (``int`` | ``str``):
The id of the supergroup to be deleted.
Returns:
``bool``: On success, True is returned.
Example:
.. code-block:: python
app.delete_supergroup(supergroup_id)
"""
self.send(
functions.channels.DeleteChannel(
channel=self.resolve_peer(chat_id)
)
)
return True

View File

@ -47,19 +47,21 @@ class ExportChatInviteLink(BaseClient):
``str``: On success, the exported invite link is returned.
Raises:
RPCError: In case of a Telegram RPC error.
ValueError: In case the chat_id belongs to a user.
Example:
.. code-block:: python
link = app.export_chat_invite_link(chat_id)
print(link)
"""
peer = self.resolve_peer(chat_id)
if isinstance(peer, types.InputPeerChat):
if isinstance(peer, (types.InputPeerChat, types.InputPeerChannel)):
return self.send(
functions.messages.ExportChatInvite(
peer=peer
)
).link
elif isinstance(peer, types.InputPeerChannel):
return self.send(
functions.channels.ExportInvite(
channel=peer
)
).link
else:
raise ValueError('The chat_id "{}" belongs to a user'.format(chat_id))

View File

@ -20,7 +20,7 @@ from typing import Union
import pyrogram
from pyrogram.api import functions, types
from ...ext import BaseClient
from ...ext import BaseClient, utils
class GetChat(BaseClient):
@ -44,8 +44,13 @@ class GetChat(BaseClient):
otherwise, a chat preview object is returned.
Raises:
RPCError: In case of a Telegram RPC error.
ValueError: In case the chat invite link points to a chat you haven't joined yet.
Example:
.. code-block:: python
chat = app.get_chat("pyrogram")
print(chat)
"""
match = self.INVITE_LINK_RE.match(str(chat_id))
@ -65,7 +70,7 @@ class GetChat(BaseClient):
chat_id = -r.chat.id
if isinstance(r.chat, types.Channel):
chat_id = int("-100" + str(r.chat.id))
chat_id = utils.get_channel_id(r.chat.id)
peer = self.resolve_peer(chat_id)

View File

@ -44,8 +44,11 @@ class GetChatMember(BaseClient):
Returns:
:obj:`ChatMember`: On success, a chat member is returned.
Raises:
RPCError: In case of a Telegram RPC error.
Example:
.. code-block:: python
dan = app.get_chat_member("pyrogramchat", "haskell")
print(dan)
"""
chat = self.resolve_peer(chat_id)
user = self.resolve_peer(user_id)

View File

@ -91,8 +91,19 @@ class GetChatMembers(BaseClient):
List of :obj:`ChatMember`: On success, a list of chat members is returned.
Raises:
RPCError: In case of a Telegram RPC error.
ValueError: In case you used an invalid filter or a chat id that belongs to a user.
Example:
.. code-block:: python
# Get first 200 recent members
app.get_chat_members("pyrogramchat")
# Get all administrators
app.get_chat_members("pyrogramchat", filter="administrators")
# Get all bots
app.get_chat_members("pyrogramchat", filter="bots")
"""
peer = self.resolve_peer(chat_id)

View File

@ -37,8 +37,13 @@ class GetChatMembersCount(BaseClient):
``int``: On success, the chat members count is returned.
Raises:
RPCError: In case of a Telegram RPC error.
ValueError: In case a chat id belongs to user.
Example:
.. code-block:: python
count = app.get_chat_members_count("pyrogramchat")
print(count)
"""
peer = self.resolve_peer(chat_id)

View File

@ -23,7 +23,7 @@ from typing import List
import pyrogram
from pyrogram.api import functions, types
from pyrogram.errors import FloodWait
from ...ext import BaseClient
from ...ext import BaseClient, utils
log = logging.getLogger(__name__)
@ -56,8 +56,14 @@ class GetDialogs(BaseClient):
Returns:
List of :obj:`Dialog`: On success, a list of dialogs is returned.
Raises:
RPCError: In case of a Telegram RPC error.
Example:
.. code-block:: python
# Get first 100 dialogs
app.get_dialogs()
# Get pinned dialogs
app.get_dialogs(pinned_only=True)
"""
while True:
@ -94,10 +100,8 @@ class GetDialogs(BaseClient):
chat_id = to_id.user_id
else:
chat_id = message.from_id
elif isinstance(to_id, types.PeerChat):
chat_id = -to_id.chat_id
else:
chat_id = int("-100" + str(to_id.channel_id))
chat_id = utils.get_peer_id(to_id)
messages[chat_id] = pyrogram.Message._parse(self, message, users, chats)

View File

@ -31,8 +31,11 @@ class GetDialogsCount(BaseClient):
Returns:
``int``: On success, the dialogs count is returned.
Raises:
RPCError: In case of a Telegram RPC error.
Example:
.. code-block:: python
count = app.get_dialogs_count()
print(count)
"""
if pinned_only:

View File

@ -77,8 +77,20 @@ class IterChatMembers(BaseClient):
Returns:
``Generator``: A generator yielding :obj:`ChatMember` objects.
Raises:
RPCError: In case of a Telegram RPC error.
Example:
.. code-block:: python
# Iterate though all chat members
for member in app.iter_chat_members("pyrogramchat"):
print(member.user.first_name)
# Iterate though all administrators
for member in app.iter_chat_members("pyrogramchat", filter="administrators"):
print(member.user.first_name)
# Iterate though all bots
for member in app.iter_chat_members("pyrogramchat", filter="bots"):
print(member.user.first_name)
"""
current = 0
yielded = set()

View File

@ -46,8 +46,12 @@ class IterDialogs(BaseClient):
Returns:
``Generator``: A generator yielding :obj:`Dialog` objects.
Raises:
RPCError: In case of a Telegram RPC error.
Example:
.. code-block:: python
# Iterate through all dialogs
for dialog in app.iter_dialogs():
print(dialog.chat.first_name or dialog.chat.title)
"""
current = 0
total = limit or (1 << 31) - 1

View File

@ -36,8 +36,14 @@ class JoinChat(BaseClient):
Returns:
:obj:`Chat`: On success, a chat object is returned.
Raises:
RPCError: In case of a Telegram RPC error.
Example:
.. code-block:: python
# Join chat via username
app.join_chat("pyrogram")
# Join chat via invite link
app.join_chat("https://t.me/joinchat/AAAAAE0QmSW3IUmm3UFR7A")
"""
match = self.INVITE_LINK_RE.match(chat_id)

View File

@ -57,8 +57,16 @@ class KickChatMember(BaseClient):
:obj:`Message` | ``bool``: On success, a service message will be returned (when applicable), otherwise, in
case a message object couldn't be returned, True is returned.
Raises:
RPCError: In case of a Telegram RPC error.
Example:
.. code-block:: python
from time import time
# Ban chat member forever
app.kick_chat_member(chat_id, user_id)
# Kick chat member and automatically unban after 24h
app.kick_chat_member(chat_id, user_id, int(time.time() + 86400))
"""
chat_peer = self.resolve_peer(chat_id)
user_peer = self.resolve_peer(user_id)

View File

@ -37,9 +37,16 @@ class LeaveChat(BaseClient):
delete (``bool``, *optional*):
Deletes the group chat dialog after leaving (for simple group chats, not supergroups).
Defaults to False.
Raises:
RPCError: In case of a Telegram RPC error.
Example:
.. code-block:: python
# Leave chat or channel
app.leave_chat(chat_id)
# Leave basic chat and also delete the dialog
app.leave_chat(chat_id, delete=True)
"""
peer = self.resolve_peer(chat_id)

View File

@ -47,8 +47,14 @@ class PinChatMessage(BaseClient):
Returns:
``bool``: True on success.
Raises:
RPCError: In case of a Telegram RPC error.
Example:
.. code-block:: python
# Pin with notification
app.pin_chat_message(chat_id, message_id)
# Pin without notification
app.pin_chat_message(chat_id, message_id, disable_notification=True)
"""
self.send(
functions.messages.UpdatePinnedMessage(

View File

@ -78,8 +78,11 @@ class PromoteChatMember(BaseClient):
Returns:
``bool``: True on success.
Raises:
RPCError: In case of a Telegram RPC error.
Example:
.. code-block:: python
# Promote chat member to supergroup admin
app.promote_chat_member(chat_id, user_id)
"""
self.send(
functions.channels.EditAdmin(

View File

@ -20,7 +20,7 @@ from typing import Union
from pyrogram.api import functions, types
from ...ext import BaseClient
from ...types.user_and_chats import Chat
from ...types.user_and_chats import Chat, ChatPermissions
class RestrictChatMember(BaseClient):
@ -28,20 +28,13 @@ class RestrictChatMember(BaseClient):
self,
chat_id: Union[int, str],
user_id: Union[int, str],
until_date: int = 0,
can_send_messages: bool = False,
can_send_media_messages: bool = False,
can_send_other_messages: bool = False,
can_add_web_page_previews: bool = False,
can_send_polls: bool = False,
can_change_info: bool = False,
can_invite_users: bool = False,
can_pin_messages: bool = False
permissions: ChatPermissions,
until_date: int = 0
) -> Chat:
"""Restrict a user in a supergroup.
The bot must be an administrator in the supergroup for this to work and must have the appropriate admin rights.
Pass True for all boolean parameters to lift restrictions from a user.
You must be an administrator in the supergroup for this to work and must have the appropriate admin rights.
Pass True for all permissions to lift restrictions from a user.
Parameters:
chat_id (``int`` | ``str``):
@ -51,42 +44,32 @@ class RestrictChatMember(BaseClient):
Unique identifier (int) or username (str) of the target user.
For a contact that exists in your Telegram address book you can use his phone number (str).
permissions (:obj:`ChatPermissions`):
New user permissions.
until_date (``int``, *optional*):
Date when the user will be unbanned, unix time.
If user is banned for more than 366 days or less than 30 seconds from the current time they are
considered to be banned forever. Defaults to 0 (ban forever).
can_send_messages (``bool``, *optional*):
Pass True, if the user can send text messages, contacts, locations and venues.
can_send_media_messages (``bool``, *optional*):
Pass True, if the user can send audios, documents, photos, videos, video notes and voice notes,
implies can_send_messages.
can_send_other_messages (``bool``, *optional*):
Pass True, if the user can send animations, games, stickers and use inline bots,
implies can_send_media_messages.
can_add_web_page_previews (``bool``, *optional*):
Pass True, if the user may add web page previews to their messages, implies can_send_media_messages.
can_send_polls (``bool``, *optional*):
Pass True, if the user can send polls, implies can_send_media_messages.
can_change_info (``bool``, *optional*):
Pass True, if the user can change the chat title, photo and other settings.
can_invite_users (``bool``, *optional*):
Pass True, if the user can invite new users to the chat.
can_pin_messages (``bool``, *optional*):
Pass True, if the user can pin messages.
Returns:
:obj:`Chat`: On success, a chat object is returned.
Raises:
RPCError: In case of a Telegram RPC error.
Example:
.. code-block:: python
from time import time
from pyrogram import ChatPermissions
# Completely restrict chat member (mute) forever
app.restrict_chat_member(chat_id, user_id, ChatPermissions())
# Chat member muted for 24h
app.restrict_chat_member(chat_id, user_id, ChatPermissions(), int(time.time() + 86400))
# Chat member can only send text messages
app.restrict_chat_member(chat_id, user_id, ChatPermissions(can_send_messages=True))
"""
send_messages = True
send_media = True
@ -100,37 +83,35 @@ class RestrictChatMember(BaseClient):
invite_users = True
pin_messages = True
if can_send_messages:
if permissions.can_send_messages:
send_messages = None
if can_send_media_messages:
if permissions.can_send_media_messages:
send_messages = None
send_media = None
if can_send_other_messages:
if permissions.can_send_other_messages:
send_messages = None
send_media = None
send_stickers = None
send_gifs = None
send_games = None
send_inline = None
if can_add_web_page_previews:
if permissions.can_add_web_page_previews:
send_messages = None
send_media = None
embed_links = None
if can_send_polls:
if permissions.can_send_polls:
send_messages = None
send_polls = None
if can_change_info:
if permissions.can_change_info:
change_info = None
if can_invite_users:
if permissions.can_invite_users:
invite_users = None
if can_pin_messages:
if permissions.can_pin_messages:
pin_messages = None
r = self.send(

View File

@ -42,8 +42,12 @@ class SetChatDescription(BaseClient):
``bool``: True on success.
Raises:
RPCError: In case of a Telegram RPC error.
``ValueError`` if a chat_id doesn't belong to a supergroup or a channel.
ValueError: if a chat_id doesn't belong to a supergroup or a channel.
Example:
.. code-block:: python
app.set_chat_description(chat_id, "New Description")
"""
peer = self.resolve_peer(chat_id)

View File

@ -20,60 +20,47 @@ from typing import Union
from pyrogram.api import functions, types
from ...ext import BaseClient
from ...types.user_and_chats import Chat
from ...types.user_and_chats import Chat, ChatPermissions
class RestrictChat(BaseClient):
def restrict_chat(
class SetChatPermissions(BaseClient):
def set_chat_permissions(
self,
chat_id: Union[int, str],
can_send_messages: bool = False,
can_send_media_messages: bool = False,
can_send_other_messages: bool = False,
can_add_web_page_previews: bool = False,
can_send_polls: bool = False,
can_change_info: bool = False,
can_invite_users: bool = False,
can_pin_messages: bool = False
permissions: ChatPermissions,
) -> Chat:
"""Restrict a chat.
Pass True for all boolean parameters to lift restrictions from a chat.
"""Set default chat permissions for all members.
You must be an administrator in the group or a supergroup for this to work and must have the
*can_restrict_members* admin rights.
Parameters:
chat_id (``int`` | ``str``):
Unique identifier (int) or username (str) of the target chat.
can_send_messages (``bool``, *optional*):
Pass True, if the user can send text messages, contacts, locations and venues.
can_send_media_messages (``bool``, *optional*):
Pass True, if the user can send audios, documents, photos, videos, video notes and voice notes,
implies can_send_messages.
can_send_other_messages (``bool``, *optional*):
Pass True, if the user can send animations, games, stickers and use inline bots,
implies can_send_media_messages.
can_add_web_page_previews (``bool``, *optional*):
Pass True, if the user may add web page previews to their messages, implies can_send_media_messages.
can_send_polls (``bool``, *optional*):
Pass True, if the user can send polls, implies can_send_media_messages.
can_change_info (``bool``, *optional*):
Pass True, if the user can change the chat title, photo and other settings.
can_invite_users (``bool``, *optional*):
Pass True, if the user can invite new users to the chat.
can_pin_messages (``bool``, *optional*):
Pass True, if the user can pin messages.
permissions (:obj:`ChatPermissions`):
New default chat permissions.
Returns:
:obj:`Chat`: On success, a chat object is returned.
Raises:
RPCError: In case of a Telegram RPC error.
Example:
.. code-block:: python
from pyrogram import ChatPermissions
# Completely restrict chat
app.set_chat_permissions(chat_id, ChatPermissions())
# Chat members can only send text messages, media, stickers and GIFs
app.set_chat_permissions(
chat_id,
ChatPermissions(
can_send_messages=True,
can_send_media_messages=True,
can_send_other_messages=True
)
)
"""
send_messages = True
send_media = True
@ -87,37 +74,35 @@ class RestrictChat(BaseClient):
invite_users = True
pin_messages = True
if can_send_messages:
if permissions.can_send_messages:
send_messages = None
if can_send_media_messages:
if permissions.can_send_media_messages:
send_messages = None
send_media = None
if can_send_other_messages:
if permissions.can_send_other_messages:
send_messages = None
send_media = None
send_stickers = None
send_gifs = None
send_games = None
send_inline = None
if can_add_web_page_previews:
if permissions.can_add_web_page_previews:
send_messages = None
send_media = None
embed_links = None
if can_send_polls:
if permissions.can_send_polls:
send_messages = None
send_polls = None
if can_change_info:
if permissions.can_change_info:
change_info = None
if can_invite_users:
if permissions.can_invite_users:
invite_users = None
if can_pin_messages:
if permissions.can_pin_messages:
pin_messages = None
r = self.send(

View File

@ -17,12 +17,11 @@
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
import os
from base64 import b64decode
from struct import unpack
from typing import Union
from pyrogram.api import functions, types
from ...ext import BaseClient
from ...ext import BaseClient, utils
class SetChatPhoto(BaseClient):
@ -32,38 +31,43 @@ class SetChatPhoto(BaseClient):
photo: str
) -> bool:
"""Set a new profile photo for the chat.
Photos can't be changed for private chats.
You must be an administrator in the chat for this to work and must have the appropriate admin rights.
Note:
In regular groups (non-supergroups), this method will only work if the "All Members Are Admins"
setting is off.
You must be an administrator in the chat for this to work and must have the appropriate admin rights.
Parameters:
chat_id (``int`` | ``str``):
Unique identifier (int) or username (str) of the target chat.
photo (``str``):
New chat photo. You can pass a :obj:`Photo` id or a file path to upload a new photo.
New chat photo. You can pass a :obj:`Photo` file_id or a file path to upload a new photo from your local
machine.
Returns:
``bool``: True on success.
Raises:
RPCError: In case of a Telegram RPC error.
ValueError: if a chat_id belongs to user.
Example:
.. code-block:: python
# Set chat photo using a local file
app.set_chat_photo(chat_id, "photo.jpg")
# Set chat photo using an exiting Photo file_id
app.set_chat_photo(chat_id, photo.file_id)
"""
peer = self.resolve_peer(chat_id)
if os.path.exists(photo):
photo = types.InputChatUploadedPhoto(file=self.save_file(photo))
else:
s = unpack("<qq", b64decode(photo + "=" * (-len(photo) % 4), "-_"))
unpacked = unpack("<iiqqc", utils.decode(photo))
photo = types.InputChatPhoto(
id=types.InputPhoto(
id=s[0],
access_hash=s[1],
id=unpacked[2],
access_hash=unpacked[3],
file_reference=b""
)
)

View File

@ -47,8 +47,12 @@ class SetChatTitle(BaseClient):
``bool``: True on success.
Raises:
RPCError: In case of a Telegram RPC error.
ValueError: In case a chat id belongs to user.
Example:
.. code-block:: python
app.set_chat_title(chat_id, "New Title")
"""
peer = self.resolve_peer(chat_id)

View File

@ -37,8 +37,14 @@ class UnarchiveChats(BaseClient):
Returns:
``bool``: On success, True is returned.
Raises:
RPCError: In case of a Telegram RPC error.
Example:
.. code-block:: python
# Unarchive chat
app.unarchive_chats(chat_id)
# Unarchive multiple chats at once
app.unarchive_chats([chat_id1, chat_id2, chat_id3])
"""
if not isinstance(chat_ids, list):

View File

@ -43,8 +43,11 @@ class UnbanChatMember(BaseClient):
Returns:
``bool``: True on success.
Raises:
RPCError: In case of a Telegram RPC error.
Example:
.. code-block:: python
# Unban chat member right now
app.unban_chat_member(chat_id, user_id)
"""
self.send(
functions.channels.EditBanned(

View File

@ -38,8 +38,10 @@ class UnpinChatMessage(BaseClient):
Returns:
``bool``: True on success.
Raises:
RPCError: In case of a Telegram RPC error.
Example:
.. code-block:: python
app.unpin_chat_message(chat_id)
"""
self.send(
functions.messages.UpdatePinnedMessage(

View File

@ -42,8 +42,12 @@ class UpdateChatUsername(BaseClient):
``bool``: True on success.
Raises:
RPCError: In case of a Telegram RPC error.
ValueError: In case a chat id belongs to a user or chat.
Example:
.. code-block:: python
app.update_chat_username(chat_id, "new_username")
"""
peer = self.resolve_peer(chat_id)

View File

@ -37,8 +37,15 @@ class AddContacts(BaseClient):
Returns:
:obj:`types.contacts.ImportedContacts`
Raises:
RPCError: In case of a Telegram RPC error.
Example:
.. code-block:: python
from pyrogram import InputPhoneContact
app.add_contacts([
InputPhoneContact("39123456789", "Foo"),
InputPhoneContact("38987654321", "Bar"),
InputPhoneContact("01234567891", "Baz")])
"""
imported_contacts = self.send(
functions.contacts.ImportContacts(

View File

@ -38,8 +38,10 @@ class DeleteContacts(BaseClient):
Returns:
``bool``: True on success.
Raises:
RPCError: In case of a Telegram RPC error.
Example:
.. code-block:: python
app.delete_contacts([user_id1, user_id2, user_id3])
"""
contacts = []

View File

@ -30,14 +30,16 @@ log = logging.getLogger(__name__)
class GetContacts(BaseClient):
def get_contacts(self) -> List["pyrogram.User"]:
# TODO: Create a Users object and return that
"""Get contacts from your Telegram address book.
Returns:
List of :obj:`User`: On success, a list of users is returned.
Raises:
RPCError: In case of a Telegram RPC error.
Example:
.. code-block:: python
contacts = app.get_contacts()
print(contacts)
"""
while True:
try:
@ -46,5 +48,4 @@ class GetContacts(BaseClient):
log.warning("get_contacts flood: waiting {} seconds".format(e.x))
time.sleep(e.x)
else:
log.info("Total contacts: {}".format(len(self.peers_by_phone)))
return pyrogram.List(pyrogram.User._parse(self, user) for user in contacts.users)

View File

@ -27,8 +27,11 @@ class GetContactsCount(BaseClient):
Returns:
``int``: On success, the contacts count is returned.
Raises:
RPCError: In case of a Telegram RPC error.
Example:
.. code-block:: python
count = app.get_contacts_count()
print(count)
"""
return len(self.send(functions.contacts.GetContacts(hash=0)).contacts)

View File

@ -16,11 +16,10 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
from typing import Tuple
from typing import Callable
import pyrogram
from pyrogram.client.filters.filter import Filter
from pyrogram.client.handlers.handler import Handler
from ...ext import BaseClient
@ -44,18 +43,15 @@ class OnCallbackQuery(BaseClient):
The group identifier, defaults to 0.
"""
def decorator(func: callable) -> Tuple[Handler, int]:
if isinstance(func, tuple):
func = func[0].callback
def decorator(func: Callable) -> Callable:
if isinstance(self, pyrogram.Client):
self.add_handler(pyrogram.CallbackQueryHandler(func, filters), group)
elif isinstance(self, Filter) or self is None:
func.handler = (
pyrogram.CallbackQueryHandler(func, self),
group if filters is None else filters
)
handler = pyrogram.CallbackQueryHandler(func, filters)
if isinstance(self, Filter):
return pyrogram.CallbackQueryHandler(func, self), group if filters is None else filters
if self is not None:
self.add_handler(handler, group)
return handler, group
return func
return decorator

View File

@ -16,11 +16,10 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
from typing import Tuple
from typing import Callable
import pyrogram
from pyrogram.client.filters.filter import Filter
from pyrogram.client.handlers.handler import Handler
from ...ext import BaseClient
@ -44,18 +43,15 @@ class OnDeletedMessages(BaseClient):
The group identifier, defaults to 0.
"""
def decorator(func: callable) -> Tuple[Handler, int]:
if isinstance(func, tuple):
func = func[0].callback
def decorator(func: Callable) -> Callable:
if isinstance(self, pyrogram.Client):
self.add_handler(pyrogram.DeletedMessagesHandler(func, filters), group)
elif isinstance(self, Filter) or self is None:
func.handler = (
pyrogram.DeletedMessagesHandler(func, self),
group if filters is None else filters
)
handler = pyrogram.DeletedMessagesHandler(func, filters)
if isinstance(self, Filter):
return pyrogram.DeletedMessagesHandler(func, self), group if filters is None else filters
if self is not None:
self.add_handler(handler, group)
return handler, group
return func
return decorator

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