mirror of
https://github.com/Mayuri-Chan/pyrofork.git
synced 2025-12-30 12:24:52 +00:00
Compare commits
57 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
84b4d2181b | ||
|
|
ab7ab16356 | ||
|
|
6331a5500e | ||
|
|
fbe56d5426 | ||
|
|
1d113f8aa4 | ||
|
|
034a4d6041 | ||
|
|
42a2f04d8e | ||
|
|
f55d75d3bb | ||
|
|
c484e82810 | ||
|
|
30822dabff | ||
|
|
3a88297a9a | ||
|
|
231f1a1620 | ||
|
|
0f60e05fce | ||
|
|
e39fdcca0e | ||
|
|
00fb071215 | ||
|
|
609d8985d4 | ||
|
|
e15e3d7d50 | ||
|
|
b1354d5579 | ||
|
|
2a8a01aeb5 | ||
|
|
bec44280a4 | ||
|
|
41b771f4bf | ||
|
|
d91b933715 | ||
|
|
b2158f702a | ||
|
|
97f0ecc7c9 | ||
|
|
b64d33dca0 | ||
|
|
482798c2fc | ||
|
|
64177b533c | ||
|
|
3c0e86a167 | ||
|
|
09ad827374 | ||
|
|
94c589ed43 | ||
|
|
77cfaa0ff4 | ||
|
|
28307ca58e | ||
|
|
65b045ff2d | ||
|
|
b865512c68 | ||
|
|
b0ac808f2e | ||
|
|
3e219771bd | ||
|
|
aedade2ff2 | ||
|
|
8428de1722 | ||
|
|
85184be318 | ||
|
|
d89b083bf0 | ||
|
|
ede9af3e00 | ||
|
|
abf1b2fece | ||
|
|
b038890745 | ||
|
|
e0b7576418 | ||
|
|
86883a3b91 | ||
|
|
e38ceb08cc | ||
|
|
b76bf48862 | ||
|
|
9f32305c3a | ||
|
|
6edc4256c3 | ||
|
|
a1aa281614 | ||
|
|
00520678f6 | ||
|
|
f22cc34c9a | ||
|
|
9b92492553 | ||
|
|
c9e36cc150 | ||
|
|
eeb1338c48 | ||
|
|
115b9877b6 | ||
|
|
6f8aaa449c |
169 changed files with 5842 additions and 187 deletions
15
.github/workflows/build-docs.yml
vendored
Normal file
15
.github/workflows/build-docs.yml
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
name: Build-docs
|
||||
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Build
|
||||
run: bash build-docs.sh
|
||||
env:
|
||||
DOCS_KEY: ${{ secrets.DOCS_KEY }}
|
||||
40
.github/workflows/python-publish.yml
vendored
Normal file
40
.github/workflows/python-publish.yml
vendored
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
# This workflow will upload a Python Package using Twine when a release is created
|
||||
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries
|
||||
|
||||
# This workflow uses actions that are not certified by GitHub.
|
||||
# They are provided by a third-party and are governed by
|
||||
# separate terms of service, privacy policy, and support
|
||||
# documentation.
|
||||
|
||||
name: Upload Python Package
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: '3.10'
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install build twine
|
||||
- name: Build package
|
||||
run: python -m build
|
||||
- name: Publish package
|
||||
env:
|
||||
TWINE_USERNAME: __token__
|
||||
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
|
||||
run: |
|
||||
twine upload dist/*
|
||||
6
Makefile
6
Makefile
|
|
@ -14,6 +14,10 @@ venv:
|
|||
$(PYTHON) -m pip install -U -r requirements.txt -r dev-requirements.txt
|
||||
@echo "Created venv with $$($(PYTHON) --version)"
|
||||
|
||||
clean-docs:
|
||||
$(RM) docs/build
|
||||
$(RM) docs/source/api/bound-methods docs/source/api/methods docs/source/api/types docs/source/telegram
|
||||
|
||||
clean-build:
|
||||
$(RM) *.egg-info build dist
|
||||
|
||||
|
|
@ -39,4 +43,4 @@ tag:
|
|||
|
||||
dtag:
|
||||
git tag -d $(TAG)
|
||||
git push origin -d $(TAG)
|
||||
git push origin -d $(TAG)
|
||||
|
|
|
|||
30
README.md
30
README.md
|
|
@ -1,28 +1,20 @@
|
|||
<p align="center">
|
||||
<a href="https://github.com/pyrogram/pyrogram">
|
||||
<img src="https://docs.pyrogram.org/_static/pyrogram.png" alt="Pyrogram" width="128">
|
||||
<a href="https://github.com/Mayuri-Chan/pyrofork">
|
||||
<img src="https://docs.pyrogram.org/_static/pyrogram.png" alt="PyroFork" width="128">
|
||||
</a>
|
||||
<br>
|
||||
<b>Telegram MTProto API Framework for Python</b>
|
||||
<br>
|
||||
<a href="https://pyrogram.org">
|
||||
<a href="https://pyrofork.mayuri.my.id">
|
||||
Homepage
|
||||
</a>
|
||||
•
|
||||
<a href="https://docs.pyrogram.org">
|
||||
<a href="https://pyrofork.mayuri.my.id">
|
||||
Documentation
|
||||
</a>
|
||||
•
|
||||
<a href="https://docs.pyrogram.org/releases">
|
||||
Releases
|
||||
</a>
|
||||
•
|
||||
<a href="https://t.me/pyrogram">
|
||||
News
|
||||
</a>
|
||||
</p>
|
||||
|
||||
## Pyrogram
|
||||
## PyroFork
|
||||
|
||||
> Elegant, modern and asynchronous Telegram MTProto API framework in Python for users and bots
|
||||
|
||||
|
|
@ -40,7 +32,7 @@ async def hello(client, message):
|
|||
app.run()
|
||||
```
|
||||
|
||||
**Pyrogram** is a modern, elegant and asynchronous [MTProto API](https://docs.pyrogram.org/topics/mtproto-vs-botapi)
|
||||
**PyroFork** is a modern, elegant and asynchronous [MTProto API](https://docs.pyrogram.org/topics/mtproto-vs-botapi)
|
||||
framework. It enables you to easily interact with the main Telegram API through a user account (custom client) or a bot
|
||||
identity (bot API alternative) using Python.
|
||||
|
||||
|
|
@ -48,10 +40,6 @@ identity (bot API alternative) using Python.
|
|||
|
||||
If you'd like to support Pyrogram, you can consider:
|
||||
|
||||
- [Become a GitHub sponsor](https://github.com/sponsors/delivrance).
|
||||
- [Become a LiberaPay patron](https://liberapay.com/delivrance).
|
||||
- [Become an OpenCollective backer](https://opencollective.com/pyrogram).
|
||||
|
||||
### Key Features
|
||||
|
||||
- **Ready**: Install Pyrogram with pip and start building your applications right away.
|
||||
|
|
@ -65,11 +53,11 @@ If you'd like to support Pyrogram, you can consider:
|
|||
### Installing
|
||||
|
||||
``` bash
|
||||
pip3 install pyrogram
|
||||
pip3 install pyrofork
|
||||
```
|
||||
|
||||
### Resources
|
||||
|
||||
- Check out the docs at https://docs.pyrogram.org to learn more about Pyrogram, get started right
|
||||
- Check out the docs at https://pyrofork.mayuri.my.id to learn more about PyroFork, get started right
|
||||
away and discover more in-depth material for building your client applications.
|
||||
- Join the official channel at https://t.me/pyrogram and stay tuned for news, updates and announcements.
|
||||
|
||||
|
|
|
|||
20
build-docs.sh
Normal file
20
build-docs.sh
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
#!/bin/bash
|
||||
export DOCS_KEY
|
||||
export VENV=$(pwd)/venv
|
||||
|
||||
make clean
|
||||
make clean-docs
|
||||
make venv
|
||||
make api
|
||||
"$VENV"/bin/pip install -r docs/requirements.txt
|
||||
cd compiler/docs && "$VENV"/bin/python compiler.py
|
||||
cd ../..
|
||||
"$VENV"/bin/sphinx-build -b html "docs/source" "docs/build/html" -j auto
|
||||
git clone https://wulan17:"$DOCS_KEY"@github.com/Mayuri-Chan/pyrofork-docs.git
|
||||
cp -r docs/build/html/* pyrofork-docs
|
||||
cd pyrofork-docs
|
||||
git config --local user.name "Mayuri-Chan"
|
||||
git config --local user.email "mayuri@mayuri.my.id"
|
||||
git add --all
|
||||
git commit -a -m "docs: Update docs $(date '+%Y-%m-%d | %H:%m:%S %p %Z')" --signoff
|
||||
git push -u origin --all
|
||||
|
|
@ -36,15 +36,15 @@ inputFile#f52ff27f id:long parts:int name:string md5_checksum:string = InputFile
|
|||
inputFileBig#fa4f0bb5 id:long parts:int name:string = InputFile;
|
||||
|
||||
inputMediaEmpty#9664f57f = InputMedia;
|
||||
inputMediaUploadedPhoto#1e287d04 flags:# file:InputFile stickers:flags.0?Vector<InputDocument> ttl_seconds:flags.1?int = InputMedia;
|
||||
inputMediaPhoto#b3ba0635 flags:# id:InputPhoto ttl_seconds:flags.0?int = InputMedia;
|
||||
inputMediaUploadedPhoto#1e287d04 flags:# spoiler:flags.2?true file:InputFile stickers:flags.0?Vector<InputDocument> ttl_seconds:flags.1?int = InputMedia;
|
||||
inputMediaPhoto#b3ba0635 flags:# spoiler:flags.1?true id:InputPhoto ttl_seconds:flags.0?int = InputMedia;
|
||||
inputMediaGeoPoint#f9c44144 geo_point:InputGeoPoint = InputMedia;
|
||||
inputMediaContact#f8ab7dfb phone_number:string first_name:string last_name:string vcard:string = InputMedia;
|
||||
inputMediaUploadedDocument#5b38c6c1 flags:# nosound_video:flags.3?true force_file:flags.4?true file:InputFile thumb:flags.2?InputFile mime_type:string attributes:Vector<DocumentAttribute> stickers:flags.0?Vector<InputDocument> ttl_seconds:flags.1?int = InputMedia;
|
||||
inputMediaDocument#33473058 flags:# id:InputDocument ttl_seconds:flags.0?int query:flags.1?string = InputMedia;
|
||||
inputMediaUploadedDocument#5b38c6c1 flags:# nosound_video:flags.3?true force_file:flags.4?true spoiler:flags.5?true file:InputFile thumb:flags.2?InputFile mime_type:string attributes:Vector<DocumentAttribute> stickers:flags.0?Vector<InputDocument> ttl_seconds:flags.1?int = InputMedia;
|
||||
inputMediaDocument#33473058 flags:# spoiler:flags.2?true id:InputDocument ttl_seconds:flags.0?int query:flags.1?string = InputMedia;
|
||||
inputMediaVenue#c13d1c11 geo_point:InputGeoPoint title:string address:string provider:string venue_id:string venue_type:string = InputMedia;
|
||||
inputMediaPhotoExternal#e5bbfe1a flags:# url:string ttl_seconds:flags.0?int = InputMedia;
|
||||
inputMediaDocumentExternal#fb52dc99 flags:# url:string ttl_seconds:flags.0?int = InputMedia;
|
||||
inputMediaPhotoExternal#e5bbfe1a flags:# spoiler:flags.1?true url:string ttl_seconds:flags.0?int = InputMedia;
|
||||
inputMediaDocumentExternal#fb52dc99 flags:# spoiler:flags.1?true url:string ttl_seconds:flags.0?int = InputMedia;
|
||||
inputMediaGame#d33f43f3 id:InputGame = InputMedia;
|
||||
inputMediaInvoice#8eb5a6d5 flags:# title:string description:string photo:flags.0?InputWebDocument invoice:Invoice payload:bytes provider:string provider_data:DataJSON start_param:flags.1?string extended_media:flags.2?InputMedia = InputMedia;
|
||||
inputMediaGeoLive#971fa843 flags:# stopped:flags.0?true geo_point:InputGeoPoint heading:flags.2?int period:flags.1?int proximity_notification_radius:flags.3?int = InputMedia;
|
||||
|
|
@ -91,7 +91,7 @@ userEmpty#d3bc4b7a id:long = User;
|
|||
user#8f97c628 flags:# self:flags.10?true contact:flags.11?true mutual_contact:flags.12?true deleted:flags.13?true bot:flags.14?true bot_chat_history:flags.15?true bot_nochats:flags.16?true verified:flags.17?true restricted:flags.18?true min:flags.20?true bot_inline_geo:flags.21?true support:flags.23?true scam:flags.24?true apply_min_photo:flags.25?true fake:flags.26?true bot_attach_menu:flags.27?true premium:flags.28?true attach_menu_enabled:flags.29?true flags2:# id:long access_hash:flags.0?long first_name:flags.1?string last_name:flags.2?string username:flags.3?string phone:flags.4?string photo:flags.5?UserProfilePhoto status:flags.6?UserStatus bot_info_version:flags.14?int restriction_reason:flags.18?Vector<RestrictionReason> bot_inline_placeholder:flags.19?string lang_code:flags.22?string emoji_status:flags.30?EmojiStatus usernames:flags2.0?Vector<Username> = User;
|
||||
|
||||
userProfilePhotoEmpty#4f11bae1 = UserProfilePhoto;
|
||||
userProfilePhoto#82d1f706 flags:# has_video:flags.0?true photo_id:long stripped_thumb:flags.1?bytes dc_id:int = UserProfilePhoto;
|
||||
userProfilePhoto#82d1f706 flags:# has_video:flags.0?true personal:flags.2?true photo_id:long stripped_thumb:flags.1?bytes dc_id:int = UserProfilePhoto;
|
||||
|
||||
userStatusEmpty#9d05049 = UserStatus;
|
||||
userStatusOnline#edb93949 expires:int = UserStatus;
|
||||
|
|
@ -107,7 +107,7 @@ channel#83259464 flags:# creator:flags.0?true left:flags.2?true broadcast:flags.
|
|||
channelForbidden#17d493d5 flags:# broadcast:flags.5?true megagroup:flags.8?true id:long access_hash:long title:string until_date:flags.16?int = Chat;
|
||||
|
||||
chatFull#c9d31138 flags:# can_set_username:flags.7?true has_scheduled:flags.8?true id:long about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector<BotInfo> pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl_period:flags.14?int groupcall_default_join_as:flags.15?Peer theme_emoticon:flags.16?string requests_pending:flags.17?int recent_requesters:flags.17?Vector<long> available_reactions:flags.18?ChatReactions = ChatFull;
|
||||
channelFull#f2355507 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_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true flags2:# can_delete_channel:flags2.0?true antispam:flags2.1?true id:long 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:flags.23?ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?long 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?long location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector<string> groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string requests_pending:flags.28?int recent_requesters:flags.28?Vector<long> default_send_as:flags.29?Peer available_reactions:flags.30?ChatReactions = ChatFull;
|
||||
channelFull#f2355507 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_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true flags2:# can_delete_channel:flags2.0?true antispam:flags2.1?true participants_hidden:flags2.2?true id:long 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:flags.23?ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?long 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?long location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector<string> groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string requests_pending:flags.28?int recent_requesters:flags.28?Vector<long> default_send_as:flags.29?Peer available_reactions:flags.30?ChatReactions = ChatFull;
|
||||
|
||||
chatParticipant#c02d4007 user_id:long inviter_id:long date:int = ChatParticipant;
|
||||
chatParticipantCreator#e46bcee4 user_id:long = ChatParticipant;
|
||||
|
|
@ -124,11 +124,11 @@ message#38116ee0 flags:# out:flags.1?true mentioned:flags.4?true media_unread:fl
|
|||
messageService#2b085862 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true legacy:flags.19?true id:int from_id:flags.8?Peer peer_id:Peer reply_to:flags.3?MessageReplyHeader date:int action:MessageAction ttl_period:flags.25?int = Message;
|
||||
|
||||
messageMediaEmpty#3ded6320 = MessageMedia;
|
||||
messageMediaPhoto#695150d7 flags:# photo:flags.0?Photo ttl_seconds:flags.2?int = MessageMedia;
|
||||
messageMediaPhoto#695150d7 flags:# spoiler:flags.3?true photo:flags.0?Photo ttl_seconds:flags.2?int = MessageMedia;
|
||||
messageMediaGeo#56e0d474 geo:GeoPoint = MessageMedia;
|
||||
messageMediaContact#70322949 phone_number:string first_name:string last_name:string vcard:string user_id:long = MessageMedia;
|
||||
messageMediaUnsupported#9f84f49e = MessageMedia;
|
||||
messageMediaDocument#9cb070d7 flags:# nopremium:flags.3?true document:flags.0?Document ttl_seconds:flags.2?int = MessageMedia;
|
||||
messageMediaDocument#9cb070d7 flags:# nopremium:flags.3?true spoiler:flags.4?true document:flags.0?Document ttl_seconds:flags.2?int = MessageMedia;
|
||||
messageMediaWebPage#a32dd600 webpage:WebPage = MessageMedia;
|
||||
messageMediaVenue#2ec0533f geo:GeoPoint title:string address:string provider:string venue_id:string venue_type:string = MessageMedia;
|
||||
messageMediaGame#fdb19008 game:Game = MessageMedia;
|
||||
|
|
@ -172,6 +172,8 @@ messageActionWebViewDataSent#b4c38cb5 text:string = MessageAction;
|
|||
messageActionGiftPremium#aba0f5c6 currency:string amount:long months:int = MessageAction;
|
||||
messageActionTopicCreate#d999256 flags:# title:string icon_color:int icon_emoji_id:flags.0?long = MessageAction;
|
||||
messageActionTopicEdit#c0944820 flags:# title:flags.0?string icon_emoji_id:flags.1?long closed:flags.2?Bool hidden:flags.3?Bool = MessageAction;
|
||||
messageActionSuggestProfilePhoto#57de635e photo:Photo = MessageAction;
|
||||
messageActionAttachMenuBotAllowed#e7e75f97 = MessageAction;
|
||||
|
||||
dialog#d58a08c6 flags:# pinned:flags.2?true unread_mark:flags.3?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int ttl_period:flags.5?int = Dialog;
|
||||
dialogFolder#71bd134c flags:# pinned:flags.2?true folder:Folder peer:Peer top_message:int unread_muted_peers_count:int unread_unmuted_peers_count:int unread_muted_messages_count:int unread_unmuted_messages_count:int = Dialog;
|
||||
|
|
@ -222,7 +224,7 @@ inputReportReasonFake#f5ddd6e7 = ReportReason;
|
|||
inputReportReasonIllegalDrugs#a8eb2be = ReportReason;
|
||||
inputReportReasonPersonalDetails#9ec7863d = ReportReason;
|
||||
|
||||
userFull#c4b1fc3f flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true voice_messages_forbidden:flags.20?true id:long 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 ttl_period:flags.14?int theme_emoticon:flags.15?string private_forward_name:flags.16?string bot_group_admin_rights:flags.17?ChatAdminRights bot_broadcast_admin_rights:flags.18?ChatAdminRights premium_gifts:flags.19?Vector<PremiumGiftOption> = UserFull;
|
||||
userFull#f8d32aed flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true voice_messages_forbidden:flags.20?true id:long about:flags.1?string settings:PeerSettings personal_photo:flags.21?Photo profile_photo:flags.2?Photo fallback_photo:flags.22?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int theme_emoticon:flags.15?string private_forward_name:flags.16?string bot_group_admin_rights:flags.17?ChatAdminRights bot_broadcast_admin_rights:flags.18?ChatAdminRights premium_gifts:flags.19?Vector<PremiumGiftOption> = UserFull;
|
||||
|
||||
contact#145ade0b user_id:long mutual:Bool = Contact;
|
||||
|
||||
|
|
@ -280,7 +282,6 @@ updateChatUserTyping#83487af0 chat_id:long from_id:Peer action:SendMessageAction
|
|||
updateChatParticipants#7761198 participants:ChatParticipants = Update;
|
||||
updateUserStatus#e5bdf8de user_id:long status:UserStatus = Update;
|
||||
updateUserName#a7848924 user_id:long first_name:string last_name:string usernames:Vector<Username> = Update;
|
||||
updateUserPhoto#f227868c user_id:long date:int photo:UserProfilePhoto previous:Bool = Update;
|
||||
updateNewEncryptedMessage#12bcbd9a message:EncryptedMessage qts:int = Update;
|
||||
updateEncryptedChatTyping#1710f156 chat_id:int = Update;
|
||||
updateEncryption#b4a2e88d chat:EncryptedChat date:int = Update;
|
||||
|
|
@ -381,6 +382,7 @@ updateMoveStickerSetToTop#86fccf85 flags:# masks:flags.0?true emojis:flags.1?tru
|
|||
updateMessageExtendedMedia#5a73a98c peer:Peer msg_id:int extended_media:MessageExtendedMedia = Update;
|
||||
updateChannelPinnedTopic#192efbe3 flags:# pinned:flags.0?true channel_id:long topic_id:int = Update;
|
||||
updateChannelPinnedTopics#fe198602 flags:# channel_id:long order:flags.0?Vector<int> = Update;
|
||||
updateUser#20529438 user_id:long = Update;
|
||||
|
||||
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
|
||||
|
||||
|
|
@ -525,7 +527,7 @@ documentAttributeVideo#ef02ce6 flags:# round_message:flags.0?true supports_strea
|
|||
documentAttributeAudio#9852f9c6 flags:# voice:flags.10?true duration:int title:flags.0?string performer:flags.1?string waveform:flags.2?bytes = DocumentAttribute;
|
||||
documentAttributeFilename#15590068 file_name:string = DocumentAttribute;
|
||||
documentAttributeHasStickers#9801d2f7 = DocumentAttribute;
|
||||
documentAttributeCustomEmoji#fd149899 flags:# free:flags.0?true alt:string stickerset:InputStickerSet = DocumentAttribute;
|
||||
documentAttributeCustomEmoji#fd149899 flags:# free:flags.0?true text_color:flags.1?true alt:string stickerset:InputStickerSet = DocumentAttribute;
|
||||
|
||||
messages.stickersNotModified#f1749a22 = messages.Stickers;
|
||||
messages.stickers#30a6ec7e hash:long stickers:Vector<Document> = messages.Stickers;
|
||||
|
|
@ -603,7 +605,7 @@ keyboardButtonRow#77608b83 buttons:Vector<KeyboardButton> = KeyboardButtonRow;
|
|||
|
||||
replyKeyboardHide#a03e5b85 flags:# selective:flags.2?true = ReplyMarkup;
|
||||
replyKeyboardForceReply#86b40b08 flags:# single_use:flags.1?true selective:flags.2?true placeholder:flags.3?string = ReplyMarkup;
|
||||
replyKeyboardMarkup#85dd99d1 flags:# resize:flags.0?true single_use:flags.1?true selective:flags.2?true rows:Vector<KeyboardButtonRow> placeholder:flags.3?string = ReplyMarkup;
|
||||
replyKeyboardMarkup#85dd99d1 flags:# resize:flags.0?true single_use:flags.1?true selective:flags.2?true persistent:flags.4?true rows:Vector<KeyboardButtonRow> placeholder:flags.3?string = ReplyMarkup;
|
||||
replyInlineMarkup#48a30254 rows:Vector<KeyboardButtonRow> = ReplyMarkup;
|
||||
|
||||
messageEntityUnknown#bb92ba95 offset:int length:int = MessageEntity;
|
||||
|
|
@ -758,6 +760,7 @@ messages.stickerSetInstallResultArchive#35e410a8 sets:Vector<StickerSetCovered>
|
|||
stickerSetCovered#6410a5d2 set:StickerSet cover:Document = StickerSetCovered;
|
||||
stickerSetMultiCovered#3407e51b set:StickerSet covers:Vector<Document> = StickerSetCovered;
|
||||
stickerSetFullCovered#40d13c0e set:StickerSet packs:Vector<StickerPack> keywords:Vector<StickerKeyword> documents:Vector<Document> = StickerSetCovered;
|
||||
stickerSetNoCovered#77b15d1c set:StickerSet = StickerSetCovered;
|
||||
|
||||
maskCoords#aed6dbb2 n:int x:double y:double zoom:double = MaskCoords;
|
||||
|
||||
|
|
@ -1357,7 +1360,7 @@ attachMenuBotIconColor#4576f3f0 name:string color:int = AttachMenuBotIconColor;
|
|||
|
||||
attachMenuBotIcon#b2a7386b flags:# name:string icon:Document colors:flags.0?Vector<AttachMenuBotIconColor> = AttachMenuBotIcon;
|
||||
|
||||
attachMenuBot#c8aa2cd2 flags:# inactive:flags.0?true has_settings:flags.1?true bot_id:long short_name:string peer_types:Vector<AttachMenuPeerType> icons:Vector<AttachMenuBotIcon> = AttachMenuBot;
|
||||
attachMenuBot#c8aa2cd2 flags:# inactive:flags.0?true has_settings:flags.1?true request_write_access:flags.2?true bot_id:long short_name:string peer_types:Vector<AttachMenuPeerType> icons:Vector<AttachMenuBotIcon> = AttachMenuBot;
|
||||
|
||||
attachMenuBotsNotModified#f1d88a5c = AttachMenuBots;
|
||||
attachMenuBots#3c4301c0 hash:long bots:Vector<AttachMenuBot> users:Vector<User> = AttachMenuBots;
|
||||
|
|
@ -1759,7 +1762,7 @@ messages.readReactions#54aa7f8e flags:# peer:InputPeer top_msg_id:flags.0?int =
|
|||
messages.searchSentMedia#107e31a0 q:string filter:MessagesFilter limit:int = messages.Messages;
|
||||
messages.getAttachMenuBots#16fcc2cb hash:long = AttachMenuBots;
|
||||
messages.getAttachMenuBot#77216192 bot:InputUser = AttachMenuBotsBot;
|
||||
messages.toggleBotInAttachMenu#1aee33af bot:InputUser enabled:Bool = Bool;
|
||||
messages.toggleBotInAttachMenu#69f59d69 flags:# write_allowed:flags.0?true bot:InputUser enabled:Bool = Bool;
|
||||
messages.requestWebView#178b480b flags:# from_bot_menu:flags.4?true silent:flags.5?true peer:InputPeer bot:InputUser url:flags.1?string start_param:flags.3?string theme_params:flags.2?DataJSON platform:string reply_to_msg_id:flags.0?int top_msg_id:flags.9?int send_as:flags.13?InputPeer = WebViewResult;
|
||||
messages.prolongWebView#7ff34309 flags:# silent:flags.5?true peer:InputPeer bot:InputUser query_id:long reply_to_msg_id:flags.0?int top_msg_id:flags.9?int send_as:flags.13?InputPeer = Bool;
|
||||
messages.requestSimpleWebView#299bec8e flags:# bot:InputUser url:string theme_params:flags.0?DataJSON platform:string = SimpleWebViewResult;
|
||||
|
|
@ -1782,10 +1785,11 @@ updates.getState#edd4882a = updates.State;
|
|||
updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference;
|
||||
updates.getChannelDifference#3173d78 flags:# force:flags.0?true channel:InputChannel filter:ChannelMessagesFilter pts:int limit:int = updates.ChannelDifference;
|
||||
|
||||
photos.updateProfilePhoto#72d4742c id:InputPhoto = photos.Photo;
|
||||
photos.uploadProfilePhoto#89f30f69 flags:# file:flags.0?InputFile video:flags.1?InputFile video_start_ts:flags.2?double = photos.Photo;
|
||||
photos.updateProfilePhoto#1c3d5956 flags:# fallback:flags.0?true id:InputPhoto = photos.Photo;
|
||||
photos.uploadProfilePhoto#89f30f69 flags:# fallback:flags.3?true file:flags.0?InputFile video:flags.1?InputFile video_start_ts:flags.2?double = photos.Photo;
|
||||
photos.deletePhotos#87cf7f2f id:Vector<InputPhoto> = Vector<long>;
|
||||
photos.getUserPhotos#91cd32a8 user_id:InputUser offset:int max_id:long limit:int = photos.Photos;
|
||||
photos.uploadContactProfilePhoto#b91a83bf flags:# suggest:flags.3?true save:flags.4?true user_id:InputUser file:flags.0?InputFile video:flags.1?InputFile video_start_ts:flags.2?double = photos.Photo;
|
||||
|
||||
upload.saveFilePart#b304a621 file_id:long file_part:int bytes:bytes = Bool;
|
||||
upload.getFile#be5335be flags:# precise:flags.0?true cdn_supported:flags.1?true location:InputFileLocation offset:long limit:int = upload.File;
|
||||
|
|
@ -1874,6 +1878,7 @@ channels.deleteTopicHistory#34435f2d channel:InputChannel top_msg_id:int = messa
|
|||
channels.reorderPinnedForumTopics#2950a18f flags:# force:flags.0?true channel:InputChannel order:Vector<int> = Updates;
|
||||
channels.toggleAntiSpam#68f3e4eb channel:InputChannel enabled:Bool = Updates;
|
||||
channels.reportAntiSpamFalsePositive#a850a693 channel:InputChannel msg_id:int = Bool;
|
||||
channels.toggleParticipantsHidden#6a6e7854 channel:InputChannel enabled:Bool = Updates;
|
||||
|
||||
bots.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON;
|
||||
bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool;
|
||||
|
|
@ -1952,4 +1957,4 @@ stats.getMegagroupStats#dcdf8607 flags:# dark:flags.0?true channel:InputChannel
|
|||
stats.getMessagePublicForwards#5630281b channel:InputChannel msg_id:int offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages;
|
||||
stats.getMessageStats#b6e0a3f5 flags:# dark:flags.0?true channel:InputChannel msg_id:int = stats.MessageStats;
|
||||
|
||||
// LAYER 150
|
||||
// LAYER 151
|
||||
|
|
|
|||
|
|
@ -219,6 +219,8 @@ def pyrogram_api():
|
|||
get_chat_members_count
|
||||
get_dialogs
|
||||
get_dialogs_count
|
||||
get_forum_topics
|
||||
get_forum_topics_by_id
|
||||
set_chat_username
|
||||
get_nearby_chats
|
||||
archive_chats
|
||||
|
|
@ -237,6 +239,16 @@ def pyrogram_api():
|
|||
get_send_as_chats
|
||||
set_send_as_chat
|
||||
set_chat_protected_content
|
||||
close_forum_topic
|
||||
close_general_topic
|
||||
create_forum_topic
|
||||
delete_forum_topic
|
||||
edit_forum_topic
|
||||
edit_general_topic
|
||||
hide_general_topic
|
||||
reopen_forum_topic
|
||||
reopen_general_topic
|
||||
unhide_general_topic
|
||||
""",
|
||||
users="""
|
||||
Users
|
||||
|
|
@ -385,9 +397,13 @@ def pyrogram_api():
|
|||
ChatMemberUpdated
|
||||
ChatJoinRequest
|
||||
ChatJoiner
|
||||
ChatJoinedByRequest
|
||||
Dialog
|
||||
Restriction
|
||||
EmojiStatus
|
||||
ForumTopic
|
||||
PeerUser
|
||||
PeerChannel
|
||||
""",
|
||||
messages_media="""
|
||||
Messages & Media
|
||||
|
|
@ -418,6 +434,12 @@ def pyrogram_api():
|
|||
WebAppData
|
||||
MessageReactions
|
||||
ChatReactions
|
||||
ForumTopicCreated
|
||||
ForumTopicEdited
|
||||
ForumTopicClosed
|
||||
ForumTopicReopened
|
||||
GeneralTopicHidden
|
||||
GeneralTopicUnhidden
|
||||
""",
|
||||
bot_keyboards="""
|
||||
Bot keyboards
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ BOT_INLINE_DISABLED The inline feature of the bot is disabled
|
|||
BOT_INVALID This is not a valid bot
|
||||
BOT_METHOD_INVALID The method can't be used by bots
|
||||
BOT_MISSING This method can only be run by a bot
|
||||
BOT_ONESIDE_NOT_AVAIL Bots can't pin messages for one side only in private chats
|
||||
BOT_PAYMENTS_DISABLED This method can only be run by a bot
|
||||
BOT_POLLS_DISABLED Sending polls by bots has been disabled
|
||||
BOT_RESPONSE_TIMEOUT The bot did not answer to the callback query in time
|
||||
|
|
@ -38,6 +39,7 @@ BROADCAST_REQUIRED The request can only be used with a channel
|
|||
BUTTON_DATA_INVALID The button callback data is invalid or too large
|
||||
BUTTON_TYPE_INVALID The type of one of the buttons you provided is invalid
|
||||
BUTTON_URL_INVALID The button url is invalid
|
||||
BUTTON_USER_PRIVACY_RESTRICTED The privacy settings of the user specified in a keyboard button do not allow creating such button
|
||||
CALL_ALREADY_ACCEPTED The call is already accepted
|
||||
CALL_ALREADY_DECLINED The call is already declined
|
||||
CALL_PEER_INVALID The provided call peer object is invalid
|
||||
|
|
@ -100,6 +102,7 @@ ENCRYPTION_ALREADY_DECLINED The secret chat is already declined
|
|||
ENCRYPTION_DECLINED The secret chat was declined
|
||||
ENCRYPTION_ID_INVALID The provided secret chat id is invalid
|
||||
ENTITIES_TOO_LONG The entity provided contains data that is too long, or you passed too many entities to this message
|
||||
ENTITY_BOUNDS_INVALID The message entity bounds are invalid
|
||||
ENTITY_MENTION_USER_INVALID The mentioned entity is not an user
|
||||
ERROR_TEXT_EMPTY The provided error message is empty
|
||||
EXPIRE_DATE_INVALID The expiration date is invalid
|
||||
|
|
@ -153,6 +156,7 @@ INPUT_USER_DEACTIVATED The target user has been deleted/deactivated
|
|||
INVITE_HASH_EMPTY The invite hash is empty
|
||||
INVITE_HASH_EXPIRED The chat invite link is no longer valid
|
||||
INVITE_HASH_INVALID The invite link hash is invalid
|
||||
INVITE_REQUEST_SENT The request to join this chat or channel has been successfully sent
|
||||
INVITE_REVOKED_MISSING The action required a chat invite link to be revoked first
|
||||
LANG_PACK_INVALID The provided language pack is invalid
|
||||
LASTNAME_INVALID The last name is invalid
|
||||
|
|
@ -296,8 +300,8 @@ STICKER_INVALID The provided sticker is invalid
|
|||
STICKER_PNG_DIMENSIONS The sticker png dimensions are invalid
|
||||
STICKER_PNG_NOPNG Stickers must be png files but the provided image was not a png
|
||||
STICKER_TGS_NOTGS A tgs sticker file was expected, but something else was provided
|
||||
STICKER_VIDEO_NOWEBM A webm video file was expected, but something else was provided
|
||||
STICKER_THUMB_PNG_NOPNG A png sticker thumbnail file was expected, but something else was provided
|
||||
STICKER_VIDEO_NOWEBM A webm video file was expected, but something else was provided
|
||||
TAKEOUT_INVALID The takeout id is invalid
|
||||
TAKEOUT_REQUIRED The method must be invoked inside a takeout session
|
||||
TEMP_AUTH_KEY_EMPTY The temporary auth key provided is empty
|
||||
|
|
@ -340,6 +344,7 @@ USER_NOT_MUTUAL_CONTACT The user is not a mutual contact
|
|||
USER_NOT_PARTICIPANT The user is not a member of this chat
|
||||
VIDEO_CONTENT_TYPE_INVALID The video content type is invalid (i.e.: not streamable)
|
||||
VIDEO_FILE_INVALID The video file is invalid
|
||||
VOICE_MESSAGES_FORBIDDEN Voice messages are restricted
|
||||
VOLUME_LOC_NOT_FOUND The volume location can't be found
|
||||
WALLPAPER_FILE_INVALID The provided file cannot be used as a wallpaper
|
||||
WALLPAPER_INVALID The input wallpaper was not valid
|
||||
|
|
@ -352,5 +357,4 @@ WEBDOCUMENT_URL_EMPTY The web document URL is empty
|
|||
WEBDOCUMENT_URL_INVALID The web document URL is invalid
|
||||
WEBPAGE_CURL_FAILED Telegram server could not fetch the provided URL
|
||||
WEBPAGE_MEDIA_EMPTY The URL doesn't contain any valid media
|
||||
YOU_BLOCKED_USER You blocked this user
|
||||
ENTITY_BOUNDS_INVALID The message entity bounds are invalid
|
||||
YOU_BLOCKED_USER You blocked this user
|
||||
|
|
|
@ -15,6 +15,7 @@ INLINE_BOT_REQUIRED The action must be performed through an inline bot callback
|
|||
MESSAGE_AUTHOR_REQUIRED You are not the author of this message
|
||||
MESSAGE_DELETE_FORBIDDEN You don't have rights to delete messages in this chat, most likely because you are not the author of them
|
||||
POLL_VOTE_REQUIRED Cast a vote in the poll before calling this method
|
||||
PREMIUM_ACCOUNT_REQUIRED This action requires a premium account
|
||||
RIGHT_FORBIDDEN You don't have enough rights for this action, or you tried to set one or more admin rights that can't be applied to this kind of chat (channel or supergroup)
|
||||
SENSITIVE_CHANGE_FORBIDDEN Your sensitive content settings can't be changed at this time
|
||||
TAKEOUT_REQUIRED The method must be invoked inside a takeout session
|
||||
|
|
@ -24,5 +25,4 @@ USER_INVALID The provided user is invalid
|
|||
USER_IS_BLOCKED The user is blocked
|
||||
USER_NOT_MUTUAL_CONTACT The provided user is not a mutual contact
|
||||
USER_PRIVACY_RESTRICTED The user's privacy settings is preventing you to perform this action
|
||||
USER_RESTRICTED You are limited/restricted. You can't perform this action
|
||||
PREMIUM_ACCOUNT_REQUIRED This action requires a premium account
|
||||
USER_RESTRICTED You are limited/restricted. You can't perform this action
|
||||
|
8
docs/requirements.txt
Normal file
8
docs/requirements.txt
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
pyston_lite-autoload
|
||||
sphinx
|
||||
sphinx_rtd_theme==1.0.0
|
||||
sphinx-rtd-dark-mode
|
||||
sphinx_copybutton
|
||||
sphinx-autobuild
|
||||
tgcrypto
|
||||
sphinx-autobuild
|
||||
5
docs/source/_includes/usable-by/bots.rst
Normal file
5
docs/source/_includes/usable-by/bots.rst
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
.. raw:: html
|
||||
|
||||
<strong>Usable by</strong>
|
||||
<span class="usable-by"><i class="fa-solid fa-xmark" style="color: var(--color-red)"></i> Users</span>
|
||||
<span class="usable-by"><i class="fa-solid fa-check" style="color: var(--color-green)"></i> Bots</span>
|
||||
5
docs/source/_includes/usable-by/users-bots.rst
Normal file
5
docs/source/_includes/usable-by/users-bots.rst
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
.. raw:: html
|
||||
|
||||
<strong>Usable by</strong>
|
||||
<span class="usable-by"><i class="fa-solid fa-check" style="color: var(--color-green)"></i> Users</span>
|
||||
<span class="usable-by"><i class="fa-solid fa-check" style="color: var(--color-green)"></i> Bots</span>
|
||||
5
docs/source/_includes/usable-by/users.rst
Normal file
5
docs/source/_includes/usable-by/users.rst
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
.. raw:: html
|
||||
|
||||
<strong>Usable by</strong>
|
||||
<span class="usable-by"><i class="fa-solid fa-check" style="color: var(--color-green)"></i> Users</span>
|
||||
<span class="usable-by"><i class="fa-solid fa-xmark" style="color: var(--color-red)"></i> Bots</span>
|
||||
3
docs/source/_static/css/my.css
Normal file
3
docs/source/_static/css/my.css
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
.pre{
|
||||
color: #bfbfbf;
|
||||
}
|
||||
24
docs/source/api/client.rst
Normal file
24
docs/source/api/client.rst
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
PyroFork Client
|
||||
===============
|
||||
|
||||
You have entered the API Reference section where you can find detailed information about PyroFork'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
|
||||
|
||||
from pyrogram import Client
|
||||
|
||||
app = Client("my_account")
|
||||
|
||||
with app:
|
||||
app.send_message("me", "Hi!")
|
||||
|
||||
-----
|
||||
|
||||
Details
|
||||
-------
|
||||
|
||||
.. autoclass:: pyrogram.Client()
|
||||
68
docs/source/api/decorators.rst
Normal file
68
docs/source/api/decorators.rst
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
Decorators
|
||||
==========
|
||||
|
||||
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
|
||||
functions.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client
|
||||
|
||||
app = Client("my_account")
|
||||
|
||||
|
||||
@app.on_message()
|
||||
def log(client, message):
|
||||
print(message)
|
||||
|
||||
|
||||
app.run()
|
||||
|
||||
.. contents:: Contents
|
||||
:backlinks: none
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
-----
|
||||
|
||||
.. currentmodule:: pyrogram
|
||||
|
||||
Index
|
||||
-----
|
||||
|
||||
.. hlist::
|
||||
:columns: 3
|
||||
|
||||
- :meth:`~Client.on_message`
|
||||
- :meth:`~Client.on_edited_message`
|
||||
- :meth:`~Client.on_callback_query`
|
||||
- :meth:`~Client.on_inline_query`
|
||||
- :meth:`~Client.on_chosen_inline_result`
|
||||
- :meth:`~Client.on_chat_member_updated`
|
||||
- :meth:`~Client.on_chat_join_request`
|
||||
- :meth:`~Client.on_deleted_messages`
|
||||
- :meth:`~Client.on_user_status`
|
||||
- :meth:`~Client.on_poll`
|
||||
- :meth:`~Client.on_disconnect`
|
||||
- :meth:`~Client.on_raw_update`
|
||||
|
||||
-----
|
||||
|
||||
Details
|
||||
-------
|
||||
|
||||
.. Decorators
|
||||
.. autodecorator:: pyrogram.Client.on_message()
|
||||
.. autodecorator:: pyrogram.Client.on_edited_message()
|
||||
.. autodecorator:: pyrogram.Client.on_callback_query()
|
||||
.. autodecorator:: pyrogram.Client.on_inline_query()
|
||||
.. autodecorator:: pyrogram.Client.on_chosen_inline_result()
|
||||
.. autodecorator:: pyrogram.Client.on_chat_member_updated()
|
||||
.. autodecorator:: pyrogram.Client.on_chat_join_request()
|
||||
.. autodecorator:: pyrogram.Client.on_deleted_messages()
|
||||
.. autodecorator:: pyrogram.Client.on_user_status()
|
||||
.. autodecorator:: pyrogram.Client.on_poll()
|
||||
.. autodecorator:: pyrogram.Client.on_disconnect()
|
||||
.. autodecorator:: pyrogram.Client.on_raw_update()
|
||||
8
docs/source/api/enums/ChatAction.rst
Normal file
8
docs/source/api/enums/ChatAction.rst
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
ChatAction
|
||||
==========
|
||||
|
||||
.. autoclass:: pyrogram.enums.ChatAction()
|
||||
:members:
|
||||
|
||||
.. raw:: html
|
||||
:file: ./cleanup.html
|
||||
8
docs/source/api/enums/ChatEventAction.rst
Normal file
8
docs/source/api/enums/ChatEventAction.rst
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
ChatEventAction
|
||||
===============
|
||||
|
||||
.. autoclass:: pyrogram.enums.ChatEventAction()
|
||||
:members:
|
||||
|
||||
.. raw:: html
|
||||
:file: ./cleanup.html
|
||||
8
docs/source/api/enums/ChatMemberStatus.rst
Normal file
8
docs/source/api/enums/ChatMemberStatus.rst
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
ChatMemberStatus
|
||||
================
|
||||
|
||||
.. autoclass:: pyrogram.enums.ChatMemberStatus()
|
||||
:members:
|
||||
|
||||
.. raw:: html
|
||||
:file: ./cleanup.html
|
||||
8
docs/source/api/enums/ChatMembersFilter.rst
Normal file
8
docs/source/api/enums/ChatMembersFilter.rst
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
ChatMembersFilter
|
||||
=================
|
||||
|
||||
.. autoclass:: pyrogram.enums.ChatMembersFilter()
|
||||
:members:
|
||||
|
||||
.. raw:: html
|
||||
:file: ./cleanup.html
|
||||
8
docs/source/api/enums/ChatType.rst
Normal file
8
docs/source/api/enums/ChatType.rst
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
ChatType
|
||||
========
|
||||
|
||||
.. autoclass:: pyrogram.enums.ChatType()
|
||||
:members:
|
||||
|
||||
.. raw:: html
|
||||
:file: ./cleanup.html
|
||||
8
docs/source/api/enums/MessageEntityType.rst
Normal file
8
docs/source/api/enums/MessageEntityType.rst
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
MessageEntityType
|
||||
=================
|
||||
|
||||
.. autoclass:: pyrogram.enums.MessageEntityType()
|
||||
:members:
|
||||
|
||||
.. raw:: html
|
||||
:file: ./cleanup.html
|
||||
8
docs/source/api/enums/MessageMediaType.rst
Normal file
8
docs/source/api/enums/MessageMediaType.rst
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
MessageMediaType
|
||||
================
|
||||
|
||||
.. autoclass:: pyrogram.enums.MessageMediaType()
|
||||
:members:
|
||||
|
||||
.. raw:: html
|
||||
:file: ./cleanup.html
|
||||
8
docs/source/api/enums/MessageServiceType.rst
Normal file
8
docs/source/api/enums/MessageServiceType.rst
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
MessageServiceType
|
||||
==================
|
||||
|
||||
.. autoclass:: pyrogram.enums.MessageServiceType()
|
||||
:members:
|
||||
|
||||
.. raw:: html
|
||||
:file: ./cleanup.html
|
||||
8
docs/source/api/enums/MessagesFilter.rst
Normal file
8
docs/source/api/enums/MessagesFilter.rst
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
MessagesFilter
|
||||
==============
|
||||
|
||||
.. autoclass:: pyrogram.enums.MessagesFilter()
|
||||
:members:
|
||||
|
||||
.. raw:: html
|
||||
:file: ./cleanup.html
|
||||
8
docs/source/api/enums/NextCodeType.rst
Normal file
8
docs/source/api/enums/NextCodeType.rst
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
NextCodeType
|
||||
============
|
||||
|
||||
.. autoclass:: pyrogram.enums.NextCodeType()
|
||||
:members:
|
||||
|
||||
.. raw:: html
|
||||
:file: ./cleanup.html
|
||||
8
docs/source/api/enums/ParseMode.rst
Normal file
8
docs/source/api/enums/ParseMode.rst
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
ParseMode
|
||||
=========
|
||||
|
||||
.. autoclass:: pyrogram.enums.ParseMode()
|
||||
:members:
|
||||
|
||||
.. raw:: html
|
||||
:file: ./cleanup.html
|
||||
8
docs/source/api/enums/PollType.rst
Normal file
8
docs/source/api/enums/PollType.rst
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
PollType
|
||||
========
|
||||
|
||||
.. autoclass:: pyrogram.enums.PollType()
|
||||
:members:
|
||||
|
||||
.. raw:: html
|
||||
:file: ./cleanup.html
|
||||
8
docs/source/api/enums/SentCodeType.rst
Normal file
8
docs/source/api/enums/SentCodeType.rst
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
SentCodeType
|
||||
============
|
||||
|
||||
.. autoclass:: pyrogram.enums.SentCodeType()
|
||||
:members:
|
||||
|
||||
.. raw:: html
|
||||
:file: ./cleanup.html
|
||||
8
docs/source/api/enums/UserStatus.rst
Normal file
8
docs/source/api/enums/UserStatus.rst
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
UserStatus
|
||||
==========
|
||||
|
||||
.. autoclass:: pyrogram.enums.UserStatus()
|
||||
:members:
|
||||
|
||||
.. raw:: html
|
||||
:file: ./cleanup.html
|
||||
9
docs/source/api/enums/cleanup.html
Normal file
9
docs/source/api/enums/cleanup.html
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<script>
|
||||
document
|
||||
.querySelectorAll("em.property")
|
||||
.forEach((elem, i) => i !== 0 ? elem.remove() : true)
|
||||
|
||||
document
|
||||
.querySelectorAll("a.headerlink")
|
||||
.forEach((elem, i) => [0, 1].includes(i) ? true : elem.remove())
|
||||
</script>
|
||||
47
docs/source/api/enums/index.rst
Normal file
47
docs/source/api/enums/index.rst
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
Enumerations
|
||||
============
|
||||
|
||||
This page is about PyroFork enumerations.
|
||||
Enumerations are types that hold a group of related values to be used whenever a constant value is required.
|
||||
They will help you deal with those values in a type-safe way and also enable code completion so that you can be sure
|
||||
to apply only a valid value among the expected ones.
|
||||
|
||||
-----
|
||||
|
||||
.. currentmodule:: pyrogram.enums
|
||||
|
||||
.. autosummary::
|
||||
:nosignatures:
|
||||
|
||||
ChatAction
|
||||
ChatEventAction
|
||||
ChatMemberStatus
|
||||
ChatMembersFilter
|
||||
ChatType
|
||||
MessageEntityType
|
||||
MessageMediaType
|
||||
MessageServiceType
|
||||
MessagesFilter
|
||||
ParseMode
|
||||
PollType
|
||||
SentCodeType
|
||||
NextCodeType
|
||||
UserStatus
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
|
||||
ChatAction
|
||||
ChatEventAction
|
||||
ChatMemberStatus
|
||||
ChatMembersFilter
|
||||
ChatType
|
||||
MessageEntityType
|
||||
MessageMediaType
|
||||
MessageServiceType
|
||||
MessagesFilter
|
||||
ParseMode
|
||||
PollType
|
||||
SentCodeType
|
||||
NextCodeType
|
||||
UserStatus
|
||||
7
docs/source/api/errors/bad-request.rst
Normal file
7
docs/source/api/errors/bad-request.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
400 - BadRequest
|
||||
----------------
|
||||
|
||||
.. csv-table::
|
||||
:file: ../../../../compiler/errors/source/400_BAD_REQUEST.tsv
|
||||
:delim: tab
|
||||
:header-rows: 1
|
||||
7
docs/source/api/errors/flood.rst
Normal file
7
docs/source/api/errors/flood.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
420 - Flood
|
||||
-----------
|
||||
|
||||
.. csv-table::
|
||||
:file: ../../../../compiler/errors/source/420_FLOOD.tsv
|
||||
:delim: tab
|
||||
:header-rows: 1
|
||||
7
docs/source/api/errors/forbidden.rst
Normal file
7
docs/source/api/errors/forbidden.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
403 - Forbidden
|
||||
---------------
|
||||
|
||||
.. csv-table::
|
||||
:file: ../../../../compiler/errors/source/403_FORBIDDEN.tsv
|
||||
:delim: tab
|
||||
:header-rows: 1
|
||||
37
docs/source/api/errors/index.rst
Normal file
37
docs/source/api/errors/index.rst
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
RPC Errors
|
||||
==========
|
||||
|
||||
All PyroFork API errors live inside the ``errors`` sub-package: ``pyrogram.errors``.
|
||||
The errors ids listed here are shown as *UPPER_SNAKE_CASE*, but the actual exception names to import from PyroFork
|
||||
follow the usual *PascalCase* convention.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram.errors import FloodWait
|
||||
|
||||
try:
|
||||
...
|
||||
except FloodWait as e:
|
||||
...
|
||||
|
||||
.. hlist::
|
||||
:columns: 1
|
||||
|
||||
- :doc:`see-other`
|
||||
- :doc:`bad-request`
|
||||
- :doc:`unauthorized`
|
||||
- :doc:`forbidden`
|
||||
- :doc:`not-acceptable`
|
||||
- :doc:`flood`
|
||||
- :doc:`internal-server-error`
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
|
||||
see-other
|
||||
bad-request
|
||||
unauthorized
|
||||
forbidden
|
||||
not-acceptable
|
||||
flood
|
||||
internal-server-error
|
||||
7
docs/source/api/errors/internal-server-error.rst
Normal file
7
docs/source/api/errors/internal-server-error.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
500 - InternalServerError
|
||||
-------------------------
|
||||
|
||||
.. csv-table::
|
||||
:file: ../../../../compiler/errors/source/500_INTERNAL_SERVER_ERROR.tsv
|
||||
:delim: tab
|
||||
:header-rows: 1
|
||||
7
docs/source/api/errors/not-acceptable.rst
Normal file
7
docs/source/api/errors/not-acceptable.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
406 - NotAcceptable
|
||||
-------------------
|
||||
|
||||
.. csv-table::
|
||||
:file: ../../../../compiler/errors/source/406_NOT_ACCEPTABLE.tsv
|
||||
:delim: tab
|
||||
:header-rows: 1
|
||||
7
docs/source/api/errors/see-other.rst
Normal file
7
docs/source/api/errors/see-other.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
303 - SeeOther
|
||||
--------------
|
||||
|
||||
.. csv-table::
|
||||
:file: ../../../../compiler/errors/source/303_SEE_OTHER.tsv
|
||||
:delim: tab
|
||||
:header-rows: 1
|
||||
7
docs/source/api/errors/unauthorized.rst
Normal file
7
docs/source/api/errors/unauthorized.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
401 - Unauthorized
|
||||
------------------
|
||||
|
||||
.. csv-table::
|
||||
:file: ../../../../compiler/errors/source/401_UNAUTHORIZED.tsv
|
||||
:delim: tab
|
||||
:header-rows: 1
|
||||
11
docs/source/api/filters.rst
Normal file
11
docs/source/api/filters.rst
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
Update Filters
|
||||
==============
|
||||
|
||||
Filters are objects that can be used to filter the content of incoming updates.
|
||||
:doc:`Read more about how filters work <../topics/use-filters>`.
|
||||
|
||||
Details
|
||||
-------
|
||||
|
||||
.. automodule:: pyrogram.filters
|
||||
:members:
|
||||
66
docs/source/api/handlers.rst
Normal file
66
docs/source/api/handlers.rst
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
Update Handlers
|
||||
===============
|
||||
|
||||
Handlers are used to instruct PyroFork 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.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client
|
||||
from pyrogram.handlers import MessageHandler
|
||||
|
||||
app = Client("my_account")
|
||||
|
||||
|
||||
def dump(client, message):
|
||||
print(message)
|
||||
|
||||
|
||||
app.add_handler(MessageHandler(dump))
|
||||
|
||||
app.run()
|
||||
|
||||
.. contents:: Contents
|
||||
:backlinks: none
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
-----
|
||||
|
||||
.. currentmodule:: pyrogram.handlers
|
||||
|
||||
Index
|
||||
-----
|
||||
|
||||
.. hlist::
|
||||
:columns: 3
|
||||
|
||||
- :class:`MessageHandler`
|
||||
- :class:`EditedMessageHandler`
|
||||
- :class:`DeletedMessagesHandler`
|
||||
- :class:`CallbackQueryHandler`
|
||||
- :class:`InlineQueryHandler`
|
||||
- :class:`ChosenInlineResultHandler`
|
||||
- :class:`ChatMemberUpdatedHandler`
|
||||
- :class:`UserStatusHandler`
|
||||
- :class:`PollHandler`
|
||||
- :class:`DisconnectHandler`
|
||||
- :class:`RawUpdateHandler`
|
||||
|
||||
-----
|
||||
|
||||
Details
|
||||
-------
|
||||
|
||||
.. Handlers
|
||||
.. autoclass:: MessageHandler()
|
||||
.. autoclass:: EditedMessageHandler()
|
||||
.. autoclass:: DeletedMessagesHandler()
|
||||
.. autoclass:: CallbackQueryHandler()
|
||||
.. autoclass:: InlineQueryHandler()
|
||||
.. autoclass:: ChosenInlineResultHandler()
|
||||
.. autoclass:: ChatMemberUpdatedHandler()
|
||||
.. autoclass:: UserStatusHandler()
|
||||
.. autoclass:: PollHandler()
|
||||
.. autoclass:: DisconnectHandler()
|
||||
.. autoclass:: RawUpdateHandler()
|
||||
99
docs/source/conf.py
Normal file
99
docs/source/conf.py
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
# PyroFork - Telegram MTProto API Client Library for Python
|
||||
# Copyright (C) 2017-present Dan <https://github.com/delivrance>
|
||||
# Copyright (C) 2022-present wulan17 <https://github.com/wulan17>
|
||||
#
|
||||
# This file is part of PyroFork.
|
||||
#
|
||||
# PyroFork 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.
|
||||
#
|
||||
# PyroFork 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 PyroFork. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.abspath("../.."))
|
||||
|
||||
from pyrogram import __version__
|
||||
|
||||
from pygments.styles.friendly import FriendlyStyle
|
||||
|
||||
FriendlyStyle.background_color = "#f3f2f1"
|
||||
|
||||
project = "PyroFork"
|
||||
copyright = f"2022-present, wulan17"
|
||||
author = "wulan17"
|
||||
|
||||
version = ".".join(__version__.split(".")[:-1])
|
||||
|
||||
extensions = [
|
||||
"sphinx.ext.autodoc",
|
||||
"sphinx.ext.napoleon",
|
||||
"sphinx.ext.autosummary",
|
||||
"sphinx.ext.intersphinx",
|
||||
"sphinx_copybutton",
|
||||
"sphinx_rtd_dark_mode"
|
||||
]
|
||||
|
||||
intersphinx_mapping = {
|
||||
"python": ("https://docs.python.org/3", None)
|
||||
}
|
||||
|
||||
master_doc = "index"
|
||||
source_suffix = ".rst"
|
||||
autodoc_member_order = "bysource"
|
||||
|
||||
templates_path = ["../resources/templates"]
|
||||
html_copy_source = False
|
||||
|
||||
napoleon_use_rtype = False
|
||||
napoleon_use_param = False
|
||||
|
||||
pygments_style = "friendly"
|
||||
|
||||
copybutton_prompt_text = "$ "
|
||||
|
||||
suppress_warnings = ["image.not_readable"]
|
||||
|
||||
html_title = "PyroFork Documentation"
|
||||
html_theme = "sphinx_rtd_theme"
|
||||
html_static_path = ["../resources/static","_static"]
|
||||
html_show_sourcelink = True
|
||||
html_show_copyright = False
|
||||
html_theme_options = {
|
||||
"canonical_url": "https://docs.pyrogram.org/",
|
||||
"collapse_navigation": True,
|
||||
"sticky_navigation": False,
|
||||
"logo_only": True,
|
||||
"display_version": False,
|
||||
"style_external_links": True
|
||||
}
|
||||
|
||||
html_logo = "../resources/static/img/pyrogram.png"
|
||||
html_favicon = "../resources/static/img/favicon.ico"
|
||||
|
||||
latex_engine = "xelatex"
|
||||
latex_logo = "../resources/static/img/pyrogram.png"
|
||||
|
||||
latex_elements = {
|
||||
"pointsize": "12pt",
|
||||
"fontpkg": r"""
|
||||
\setmainfont{Open Sans}
|
||||
\setsansfont{Bitter}
|
||||
\setmonofont{Ubuntu Mono}
|
||||
"""
|
||||
}
|
||||
|
||||
html_css_files = [
|
||||
"css/my.css",
|
||||
"https://docs.pyrogram.org/_static/css/custom.css",
|
||||
"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.2/css/all.min.css",
|
||||
]
|
||||
11
docs/source/faq/client-started-but-nothing-happens.rst
Normal file
11
docs/source/faq/client-started-but-nothing-happens.rst
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
Client started, but nothing happens
|
||||
===================================
|
||||
|
||||
A possible cause might be network issues, either yours or Telegram's. To check this, add the following code at
|
||||
the top of your script and run it again. You should see some error mentioning a socket timeout or an unreachable
|
||||
network:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import logging
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
Code hangs when calling stop, restart, add/remove_handler
|
||||
=========================================================
|
||||
|
||||
You tried to ``.stop()``, ``.restart()``, ``.add_handler()`` or ``.remove_handler()`` inside a running handler, but
|
||||
that can't be done because the way PyroFork deals with handlers would make it hang.
|
||||
|
||||
When calling one of the methods above inside an event handler, PyroFork needs to wait for all running handlers to finish
|
||||
in order to continue. Since your handler is blocking the execution by waiting for the called method to finish
|
||||
and since PyroFork needs to wait for your handler to finish, you are left with a deadlock.
|
||||
|
||||
The solution to this problem is to pass ``block=False`` to such methods so that they return immediately and the actual
|
||||
code called asynchronously.
|
||||
23
docs/source/faq/how-to-avoid-flood-waits.rst
Normal file
23
docs/source/faq/how-to-avoid-flood-waits.rst
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
How to avoid Flood Waits?
|
||||
=========================
|
||||
|
||||
Slow things down and make less requests. Moreover, exact limits are unknown and can change anytime based on normal
|
||||
usages.
|
||||
|
||||
When a flood wait happens the server will tell you how much time to wait before continuing.
|
||||
The following shows how to catch the exception in your code and wait the required seconds.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import asyncio
|
||||
from pyrogram.errors import FloodWait
|
||||
|
||||
...
|
||||
try:
|
||||
... # Your code
|
||||
except FloodWait as e:
|
||||
await asyncio.sleep(e.value) # Wait "value" seconds before continuing
|
||||
...
|
||||
|
||||
|
||||
More info about error handling can be found :doc:`here <../start/errors>`.
|
||||
9
docs/source/faq/how-to-use-webhooks.rst
Normal file
9
docs/source/faq/how-to-use-webhooks.rst
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
How to use webhooks?
|
||||
====================
|
||||
|
||||
There is no webhook in PyroFork, simply because there is no HTTP involved. However, a similar technique is
|
||||
being used to make receiving updates efficient.
|
||||
|
||||
PyroFork uses persistent connections via TCP sockets to interact with the server and instead of actively asking for
|
||||
updates every time (polling), PyroFork will sit down and wait for the server to send updates by itself the very moment
|
||||
they are available (server push).
|
||||
45
docs/source/faq/index.rst
Normal file
45
docs/source/faq/index.rst
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
Frequently Asked Questions
|
||||
==========================
|
||||
|
||||
This FAQ page provides answers to common questions about PyroFork and, to some extent, Telegram in general.
|
||||
|
||||
**Contents**
|
||||
|
||||
- :doc:`why-is-the-api-key-needed-for-bots`
|
||||
- :doc:`how-to-use-webhooks`
|
||||
- :doc:`using-the-same-file-id-across-different-accounts`
|
||||
- :doc:`using-multiple-clients-at-once-on-the-same-account`
|
||||
- :doc:`client-started-but-nothing-happens`
|
||||
- :doc:`what-are-the-ip-addresses-of-telegram-data-centers`
|
||||
- :doc:`migrating-the-account-to-another-data-center`
|
||||
- :doc:`why-is-the-client-reacting-slowly-in-supergroups-channels`
|
||||
- :doc:`peer-id-invalid-error`
|
||||
- :doc:`code-hangs-when-calling-stop-restart-add-remove-handler`
|
||||
- :doc:`unicodeencodeerror-codec-cant-encode`
|
||||
- :doc:`uploading-with-urls-gives-error-webpage-curl-failed`
|
||||
- :doc:`sqlite3-operationalerror-database-is-locked`
|
||||
- :doc:`sqlite3-interfaceerror-error-binding-parameter`
|
||||
- :doc:`socket-send-oserror-timeouterror-connection-lost-reset`
|
||||
- :doc:`how-to-avoid-flood-waits`
|
||||
- :doc:`the-account-has-been-limited-deactivated`
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
|
||||
why-is-the-api-key-needed-for-bots
|
||||
how-to-use-webhooks
|
||||
using-the-same-file-id-across-different-accounts
|
||||
using-multiple-clients-at-once-on-the-same-account
|
||||
client-started-but-nothing-happens
|
||||
what-are-the-ip-addresses-of-telegram-data-centers
|
||||
migrating-the-account-to-another-data-center
|
||||
why-is-the-client-reacting-slowly-in-supergroups-channels
|
||||
peer-id-invalid-error
|
||||
code-hangs-when-calling-stop-restart-add-remove-handler
|
||||
unicodeencodeerror-codec-cant-encode
|
||||
uploading-with-urls-gives-error-webpage-curl-failed
|
||||
sqlite3-operationalerror-database-is-locked
|
||||
sqlite3-interfaceerror-error-binding-parameter
|
||||
socket-send-oserror-timeouterror-connection-lost-reset
|
||||
how-to-avoid-flood-waits
|
||||
the-account-has-been-limited-deactivated
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
Migrating the account to another data center
|
||||
============================================
|
||||
|
||||
This question is asked by people who find their account always being connected to one DC (data center), but are
|
||||
connecting from a place far away, thus resulting in slower interactions when using the API because of the greater
|
||||
physical distance between the user and the associated DC.
|
||||
|
||||
When registering an account for the first time, is up to Telegram to decide which DC the new user is going to be
|
||||
created in. It's also up to the server to decide whether to automatically migrate a user in case of prolonged usages
|
||||
from a distant location.
|
||||
14
docs/source/faq/peer-id-invalid-error.rst
Normal file
14
docs/source/faq/peer-id-invalid-error.rst
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
PEER_ID_INVALID error
|
||||
=====================
|
||||
|
||||
This error could mean several things:
|
||||
|
||||
- The chat id you tried to use is simply wrong, check it again.
|
||||
- The chat id refers to a group or channel you are not a member of.
|
||||
- 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 or chat your current session hasn'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 PyroFork too and involves normal usages such as searching
|
||||
for usernames, meeting them in a common group, having their phone contacts saved, getting a message mentioning them
|
||||
or obtaining the dialogs list.
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
socket.send(), OSError(), TimeoutError(), Connection lost/reset
|
||||
===============================================================
|
||||
|
||||
If you get any of these errors chances are you ended up with a slow or inconsistent network connection.
|
||||
Another reason could be because you are blocking the event loop for too long.
|
||||
|
||||
You can consider the following:
|
||||
|
||||
- Use PyroFork asynchronously in its intended way.
|
||||
- Use shorter non-asynchronous processing loops.
|
||||
- Use ``asyncio.sleep()`` instead of ``time.sleep()``.
|
||||
- Use a stable network connection.
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
sqlite3.InterfaceError: Error binding parameter
|
||||
===============================================
|
||||
|
||||
This error occurs when you pass a chat id value of the wrong type when trying to call a method. Most likely, you
|
||||
accidentally passed the whole user or chat object instead of the id or username.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# Wrong. You passed the whole Chat instance
|
||||
app.send_message(chat, "text")
|
||||
|
||||
# Correct
|
||||
app.send_message(chat.id, "text")
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
sqlite3.OperationalError: database is locked
|
||||
============================================
|
||||
|
||||
This error occurs when more than one process is using the same session file, that is, when you run two or more clients
|
||||
at the same time using the same session name or in case another program has accessed the file.
|
||||
|
||||
For example, it could occur when a background script is still running and you forgot about it. In this case, you either
|
||||
restart your system or find and kill the process that is locking the database. On Unix based systems, you can try the
|
||||
following:
|
||||
|
||||
#. ``cd`` into your session file directory.
|
||||
#. ``fuser my_account.session`` to find the process id.
|
||||
#. ``kill 1234`` to gracefully stop the process.
|
||||
#. If the last command doesn't help, use ``kill -9 1234`` instead.
|
||||
|
||||
If you want to run multiple clients on the same account, you must authorize your account (either user or bot)
|
||||
from the beginning every time, and use different session names for each parallel client you are going to use.
|
||||
16
docs/source/faq/the-account-has-been-limited-deactivated.rst
Normal file
16
docs/source/faq/the-account-has-been-limited-deactivated.rst
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
The account has been limited/deactivated
|
||||
========================================
|
||||
|
||||
PyroFork is a framework that interfaces with Telegram; it is at your commands, meaning it only does what you tell it to
|
||||
do, the rest is up to you and Telegram (see `Telegram's ToS`_).
|
||||
|
||||
If you found your account being limited/deactivated, it could be due spam/flood/abuse of the API or the usage of certain
|
||||
virtual/VoIP numbers.
|
||||
|
||||
If you think your account was limited/deactivated by mistake, you can write to recover@telegram.org, contact
|
||||
`@SpamBot`_ or use `this form`_.
|
||||
|
||||
.. _@SpamBot: https://t.me/spambot
|
||||
.. _this form: https://telegram.org/support
|
||||
.. _Telegram's ToS: https://telegram.org/tos
|
||||
|
||||
7
docs/source/faq/unicodeencodeerror-codec-cant-encode.rst
Normal file
7
docs/source/faq/unicodeencodeerror-codec-cant-encode.rst
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
UnicodeEncodeError: '...' 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 PyroFork 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
|
||||
another terminal altogether.
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
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 file is too large. In such cases, your only option is to download the media yourself and upload it from your
|
||||
local machine.
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
Using multiple clients at once on the same account
|
||||
==================================================
|
||||
|
||||
Both user and bot accounts are able to run multiple sessions in parallel. However, you must not use the same session
|
||||
in more than one client at the same time. The correct way to run multiple clients on the same account is by authorizing
|
||||
your account (either user or bot) from the beginning each time, and use one separate session for each parallel client.
|
||||
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
Using the same file_id across different accounts
|
||||
================================================
|
||||
|
||||
Telegram file_id strings are bound to the account which generated them. An attempt in using a foreign file id will
|
||||
result in errors such as ``[400 MEDIA_EMPTY]``. The only exception are stickers' file ids; you can use them across
|
||||
different accounts without any problem.
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
What are the IP addresses of Telegram Data Centers?
|
||||
===================================================
|
||||
|
||||
Telegram is currently composed by a decentralized, multi-DC infrastructure (currently 5 DCs, each of which can
|
||||
work independently) spread across 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.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.130``, ``2001:b28:f23f:f005::a``
|
||||
|
||||
.. csv-table:: Test Environment
|
||||
:header: ID, Location, IPv4, IPv6
|
||||
:widths: auto
|
||||
:align: center
|
||||
|
||||
DC1, "MIA, Miami FL, USA", ``149.154.175.10``, ``2001:b28:f23d:f001::e``
|
||||
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
|
||||
12
docs/source/faq/why-is-the-api-key-needed-for-bots.rst
Normal file
12
docs/source/faq/why-is-the-api-key-needed-for-bots.rst
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
Why is the API key needed for bots?
|
||||
===================================
|
||||
|
||||
Requests against the official bot API endpoints are made via JSON/HTTP and are handled by an intermediate server
|
||||
application that implements the MTProto protocol and uses its own API key to communicate with the MTProto servers.
|
||||
|
||||
.. figure:: //_static/img/mtproto-vs-bot-api.png
|
||||
:align: center
|
||||
|
||||
Using MTProto is the only way to communicate with the actual Telegram servers, and the main API requires developers to
|
||||
identify applications by means of a unique key; the bot token identifies a bot as a user and replaces the user's phone
|
||||
number only.
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
Why is the client reacting slowly in supergroups/channels?
|
||||
==========================================================
|
||||
|
||||
Because of how Telegram works internally, every message you receive and send 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 their DC to the creator's DC and finally to your DC. This is applied to each message and member
|
||||
of a supergroup/channel and the process will inevitably take its time.
|
||||
|
||||
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.
|
||||
172
docs/source/index.rst
Normal file
172
docs/source/index.rst
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
Welcome to PyroFork
|
||||
===================
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<div align="center">
|
||||
<a href="/">
|
||||
<div class="pyrogram-logo-index"><img src="_static/pyrogram.png" alt="PyroFork"></div>
|
||||
<div class="pyrogram-text pyrogram-text-index">PyroFork</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<p align="center">
|
||||
<b>Telegram MTProto API Framework for Python</b>
|
||||
|
||||
<br>
|
||||
<a href="https://pyrogram.org">
|
||||
Homepage
|
||||
</a>
|
||||
•
|
||||
<a href="https://github.com/pyrogram/pyrogram">
|
||||
Development
|
||||
</a>
|
||||
•
|
||||
<a href="https://docs.pyrogram.org/releases">
|
||||
Releases
|
||||
</a>
|
||||
•
|
||||
<a href="https://t.me/pyrogram">
|
||||
News
|
||||
</a>
|
||||
</p>
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client, filters
|
||||
|
||||
app = Client("my_account")
|
||||
|
||||
|
||||
@app.on_message(filters.private)
|
||||
async def hello(client, message):
|
||||
await message.reply("Hello from PyroFork!")
|
||||
|
||||
|
||||
app.run()
|
||||
|
||||
**PyroFork** is a modern, elegant and asynchronous :doc:`MTProto API <topics/mtproto-vs-botapi>` framework.
|
||||
It enables you to easily interact with the main Telegram API through a user account (custom client) or a bot identity
|
||||
(bot API alternative) using Python.
|
||||
|
||||
Support
|
||||
-------
|
||||
|
||||
If you'd like to support PyroFork, you can consider:
|
||||
|
||||
- `Become a GitHub sponsor <https://github.com/sponsors/delivrance>`_.
|
||||
- `Become a LiberaPay patron <https://liberapay.com/delivrance>`_.
|
||||
- `Become an OpenCollective backer <https://opencollective.com/pyrogram>`_.
|
||||
|
||||
How the Documentation is Organized
|
||||
----------------------------------
|
||||
|
||||
Contents are organized into sections composed of self-contained topics which can be all accessed from the sidebar, or by
|
||||
following them in order using the :guilabel:`Next` button at the end of each page.
|
||||
You can also switch to Dark or Light theme or leave on Auto (follows system preferences) by using the dedicated button
|
||||
in the top left corner.
|
||||
|
||||
Here below you can, instead, find a list of the most relevant pages for a quick access.
|
||||
|
||||
First Steps
|
||||
^^^^^^^^^^^
|
||||
|
||||
.. hlist::
|
||||
:columns: 1
|
||||
|
||||
- :doc:`Quick Start <intro/quickstart>`: Overview to get you started quickly.
|
||||
- :doc:`Invoking Methods <start/invoking>`: How to call PyroFork's methods.
|
||||
- :doc:`Handling Updates <start/updates>`: How to handle Telegram updates.
|
||||
- :doc:`Error Handling <start/errors>`: How to handle API errors correctly.
|
||||
|
||||
API Reference
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
.. hlist::
|
||||
:columns: 1
|
||||
|
||||
- :doc:`PyroFork Client <api/client>`: Reference details about the Client class.
|
||||
- :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:`Enumerations <api/enums/index>`: List of available enumerations.
|
||||
- :doc:`Bound Methods <api/bound-methods/index>`: List of convenient bound methods.
|
||||
|
||||
Meta
|
||||
^^^^
|
||||
|
||||
.. hlist::
|
||||
:columns: 1
|
||||
|
||||
- :doc:`PyroFork FAQ <faq/index>`: Answers to common PyroFork questions.
|
||||
- :doc:`Support PyroFork <support>`: Ways to show your appreciation.
|
||||
- :doc:`Release Notes <releases/index>`: Release notes for PyroFork releases.
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
:caption: Introduction
|
||||
|
||||
intro/quickstart
|
||||
intro/install
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
:caption: Getting Started
|
||||
|
||||
start/setup
|
||||
start/auth
|
||||
start/invoking
|
||||
start/updates
|
||||
start/errors
|
||||
start/examples/index
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
:caption: API Reference
|
||||
|
||||
api/client
|
||||
api/methods/index
|
||||
api/types/index
|
||||
api/bound-methods/index
|
||||
api/enums/index
|
||||
api/handlers
|
||||
api/decorators
|
||||
api/errors/index
|
||||
api/filters
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
:caption: Topic Guides
|
||||
|
||||
topics/use-filters
|
||||
topics/create-filters
|
||||
topics/more-on-updates
|
||||
topics/client-settings
|
||||
topics/speedups
|
||||
topics/text-formatting
|
||||
topics/synchronous
|
||||
topics/smart-plugins
|
||||
topics/storage-engines
|
||||
topics/serializing
|
||||
topics/proxy
|
||||
topics/scheduling
|
||||
topics/mtproto-vs-botapi
|
||||
topics/debugging
|
||||
topics/test-servers
|
||||
topics/advanced-usage
|
||||
topics/voice-calls
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
:caption: Meta
|
||||
|
||||
faq/index
|
||||
support
|
||||
releases/index
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
:caption: Telegram Raw API
|
||||
|
||||
telegram/functions/index
|
||||
telegram/types/index
|
||||
telegram/base/index
|
||||
41
docs/source/intro/install.rst
Normal file
41
docs/source/intro/install.rst
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
Install Guide
|
||||
=============
|
||||
|
||||
Being a modern Python framework, PyroFork requires an up to date version of Python to be installed in your system.
|
||||
We recommend using the latest versions of both Python 3 and pip.
|
||||
|
||||
.. contents:: Contents
|
||||
:backlinks: none
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
-----
|
||||
|
||||
Install PyroFork
|
||||
----------------
|
||||
|
||||
- The easiest way to install and upgrade PyroFork to its latest stable version is by using **pip**:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ pip3 install -U git+https://github.com/Mayuri-Chan/pyrofork@dev/pyrofork
|
||||
|
||||
- or, with :doc:`TgCrypto <../topics/speedups>` as extra requirement (recommended):
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ pip3 install -U git+https://github.com/Mayuri-Chan/pyrofork@dev/pyrofork tgcrypto
|
||||
|
||||
Verifying
|
||||
---------
|
||||
|
||||
To verify that PyroFork is correctly installed, open a Python shell and import it.
|
||||
If no error shows up you are good to go.
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
>>> import pyrogram
|
||||
>>> pyrogram.__version__
|
||||
'x.y.z'
|
||||
|
||||
.. _`Github repo`: https://github.com/Mayuri-Chan/pyrofork
|
||||
56
docs/source/intro/quickstart.rst
Normal file
56
docs/source/intro/quickstart.rst
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
Quick Start
|
||||
===========
|
||||
|
||||
The next few steps serve as a quick start to see PyroFork in action as fast as possible.
|
||||
|
||||
Get PyroFork Real Fast
|
||||
----------------------
|
||||
|
||||
.. admonition :: Cloud Credits
|
||||
:class: tip
|
||||
|
||||
If you need a cloud server to host your applications, try Hetzner Cloud. You can sign up with
|
||||
`this link <https://hetzner.cloud/?ref=9CyT92gZEINU>`_ to get €20 in cloud credits.
|
||||
|
||||
1. Install PyroFork with ``pip3 install -U pyrogram``.
|
||||
|
||||
2. Get your own Telegram API key from https://my.telegram.org/apps.
|
||||
|
||||
3. Open the text editor of your choice and paste the following:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import asyncio
|
||||
from pyrogram import Client
|
||||
|
||||
api_id = 12345
|
||||
api_hash = "0123456789abcdef0123456789abcdef"
|
||||
|
||||
|
||||
async def main():
|
||||
async with Client("my_account", api_id, api_hash) as app:
|
||||
await app.send_message("me", "Greetings from **PyroFork**!")
|
||||
|
||||
|
||||
asyncio.run(main())
|
||||
|
||||
4. Replace *api_id* and *api_hash* values with your own.
|
||||
|
||||
5. Save the file as ``hello.py``.
|
||||
|
||||
6. Run the script with ``python3 hello.py``
|
||||
|
||||
7. Follow the instructions on your terminal to login.
|
||||
|
||||
8. Watch PyroFork send a message to yourself.
|
||||
|
||||
Enjoy the API
|
||||
-------------
|
||||
|
||||
That was just a quick overview. In the next few pages of the introduction, we'll take a much more in-depth look of what
|
||||
we have just done above.
|
||||
|
||||
If you are feeling eager to continue you can take a shortcut to :doc:`../start/invoking` and come back
|
||||
later to learn some more details.
|
||||
|
||||
.. _community: https://t.me/PyroFork
|
||||
93
docs/source/start/auth.rst
Normal file
93
docs/source/start/auth.rst
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
Authorization
|
||||
=============
|
||||
|
||||
Once a :doc:`project is set up <setup>`, you will still have to follow a few steps before you can actually use PyroFork to make
|
||||
API calls. This section provides all the information you need in order to authorize yourself as user or bot.
|
||||
|
||||
.. contents:: Contents
|
||||
:backlinks: none
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
-----
|
||||
|
||||
User Authorization
|
||||
------------------
|
||||
|
||||
In order to use the API, Telegram requires that users be authorized via their phone numbers.
|
||||
PyroFork automatically manages this process, all you need to do is create an instance of the
|
||||
:class:`~pyrogram.Client` class by passing to it a ``name`` of your choice (e.g.: "my_account") and call
|
||||
the :meth:`~pyrogram.Client.run` method:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client
|
||||
|
||||
api_id = 12345
|
||||
api_hash = "0123456789abcdef0123456789abcdef"
|
||||
|
||||
app = Client("my_account", api_id=api_id, api_hash=api_hash)
|
||||
|
||||
app.run()
|
||||
|
||||
This starts an interactive shell asking you to input your **phone number**, including your `Country Code`_ (the plus
|
||||
``+`` and minus ``-`` symbols can be omitted) and the **phone code** you will receive in your devices that are already
|
||||
authorized or via SMS:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
Enter phone number: +1-123-456-7890
|
||||
Is "+1-123-456-7890" correct? (y/n): y
|
||||
Enter phone code: 12345
|
||||
Logged in successfully
|
||||
|
||||
After successfully authorizing yourself, a new file called ``my_account.session`` will be created allowing PyroFork to
|
||||
execute API calls with your identity. This file is personal and will be loaded again when you restart your app.
|
||||
You can now remove the api_id and api_hash values from the code as they are not needed anymore.
|
||||
|
||||
.. note::
|
||||
|
||||
The code above does nothing except asking for credentials and keeping the client online, hit :guilabel:`CTRL+C` now
|
||||
to stop your application and keep reading.
|
||||
|
||||
Bot Authorization
|
||||
-----------------
|
||||
|
||||
Bots are a special kind of users that are authorized via their tokens (instead of phone numbers), which are created by
|
||||
the `Bot Father`_. Bot tokens replace the users' phone numbers only — you still need to
|
||||
:doc:`configure a Telegram API key <../start/setup>` with PyroFork, even when using bots.
|
||||
|
||||
The authorization process is automatically managed. All you need to do is choose a ``name`` (can be anything,
|
||||
usually your bot username) and pass your bot token using the ``bot_token`` parameter. The session file will be named
|
||||
after the session name, which will be ``my_bot.session`` for the example below.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client
|
||||
|
||||
api_id = 12345
|
||||
api_hash = "0123456789abcdef0123456789abcdef"
|
||||
bot_token = "123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11"
|
||||
|
||||
app = Client(
|
||||
"my_bot",
|
||||
api_id=api_id, api_hash=api_hash,
|
||||
bot_token=bot_token
|
||||
)
|
||||
|
||||
app.run()
|
||||
|
||||
.. _Country Code: https://en.wikipedia.org/wiki/List_of_country_calling_codes
|
||||
.. _Bot Father: https://t.me/botfather
|
||||
|
||||
.. note::
|
||||
|
||||
The API key (api_id and api_hash) and the bot_token are not required anymore after a successful authorization.
|
||||
This means you can now simply use the following:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client
|
||||
|
||||
app = Client("my_account")
|
||||
app.run()
|
||||
101
docs/source/start/errors.rst
Normal file
101
docs/source/start/errors.rst
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
Error Handling
|
||||
==============
|
||||
|
||||
Errors can be correctly handled with ``try...except`` blocks in order to control the behaviour of your application.
|
||||
PyroFork errors all live inside the ``errors`` package:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import errors
|
||||
|
||||
.. contents:: Contents
|
||||
:backlinks: none
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
-----
|
||||
|
||||
RPCError
|
||||
--------
|
||||
|
||||
The father of all errors is named ``RPCError`` and is able to catch all Telegram API related errors.
|
||||
This error is raised every time a method call against Telegram's API was unsuccessful.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram.errors import RPCError
|
||||
|
||||
.. warning::
|
||||
|
||||
Avoid catching this error everywhere, especially when no feedback is given (i.e. by logging/printing the full error
|
||||
traceback), because it makes it impossible to understand what went wrong.
|
||||
|
||||
Error Categories
|
||||
----------------
|
||||
|
||||
The ``RPCError`` packs together all the possible errors Telegram could raise, but to make things tidier, PyroFork
|
||||
provides categories of errors, which are named after the common HTTP errors and are subclass-ed from the ``RPCError``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram.errors import BadRequest, Forbidden, ...
|
||||
|
||||
- :doc:`303 - SeeOther <../api/errors/see-other>`
|
||||
- :doc:`400 - BadRequest <../api/errors/bad-request>`
|
||||
- :doc:`401 - Unauthorized <../api/errors/unauthorized>`
|
||||
- :doc:`403 - Forbidden <../api/errors/forbidden>`
|
||||
- :doc:`406 - NotAcceptable <../api/errors/not-acceptable>`
|
||||
- :doc:`420 - Flood <../api/errors/flood>`
|
||||
- :doc:`500 - InternalServerError <../api/errors/internal-server-error>`
|
||||
|
||||
Single Errors
|
||||
-------------
|
||||
|
||||
For a fine-grained control over every single error, PyroFork does also expose errors that deal each with a specific
|
||||
issue. For example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram.errors import FloodWait
|
||||
|
||||
These errors subclass directly from the category of errors they belong to, which in turn subclass from the father
|
||||
``RPCError``, thus building a class of error hierarchy such as this:
|
||||
|
||||
- RPCError
|
||||
- BadRequest
|
||||
- ``MessageEmpty``
|
||||
- ``UsernameOccupied``
|
||||
- ``...``
|
||||
- InternalServerError
|
||||
- ``RpcCallFail``
|
||||
- ``InterDcCallError``
|
||||
- ``...``
|
||||
- ``...``
|
||||
|
||||
.. _Errors: api/errors
|
||||
|
||||
Unknown Errors
|
||||
--------------
|
||||
|
||||
In case PyroFork 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.
|
||||
|
||||
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, some other errors contain the DC number on which the request must be repeated on.
|
||||
The value is stored in the ``value`` attribute of the exception object:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import asyncio
|
||||
from pyrogram.errors import FloodWait
|
||||
|
||||
...
|
||||
try:
|
||||
... # Your code
|
||||
except FloodWait as e:
|
||||
await asyncio.sleep(e.value) # Wait N seconds before continuing
|
||||
...
|
||||
68
docs/source/start/examples/bot_keyboards.rst
Normal file
68
docs/source/start/examples/bot_keyboards.rst
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
bot_keyboards
|
||||
=============
|
||||
|
||||
This example will show you how to send normal and inline keyboards (as bot).
|
||||
|
||||
You must log-in as a regular bot in order to send keyboards (use the token from @BotFather).
|
||||
Any attempt in sending keyboards with a user account will be simply ignored by the server.
|
||||
|
||||
send_message() is used as example, but a keyboard can be sent with any other send_* methods,
|
||||
like send_audio(), send_document(), send_location(), etc...
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client
|
||||
from pyrogram.types import (ReplyKeyboardMarkup, InlineKeyboardMarkup,
|
||||
InlineKeyboardButton)
|
||||
|
||||
# Create a client using your bot token
|
||||
app = Client("my_bot", bot_token="123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11")
|
||||
|
||||
|
||||
async def main():
|
||||
async with app:
|
||||
await app.send_message(
|
||||
"me", # Edit this
|
||||
"This is a ReplyKeyboardMarkup example",
|
||||
reply_markup=ReplyKeyboardMarkup(
|
||||
[
|
||||
["A", "B", "C", "D"], # First row
|
||||
["E", "F", "G"], # Second row
|
||||
["H", "I"], # Third row
|
||||
["J"] # Fourth row
|
||||
],
|
||||
resize_keyboard=True # Make the keyboard smaller
|
||||
)
|
||||
)
|
||||
|
||||
await app.send_message(
|
||||
"me", # Edit this
|
||||
"This is a InlineKeyboardMarkup example",
|
||||
reply_markup=InlineKeyboardMarkup(
|
||||
[
|
||||
[ # First row
|
||||
InlineKeyboardButton( # Generates a callback query when pressed
|
||||
"Button",
|
||||
callback_data="data"
|
||||
),
|
||||
InlineKeyboardButton( # Opens a web URL
|
||||
"URL",
|
||||
url="https://docs.pyrogram.org"
|
||||
),
|
||||
],
|
||||
[ # Second row
|
||||
InlineKeyboardButton( # Opens the inline interface
|
||||
"Choose chat",
|
||||
switch_inline_query="pyrogram"
|
||||
),
|
||||
InlineKeyboardButton( # Opens the inline interface in the current chat
|
||||
"Inline here",
|
||||
switch_inline_query_current_chat="pyrogram"
|
||||
)
|
||||
]
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
app.run(main())
|
||||
21
docs/source/start/examples/callback_queries.rst
Normal file
21
docs/source/start/examples/callback_queries.rst
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
callback_queries
|
||||
================
|
||||
|
||||
This example shows how to handle callback queries, i.e.: queries coming from inline button presses.
|
||||
It uses the @on_callback_query decorator to register a CallbackQueryHandler.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client
|
||||
|
||||
app = Client("my_bot", bot_token="123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11")
|
||||
|
||||
|
||||
@app.on_callback_query()
|
||||
async def answer(client, callback_query):
|
||||
await callback_query.answer(
|
||||
f"Button contains: '{callback_query.data}'",
|
||||
show_alert=True)
|
||||
|
||||
|
||||
app.run() # Automatically start() and idle()
|
||||
21
docs/source/start/examples/echo_bot.rst
Normal file
21
docs/source/start/examples/echo_bot.rst
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
echo_bot
|
||||
========
|
||||
|
||||
This simple echo bot replies to every private text message.
|
||||
|
||||
It uses the ``@on_message`` decorator to register a ``MessageHandler`` and applies two filters on it:
|
||||
``filters.text`` and ``filters.private`` to make sure it will reply to private text messages only.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client, filters
|
||||
|
||||
app = Client("my_account")
|
||||
|
||||
|
||||
@app.on_message(filters.text & filters.private)
|
||||
async def echo(client, message):
|
||||
await message.reply(message.text)
|
||||
|
||||
|
||||
app.run() # Automatically start() and idle()
|
||||
20
docs/source/start/examples/get_chat_history.rst
Normal file
20
docs/source/start/examples/get_chat_history.rst
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
get_history
|
||||
===========
|
||||
|
||||
This example shows how to get the full message history of a chat, starting from the latest message.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client
|
||||
|
||||
app = Client("my_account")
|
||||
|
||||
|
||||
async def main():
|
||||
async with app:
|
||||
# "me" refers to your own chat (Saved Messages)
|
||||
async for message in app.get_chat_history("me"):
|
||||
print(message)
|
||||
|
||||
|
||||
app.run(main())
|
||||
22
docs/source/start/examples/get_chat_members.rst
Normal file
22
docs/source/start/examples/get_chat_members.rst
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
get_chat_members
|
||||
================
|
||||
|
||||
This example shows how to get all the members of a chat.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client
|
||||
|
||||
# Target channel/supergroup
|
||||
TARGET = -100123456789
|
||||
|
||||
app = Client("my_account")
|
||||
|
||||
|
||||
async def main():
|
||||
async with app:
|
||||
async for member in app.get_chat_members(TARGET):
|
||||
print(member)
|
||||
|
||||
|
||||
app.run(main())
|
||||
19
docs/source/start/examples/get_dialogs.rst
Normal file
19
docs/source/start/examples/get_dialogs.rst
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
get_dialogs
|
||||
===========
|
||||
|
||||
This example shows how to get the full dialogs list (as user).
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client
|
||||
|
||||
app = Client("my_account")
|
||||
|
||||
|
||||
async def main():
|
||||
async with app:
|
||||
async for dialog in app.get_dialogs():
|
||||
print(dialog.chat.title or dialog.chat.first_name)
|
||||
|
||||
|
||||
app.run(main())
|
||||
20
docs/source/start/examples/hello_world.rst
Normal file
20
docs/source/start/examples/hello_world.rst
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
hello_world
|
||||
===========
|
||||
|
||||
This example demonstrates a basic API usage
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client
|
||||
|
||||
# Create a new Client instance
|
||||
app = Client("my_account")
|
||||
|
||||
|
||||
async def main():
|
||||
async with app:
|
||||
# Send a message, Markdown is enabled by default
|
||||
await app.send_message("me", "Hi there! I'm using **PyroFork**")
|
||||
|
||||
|
||||
app.run(main())
|
||||
46
docs/source/start/examples/index.rst
Normal file
46
docs/source/start/examples/index.rst
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
Examples
|
||||
========
|
||||
|
||||
This page contains example scripts to show you how PyroFork looks like.
|
||||
|
||||
Every script is working right away (provided you correctly set up your credentials), meaning you can simply copy-paste
|
||||
and run. The only things you have to change are session names and target chats, where applicable.
|
||||
|
||||
The examples listed below can be treated as building blocks for your own applications and are meant to be simple enough
|
||||
to give you a basic idea.
|
||||
|
||||
-----
|
||||
|
||||
.. csv-table::
|
||||
:header: Example, Description
|
||||
:widths: auto
|
||||
:align: center
|
||||
|
||||
:doc:`hello_world`, "Demonstration of basic API usage"
|
||||
:doc:`echo_bot`, "Echo every private text message"
|
||||
:doc:`welcome_bot`, "The Welcome Bot in @PyroForkChat"
|
||||
:doc:`get_chat_history`, "Get the full message history of a chat"
|
||||
:doc:`get_chat_members`, "Get all the members of a chat"
|
||||
:doc:`get_dialogs`, "Get all of your dialog chats"
|
||||
:doc:`callback_queries`, "Handle callback queries (as bot) coming from inline button presses"
|
||||
:doc:`inline_queries`, "Handle inline queries (as bot) and answer with results"
|
||||
:doc:`use_inline_bots`, "Query an inline bot (as user) and send a result to a chat"
|
||||
:doc:`bot_keyboards`, "Send normal and inline keyboards using regular bots"
|
||||
:doc:`raw_updates`, "Handle raw updates (old, should be avoided)"
|
||||
|
||||
For more advanced examples, see https://snippets.pyrogram.org.
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
|
||||
hello_world
|
||||
echo_bot
|
||||
welcome_bot
|
||||
get_chat_history
|
||||
get_chat_members
|
||||
get_dialogs
|
||||
callback_queries
|
||||
inline_queries
|
||||
use_inline_bots
|
||||
bot_keyboards
|
||||
raw_updates
|
||||
59
docs/source/start/examples/inline_queries.rst
Normal file
59
docs/source/start/examples/inline_queries.rst
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
inline_queries
|
||||
==============
|
||||
|
||||
This example shows how to handle inline queries.
|
||||
|
||||
Two results are generated when users invoke the bot inline mode, e.g.: @pyrogrambot hi.
|
||||
It uses the @on_inline_query decorator to register an InlineQueryHandler.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client
|
||||
from pyrogram.types import (InlineQueryResultArticle, InputTextMessageContent,
|
||||
InlineKeyboardMarkup, InlineKeyboardButton)
|
||||
|
||||
app = Client("my_bot", bot_token="123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11")
|
||||
|
||||
|
||||
@app.on_inline_query()
|
||||
async def answer(client, inline_query):
|
||||
await inline_query.answer(
|
||||
results=[
|
||||
InlineQueryResultArticle(
|
||||
title="Installation",
|
||||
input_message_content=InputTextMessageContent(
|
||||
"Here's how to install **PyroFork**"
|
||||
),
|
||||
url="https://docs.pyrogram.org/intro/install",
|
||||
description="How to install PyroFork",
|
||||
reply_markup=InlineKeyboardMarkup(
|
||||
[
|
||||
[InlineKeyboardButton(
|
||||
"Open website",
|
||||
url="https://docs.pyrogram.org/intro/install"
|
||||
)]
|
||||
]
|
||||
)
|
||||
),
|
||||
InlineQueryResultArticle(
|
||||
title="Usage",
|
||||
input_message_content=InputTextMessageContent(
|
||||
"Here's how to use **PyroFork**"
|
||||
),
|
||||
url="https://docs.pyrogram.org/start/invoking",
|
||||
description="How to use PyroFork",
|
||||
reply_markup=InlineKeyboardMarkup(
|
||||
[
|
||||
[InlineKeyboardButton(
|
||||
"Open website",
|
||||
url="https://docs.pyrogram.org/start/invoking"
|
||||
)]
|
||||
]
|
||||
)
|
||||
)
|
||||
],
|
||||
cache_time=1
|
||||
)
|
||||
|
||||
|
||||
app.run() # Automatically start() and idle()
|
||||
18
docs/source/start/examples/raw_updates.rst
Normal file
18
docs/source/start/examples/raw_updates.rst
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
raw_updates
|
||||
===========
|
||||
|
||||
This example shows how to handle raw updates.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client
|
||||
|
||||
app = Client("my_account")
|
||||
|
||||
|
||||
@app.on_raw_update()
|
||||
async def raw(client, update, users, chats):
|
||||
print(update)
|
||||
|
||||
|
||||
app.run() # Automatically start() and idle()
|
||||
25
docs/source/start/examples/use_inline_bots.rst
Normal file
25
docs/source/start/examples/use_inline_bots.rst
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
use_inline_bots
|
||||
===============
|
||||
|
||||
This example shows how to query an inline bot (as user).
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client
|
||||
|
||||
# Create a new Client
|
||||
app = Client("my_account")
|
||||
|
||||
|
||||
async def main():
|
||||
async with app:
|
||||
# Get bot results for "hello" from the inline bot @vid
|
||||
bot_results = await app.get_inline_bot_results("vid", "hello")
|
||||
|
||||
# Send the first result to your own chat (Saved Messages)
|
||||
await app.send_inline_bot_result(
|
||||
"me", bot_results.query_id,
|
||||
bot_results.results[0].id)
|
||||
|
||||
|
||||
app.run(main())
|
||||
30
docs/source/start/examples/welcome_bot.rst
Normal file
30
docs/source/start/examples/welcome_bot.rst
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
welcome_bot
|
||||
===========
|
||||
|
||||
This example uses the ``emoji`` module to easily add emoji in your text messages and ``filters``
|
||||
to make it only work for specific messages in a specific chat.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client, emoji, filters
|
||||
|
||||
# Target chat. Can also be a list of multiple chat ids/usernames
|
||||
TARGET = -100123456789
|
||||
# Welcome message template
|
||||
MESSAGE = "{} Welcome to [PyroFork](https://docs.pyrogram.org/)'s group chat {}!"
|
||||
|
||||
app = Client("my_account")
|
||||
|
||||
|
||||
# Filter in only new_chat_members updates generated in TARGET chat
|
||||
@app.on_message(filters.chat(TARGET) & filters.new_chat_members)
|
||||
async def welcome(client, message):
|
||||
# Build the new members list (with mentions) by using their first_name
|
||||
new_members = [u.mention for u in message.new_chat_members]
|
||||
# Build the welcome message by using an emoji and the list we built above
|
||||
text = MESSAGE.format(emoji.SPARKLES, ", ".join(new_members))
|
||||
# Send the welcome message, without the web page preview
|
||||
await message.reply_text(text, disable_web_page_preview=True)
|
||||
|
||||
|
||||
app.run() # Automatically start() and idle()
|
||||
110
docs/source/start/invoking.rst
Normal file
110
docs/source/start/invoking.rst
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
Invoking Methods
|
||||
================
|
||||
|
||||
At this point, we have successfully :doc:`installed PyroFork <../intro/install>` and :doc:`authorized <auth>` our
|
||||
account; we are now aiming towards the core of the framework.
|
||||
|
||||
.. contents:: Contents
|
||||
:backlinks: none
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
-----
|
||||
|
||||
Basic Usage
|
||||
-----------
|
||||
|
||||
Making API calls with PyroFork is very simple. Here's a basic example we are going to examine step by step:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client
|
||||
|
||||
app = Client("my_account")
|
||||
|
||||
|
||||
async def main():
|
||||
async with app:
|
||||
await app.send_message("me", "Hi!")
|
||||
|
||||
|
||||
app.run(main())
|
||||
|
||||
Step-by-step
|
||||
^^^^^^^^^^^^
|
||||
|
||||
#. Let's begin by importing the Client class.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client
|
||||
|
||||
#. Now instantiate a new Client object, "my_account" is a session name of your choice.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
app = Client("my_account")
|
||||
|
||||
#. Async methods must be invoked within an async context.
|
||||
Here we define an async function and put our code inside. Also notice the ``await`` keyword in front of the method
|
||||
call; this is required for all asynchronous methods.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
async def main():
|
||||
async with app:
|
||||
await app.send_message("me", "Hi!")
|
||||
|
||||
#. Finally, we tell Python to schedule our ``main()`` async function by using PyroFork's :meth:`~pyrogram.Client.run`
|
||||
method.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
app.run(main())
|
||||
|
||||
Context Manager
|
||||
---------------
|
||||
|
||||
The ``async with`` statement starts a context manager, which is used as a shortcut for starting, executing and stopping
|
||||
the Client, asynchronously. It does so by automatically calling :meth:`~pyrogram.Client.start` and
|
||||
:meth:`~pyrogram.Client.stop` in a more convenient way which also gracefully stops the client, even in case of
|
||||
unhandled exceptions in your code.
|
||||
|
||||
Below there's the same example as above, but without the use of the context manager:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client
|
||||
|
||||
app = Client("my_account")
|
||||
|
||||
|
||||
async def main():
|
||||
await app.start()
|
||||
await app.send_message("me", "Hi!")
|
||||
await app.stop()
|
||||
|
||||
|
||||
app.run(main())
|
||||
|
||||
Using asyncio.run()
|
||||
-------------------
|
||||
|
||||
Alternatively to the :meth:`~pyrogram.Client.run` method, you can use Python's ``asyncio.run()`` to execute the main
|
||||
function, with one little caveat: the Client instance (and possibly other asyncio resources you are going to use) must
|
||||
be instantiated inside the main function.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import asyncio
|
||||
from pyrogram import Client
|
||||
|
||||
|
||||
async def main():
|
||||
app = Client("my_account")
|
||||
|
||||
async with app:
|
||||
await app.send_message("me", "Hi!")
|
||||
|
||||
|
||||
asyncio.run(main())
|
||||
40
docs/source/start/setup.rst
Normal file
40
docs/source/start/setup.rst
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
Project Setup
|
||||
=============
|
||||
|
||||
We have just :doc:`installed PyroFork <../intro/install>`. In this page we'll discuss what you need to do in order to set up a
|
||||
project with the framework.
|
||||
|
||||
.. contents:: Contents
|
||||
:backlinks: none
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
-----
|
||||
|
||||
API Key
|
||||
-------
|
||||
|
||||
The first step requires you to obtain a valid Telegram API key (api_id and api_hash pair):
|
||||
|
||||
#. Visit https://my.telegram.org/apps and log in with your Telegram account.
|
||||
#. Fill out the form with your details and register a new Telegram application.
|
||||
#. Done. The API key consists of two parts: **api_id** and **api_hash**. Keep it secret.
|
||||
|
||||
.. note::
|
||||
|
||||
The API key defines a token for a Telegram *application* you are going to build.
|
||||
This means that you are able to authorize multiple users or bots with a single API key.
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
Having the API key from the previous step in handy, we can now begin to configure a PyroFork project: pass your API key to PyroFork by using the *api_id* and *api_hash* parameters of the Client class:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client
|
||||
|
||||
api_id = 12345
|
||||
api_hash = "0123456789abcdef0123456789abcdef"
|
||||
|
||||
app = Client("my_account", api_id=api_id, api_hash=api_hash)
|
||||
78
docs/source/start/updates.rst
Normal file
78
docs/source/start/updates.rst
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
Handling Updates
|
||||
================
|
||||
|
||||
:doc:`Invoking API methods <invoking>` sequentially is one way to use PyroFork. This page deals with Telegram updates
|
||||
and how to handle new incoming messages or other events in PyroFork.
|
||||
|
||||
.. contents:: Contents
|
||||
:backlinks: none
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
-----
|
||||
|
||||
Defining Updates
|
||||
----------------
|
||||
|
||||
Updates are events that happen in your Telegram account (incoming messages, new members join,
|
||||
bot button presses, etc.), which are meant to notify you about a new specific state that has changed. These updates are
|
||||
handled by registering one or more callback functions in your app using :doc:`Handlers <../api/handlers>`.
|
||||
|
||||
Each handler deals with a specific event and once a matching update arrives from Telegram, your registered callback
|
||||
function will be called back by the framework and its body executed.
|
||||
|
||||
Registering a Handler
|
||||
---------------------
|
||||
|
||||
To explain how handlers work let's examine the one which will be in charge for handling :class:`~pyrogram.types.Message`
|
||||
updates coming from all around your chats. Every other kind of handler shares the same setup logic and you should not
|
||||
have troubles settings them up once you learn from this section.
|
||||
|
||||
Using Decorators
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
The most elegant way to register a message handler is by using the :meth:`~pyrogram.Client.on_message` decorator:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client
|
||||
|
||||
app = Client("my_account")
|
||||
|
||||
|
||||
@app.on_message()
|
||||
async def my_handler(client, message):
|
||||
await message.forward("me")
|
||||
|
||||
|
||||
app.run()
|
||||
|
||||
The defined function ``my_handler``, which accepts the two arguments *(client, message)*, will be the function that gets
|
||||
executed every time a new message arrives.
|
||||
|
||||
In the last line we see again the :meth:`~pyrogram.Client.run` method, this time used without any argument.
|
||||
Its purpose here is simply to automatically :meth:`~pyrogram.Client.start`, keep the Client online so that it can listen
|
||||
for updates and :meth:`~pyrogram.Client.stop` it once you hit ``CTRL+C``.
|
||||
|
||||
Using add_handler()
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The :meth:`~pyrogram.Client.add_handler` method takes any handler instance that wraps around your defined callback
|
||||
function and registers it in your Client. It is useful in case you want to programmatically add handlers.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client
|
||||
from pyrogram.handlers import MessageHandler
|
||||
|
||||
|
||||
async def my_function(client, message):
|
||||
await message.forward("me")
|
||||
|
||||
|
||||
app = Client("my_account")
|
||||
|
||||
my_handler = MessageHandler(my_function)
|
||||
app.add_handler(my_handler)
|
||||
|
||||
app.run()
|
||||
32
docs/source/support.rst
Normal file
32
docs/source/support.rst
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
Support PyroFork
|
||||
================
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<script async defer src="https://buttons.github.io/buttons.js"></script>
|
||||
|
||||
<div style="float: right; margin-bottom: 10px">
|
||||
<a class="github-button"
|
||||
href="https://github.com/Mayuri-Chan/pyrofork"
|
||||
data-color-scheme="no-preference: light; light: light; dark: dark;"
|
||||
data-icon="octicon-star" data-size="large" data-show-count="true"
|
||||
aria-label="Star pyrogram/pyrogram on GitHub">Star</a>
|
||||
|
||||
<a class="github-button"
|
||||
href="https://github.com/Mayuri-Chan/pyrofork/fork"
|
||||
data-color-scheme="no-preference: light; light: light; dark: dark;"
|
||||
data-icon="octicon-repo-forked" data-size="large"
|
||||
data-show-count="true" aria-label="Fork pyrogram/pyrogram on GitHub">Fork</a>
|
||||
</div>
|
||||
|
||||
<br style="clear: both"/>
|
||||
|
||||
PyroFork is a free and open source project.
|
||||
If you enjoy PyroFork and would like to show your appreciation, consider donating or becoming
|
||||
a sponsor of the project. You can support PyroFork via the ways shown below:
|
||||
|
||||
-----
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<script src="https://opencollective.com/pyrogram/banner.js"></script>
|
||||
124
docs/source/topics/advanced-usage.rst
Normal file
124
docs/source/topics/advanced-usage.rst
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
Advanced Usage
|
||||
==============
|
||||
|
||||
PyroFork's API -- which consists of well documented :doc:`methods <../api/methods/index>` and
|
||||
:doc:`types <../api/types/index>` -- exists to provide an easier interface to the more complex Telegram API.
|
||||
|
||||
In this section, you'll be shown the alternative way of communicating with Telegram using PyroFork: the main "raw"
|
||||
Telegram API with its functions and types.
|
||||
|
||||
.. contents:: Contents
|
||||
:backlinks: none
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
-----
|
||||
|
||||
Telegram Raw API
|
||||
----------------
|
||||
|
||||
If you can't find a high-level method for your needs or if you want complete, low-level access to the whole
|
||||
Telegram API, you have to use the raw :mod:`~pyrogram.raw.functions` and :mod:`~pyrogram.raw.types`.
|
||||
|
||||
As already hinted, raw functions and types can be less convenient. This section will therefore explain some pitfalls to
|
||||
take into consideration when working with the raw API.
|
||||
|
||||
.. tip::
|
||||
|
||||
Every available high-level method in PyroFork is built on top of these raw functions.
|
||||
|
||||
Invoking Functions
|
||||
------------------
|
||||
|
||||
Unlike the :doc:`methods <../api/methods/index>` found in PyroFork'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.
|
||||
|
||||
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.raw.functions``, ``pyrogram.raw.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.invoke` method provided by the
|
||||
Client class and pass the function object you created.
|
||||
|
||||
Here's some examples:
|
||||
|
||||
- Update first name, last name and bio:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client
|
||||
from pyrogram.raw import functions
|
||||
|
||||
async with Client("my_account") as app:
|
||||
await app.invoke(
|
||||
functions.account.UpdateProfile(
|
||||
first_name="First Name", last_name="Last Name",
|
||||
about="New bio text"
|
||||
)
|
||||
)
|
||||
|
||||
- Set online/offline status:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client
|
||||
from pyrogram.raw import functions, types
|
||||
|
||||
async with Client("my_account") as app:
|
||||
# Set online status
|
||||
await app.invoke(functions.account.UpdateStatus(offline=False))
|
||||
|
||||
# Set offline status
|
||||
await app.invoke(functions.account.UpdateStatus(offline=True))
|
||||
|
||||
- Get chat info:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client
|
||||
from pyrogram.raw import functions, types
|
||||
|
||||
async with Client("my_account") as app:
|
||||
r = await app.invoke(
|
||||
functions.channels.GetFullChannel(
|
||||
channel=app.resolve_peer("username")
|
||||
)
|
||||
)
|
||||
|
||||
print(r)
|
||||
|
||||
Chat IDs
|
||||
--------
|
||||
|
||||
The way Telegram works makes it not possible to directly send a message to a user or a chat by using their IDs only.
|
||||
Instead, a pair of ``id`` and ``access_hash`` wrapped in a so called ``InputPeer`` is always needed. PyroFork allows
|
||||
sending messages with IDs only thanks to cached access hashes.
|
||||
|
||||
There are three different InputPeer types, one for each kind of Telegram entity.
|
||||
Whenever an InputPeer is needed you must pass one of these:
|
||||
|
||||
- :class:`~pyrogram.raw.types.InputPeerUser` - Users
|
||||
- :class:`~pyrogram.raw.types.InputPeerChat` - Basic Chats
|
||||
- :class:`~pyrogram.raw.types.InputPeerChannel` - Channels & Supergroups
|
||||
|
||||
But you don't necessarily have to manually instantiate each object because PyroFork already provides
|
||||
:meth:`~pyrogram.Client.resolve_peer` as a convenience utility method that returns the correct InputPeer
|
||||
by accepting a peer ID only.
|
||||
|
||||
Another thing to take into consideration about chat IDs is the way they are represented: they are all integers and
|
||||
all positive within their respective raw types.
|
||||
|
||||
Things are different when working with PyroFork's API because having them in the same space could lead to
|
||||
collisions, and that's why PyroFork uses a slightly different representation for each kind of ID.
|
||||
|
||||
For example, given the ID *123456789*, here's how PyroFork can tell entities apart:
|
||||
|
||||
- ``+ID`` User: *123456789*
|
||||
- ``-ID`` Chat: *-123456789*
|
||||
- ``-100ID`` Channel or Supergroup: *-100123456789*
|
||||
|
||||
So, every time you take a raw ID, make sure to translate it into the correct ID when you want to use it with an
|
||||
high-level method.
|
||||
|
||||
.. _Community: https://t.me/PyroFork
|
||||
46
docs/source/topics/client-settings.rst
Normal file
46
docs/source/topics/client-settings.rst
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
Client Settings
|
||||
===============
|
||||
|
||||
You can control the way your client appears in the Active Sessions menu of an official client by changing some client
|
||||
settings. By default you will see something like the following:
|
||||
|
||||
- Device Model: ``CPython x.y.z``
|
||||
- Application: ``PyroFork x.y.z``
|
||||
- System Version: ``Linux x.y.z``
|
||||
|
||||
.. contents:: Contents
|
||||
:backlinks: none
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
-----
|
||||
|
||||
Set Custom Values
|
||||
-----------------
|
||||
|
||||
To set custom values, you can pass the arguments directly in the Client's constructor.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
app = Client(
|
||||
"my_account",
|
||||
app_version="1.2.3",
|
||||
device_model="PC",
|
||||
system_version="Linux"
|
||||
)
|
||||
|
||||
Set Custom Languages
|
||||
--------------------
|
||||
|
||||
To tell Telegram in which language should speak to you (terms of service, bots, service messages, ...) you can
|
||||
set ``lang_code`` in `ISO 639-1 <https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes>`_ standard (defaults to "en",
|
||||
English).
|
||||
|
||||
With the following code we make Telegram know we want it to speak in Italian (it):
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
app = Client(
|
||||
"my_account",
|
||||
lang_code="it",
|
||||
)
|
||||
109
docs/source/topics/create-filters.rst
Normal file
109
docs/source/topics/create-filters.rst
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
Creating Filters
|
||||
================
|
||||
|
||||
PyroFork already provides lots of built-in :class:`~pyrogram.filters` to work with, but in case you can't find a
|
||||
specific one for your needs or want to build a custom filter by yourself you can use
|
||||
:meth:`filters.create() <pyrogram.filters.create>`.
|
||||
|
||||
.. contents:: Contents
|
||||
:backlinks: none
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
-----
|
||||
|
||||
Custom Filters
|
||||
--------------
|
||||
|
||||
An example to demonstrate how custom filters work is to show how to create and use one for the
|
||||
:class:`~pyrogram.handlers.CallbackQueryHandler`. Note that callback queries updates are only received by bots as result
|
||||
of a user pressing an inline button attached to the bot's message; create and :doc:`authorize your bot <../start/auth>`,
|
||||
then send a message with an inline keyboard to yourself. This allows you to test your filter by pressing the inline
|
||||
button:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram.types import InlineKeyboardMarkup, InlineKeyboardButton
|
||||
|
||||
await app.send_message(
|
||||
"username", # Change this to your username or id
|
||||
"PyroFork custom filter test",
|
||||
reply_markup=InlineKeyboardMarkup(
|
||||
[[InlineKeyboardButton("Press me", "pyrogram")]]
|
||||
)
|
||||
)
|
||||
|
||||
Basic Filters
|
||||
-------------
|
||||
|
||||
For this basic filter we will be using only the first parameter of :meth:`~pyrogram.filters.create`.
|
||||
|
||||
The heart of a filter is its callback function, which accepts three arguments *(self, client, update)* and returns
|
||||
either ``True``, in case you want the update to pass the filter or ``False`` otherwise.
|
||||
|
||||
In this example we are matching the query data to "pyrogram", which means that the filter will only allow callback
|
||||
queries containing "pyrogram" as data:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import filters
|
||||
|
||||
async def func(_, __, query):
|
||||
return query.data == "pyrogram"
|
||||
|
||||
static_data_filter = filters.create(func)
|
||||
|
||||
|
||||
The first two arguments of the callback function are unused here and because of this we named them using underscores.
|
||||
|
||||
Finally, the filter usage remains the same:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@app.on_callback_query(static_data_filter)
|
||||
async def pyrogram_data(_, query):
|
||||
query.answer("it works!")
|
||||
|
||||
Filters with Arguments
|
||||
----------------------
|
||||
|
||||
A more flexible filter would be one that accepts "pyrogram" or any other string as argument at usage time.
|
||||
A dynamic filter like this will make use of named arguments for the :meth:`~pyrogram.filters.create` method and the
|
||||
first argument of the callback function, which is a reference to the filter object itself holding the extra data passed
|
||||
via named arguments.
|
||||
|
||||
This is how a dynamic custom filter looks like:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import filters
|
||||
|
||||
def dynamic_data_filter(data):
|
||||
async def func(flt, _, query):
|
||||
return flt.data == query.data
|
||||
|
||||
# "data" kwarg is accessed with "flt.data" above
|
||||
return filters.create(func, data=data)
|
||||
|
||||
And finally its usage:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@app.on_callback_query(dynamic_data_filter("pyrogram"))
|
||||
async def pyrogram_data(_, query):
|
||||
query.answer("it works!")
|
||||
|
||||
|
||||
Method Calls Inside Filters
|
||||
---------------------------
|
||||
|
||||
The missing piece we haven't covered yet is the second argument of a filter callback function, namely, the ``client``
|
||||
argument. This is a reference to the :obj:`~pyrogram.Client` instance that is running the filter and it is useful in
|
||||
case you would like to make some API calls before deciding whether the filter should allow the update or not:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
async def func(_, client, query):
|
||||
# r = await client.some_api_method()
|
||||
# check response "r" and decide to return True or False
|
||||
...
|
||||
122
docs/source/topics/debugging.rst
Normal file
122
docs/source/topics/debugging.rst
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
Debugging
|
||||
=========
|
||||
|
||||
When working with the API, chances are you'll stumble upon bugs, get stuck and start wondering how to continue. Nothing
|
||||
to actually worry about since PyroFork provides some commodities to help you in this.
|
||||
|
||||
.. contents:: Contents
|
||||
:backlinks: none
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
-----
|
||||
|
||||
Caveman Debugging
|
||||
-----------------
|
||||
|
||||
*The most effective debugging tool is still careful thought, coupled with judiciously placed print statements.*
|
||||
|
||||
-- Brian Kernighan, "Unix for Beginners" (1979)
|
||||
|
||||
Adding ``print()`` statements in crucial parts of your code is by far the most ancient, yet efficient technique for
|
||||
debugging programs, especially considering the concurrent nature of the framework itself. PyroFork goodness in this
|
||||
respect comes with the fact that any object can be nicely printed just by calling ``print(obj)``, thus giving to you
|
||||
an insight of all its inner details.
|
||||
|
||||
Consider the following code:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
me = await app.get_users("me")
|
||||
print(me) # User
|
||||
|
||||
This will show a JSON representation of the object returned by :meth:`~pyrogram.Client.get_users`, which is a
|
||||
:class:`~pyrogram.types.User` instance, in this case. The output on your terminal will be something similar to this:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"_": "User",
|
||||
"id": 123456789,
|
||||
"is_self": true,
|
||||
"is_contact": false,
|
||||
"is_mutual_contact": false,
|
||||
"is_deleted": false,
|
||||
"is_bot": false,
|
||||
"is_verified": false,
|
||||
"is_restricted": false,
|
||||
"is_support": false,
|
||||
"first_name": "PyroFork",
|
||||
"photo": {
|
||||
"_": "ChatPhoto",
|
||||
"small_file_id": "AbCdE...EdCbA",
|
||||
"small_photo_unique_id": "VwXyZ...ZyXwV",
|
||||
"big_file_id": "AbCdE...EdCbA",
|
||||
"big_photo_unique_id": "VwXyZ...ZyXwV"
|
||||
}
|
||||
}
|
||||
|
||||
As you've probably guessed already, PyroFork objects can be nested. That's how compound data are built, and nesting
|
||||
keeps going until we are left with base data types only, such as ``str``, ``int``, ``bool``, etc.
|
||||
|
||||
Accessing Attributes
|
||||
--------------------
|
||||
|
||||
Even though you see a JSON output, it doesn't mean we are dealing with dictionaries; in fact, all PyroFork types are
|
||||
fully-fledged Python objects and the correct way to access any attribute of them is by using the dot notation ``.``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
photo = me.photo
|
||||
print(photo) # ChatPhoto
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"_": "ChatPhoto",
|
||||
"small_file_id": "AbCdE...EdCbA",
|
||||
"small_photo_unique_id": "VwXyZ...ZyXwV",
|
||||
"big_file_id": "AbCdE...EdCbA",
|
||||
"big_photo_unique_id": "VwXyZ...ZyXwV"
|
||||
}
|
||||
|
||||
Checking an Object's Type
|
||||
-------------------------
|
||||
|
||||
Another thing worth talking about is how to tell and check for an object's type.
|
||||
|
||||
As you noticed already, when printing an object you'll see the special attribute ``"_"``. This is just a visual thing
|
||||
useful to show humans the object type, but doesn't really exist anywhere; any attempt in accessing it will lead to an
|
||||
error. The correct way to get the object type is by using the built-in function ``type()``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
status = me.status
|
||||
print(type(status))
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
<class 'pyrogram.types.UserStatus'>
|
||||
|
||||
And to check if an object is an instance of a given class, you use the built-in function ``isinstance()``:
|
||||
|
||||
.. code-block:: python
|
||||
:name: this-py
|
||||
|
||||
from pyrogram.types import UserStatus
|
||||
|
||||
status = me.status
|
||||
print(isinstance(status, UserStatus))
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
True
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<script>
|
||||
var e = document.querySelector("blockquote p.attribution");
|
||||
var s = e.innerHTML;
|
||||
|
||||
e.innerHTML = s[0] + " " + s.slice(1);
|
||||
</script>
|
||||
226
docs/source/topics/more-on-updates.rst
Normal file
226
docs/source/topics/more-on-updates.rst
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
More on Updates
|
||||
===============
|
||||
|
||||
Here we'll show some advanced usages when working with :doc:`update handlers <../start/updates>` and
|
||||
:doc:`filters <use-filters>`.
|
||||
|
||||
.. contents:: Contents
|
||||
:backlinks: none
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
-----
|
||||
|
||||
Handler Groups
|
||||
--------------
|
||||
|
||||
If you register handlers with overlapping (conflicting) filters, only the first one is executed and any other handler
|
||||
will be ignored. This is intended by design.
|
||||
|
||||
In order to handle the very same update more than once, you have to register your handler in a different dispatching
|
||||
group. Dispatching groups hold one or more handlers and are processed sequentially, they are identified by a number
|
||||
(number 0 being the default) and sorted, that is, a lower group number has a higher priority:
|
||||
|
||||
For example, take these two handlers:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@app.on_message(filters.text | filters.sticker)
|
||||
async def text_or_sticker(client, message):
|
||||
print("Text or Sticker")
|
||||
|
||||
|
||||
@app.on_message(filters.text)
|
||||
async def just_text(client, message):
|
||||
print("Just Text")
|
||||
|
||||
Here, ``just_text`` is never executed because ``text_or_sticker``, which has been registered first, already handles
|
||||
texts (``filters.text`` is shared and conflicting). To enable it, register the handler using a different group:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@app.on_message(filters.text, group=1)
|
||||
async def just_text(client, message):
|
||||
print("Just Text")
|
||||
|
||||
Or, if you want ``just_text`` to be executed *before* ``text_or_sticker`` (note ``-1``, which is less than ``0``):
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@app.on_message(filters.text, group=-1)
|
||||
async def just_text(client, message):
|
||||
print("Just Text")
|
||||
|
||||
With :meth:`~pyrogram.Client.add_handler` (without decorators) the same can be achieved with:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
app.add_handler(MessageHandler(just_text, filters.text), -1)
|
||||
|
||||
Update propagation
|
||||
------------------
|
||||
|
||||
Registering multiple handlers, each in a different group, becomes useful when you want to handle the same update more
|
||||
than once. Any incoming update will be sequentially processed by all of your registered functions by respecting the
|
||||
groups priority policy described above. Even in case any handler raises an unhandled exception, PyroFork will still
|
||||
continue to propagate the same update to the next groups until all the handlers are done. Example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@app.on_message(filters.private)
|
||||
async def _(client, message):
|
||||
print(0)
|
||||
|
||||
|
||||
@app.on_message(filters.private, group=1)
|
||||
async def _(client, message):
|
||||
raise Exception("Unhandled exception!") # Simulate an unhandled exception
|
||||
|
||||
|
||||
@app.on_message(filters.private, group=2)
|
||||
async def _(client, message):
|
||||
print(2)
|
||||
|
||||
All these handlers will handle the same kind of messages, that are, messages sent or received in private chats.
|
||||
The output for each incoming update will therefore be:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
0
|
||||
Exception: Unhandled exception!
|
||||
2
|
||||
|
||||
Stop Propagation
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
In order to prevent further propagation of an update in the dispatching phase, you can do *one* of the following:
|
||||
|
||||
- Call the update's bound-method ``.stop_propagation()`` (preferred way).
|
||||
- Manually ``raise StopPropagation`` exception (more suitable for raw updates only).
|
||||
|
||||
.. note::
|
||||
|
||||
Internally, the propagation is stopped by handling a custom exception. ``.stop_propagation()`` is just an elegant
|
||||
and intuitive way to ``raise StopPropagation``; this also means that any code coming *after* calling the method
|
||||
won't be executed as your function just raised an exception to signal the dispatcher not to propagate the
|
||||
update anymore.
|
||||
|
||||
Example with ``stop_propagation()``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@app.on_message(filters.private)
|
||||
async def _(client, message):
|
||||
print(0)
|
||||
|
||||
|
||||
@app.on_message(filters.private, group=1)
|
||||
async def _(client, message):
|
||||
print(1)
|
||||
message.stop_propagation()
|
||||
|
||||
|
||||
@app.on_message(filters.private, group=2)
|
||||
async def _(client, message):
|
||||
print(2)
|
||||
|
||||
Example with ``raise StopPropagation``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import StopPropagation
|
||||
|
||||
@app.on_message(filters.private)
|
||||
async def _(client, message):
|
||||
print(0)
|
||||
|
||||
|
||||
@app.on_message(filters.private, group=1)
|
||||
async ef _(client, message):
|
||||
print(1)
|
||||
raise StopPropagation
|
||||
|
||||
|
||||
@app.on_message(filters.private, group=2)
|
||||
async def _(client, message):
|
||||
print(2)
|
||||
|
||||
Each handler is registered in a different group, but the handler in group number 2 will never be executed because the
|
||||
propagation was stopped earlier. The output of both (equivalent) examples will be:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
0
|
||||
1
|
||||
|
||||
Continue Propagation
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
As opposed to `stopping the update propagation <#stop-propagation>`_ and also as an alternative to the
|
||||
`handler groups <#handler-groups>`_, you can signal the internal dispatcher to continue the update propagation within
|
||||
**the same group** despite having conflicting filters in the next registered handler. This allows you to register
|
||||
multiple handlers with overlapping filters in the same group; to let the dispatcher process the next handler you can do
|
||||
*one* of the following in each handler you want to grant permission to continue:
|
||||
|
||||
- Call the update's bound-method ``.continue_propagation()`` (preferred way).
|
||||
- Manually ``raise ContinuePropagation`` exception (more suitable for raw updates only).
|
||||
|
||||
.. note::
|
||||
|
||||
Internally, the propagation is continued by handling a custom exception. ``.continue_propagation()`` is just an
|
||||
elegant and intuitive way to ``raise ContinuePropagation``; this also means that any code coming *after* calling the
|
||||
method won't be executed as your function just raised an exception to signal the dispatcher to continue with the
|
||||
next available handler.
|
||||
|
||||
|
||||
Example with ``continue_propagation()``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@app.on_message(filters.private)
|
||||
async def _(client, message):
|
||||
print(0)
|
||||
message.continue_propagation()
|
||||
|
||||
|
||||
@app.on_message(filters.private)
|
||||
async def _(client, message):
|
||||
print(1)
|
||||
message.continue_propagation()
|
||||
|
||||
|
||||
@app.on_message(filters.private)
|
||||
async def _(client, message):
|
||||
print(2)
|
||||
|
||||
Example with ``raise ContinuePropagation``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import ContinuePropagation
|
||||
|
||||
@app.on_message(filters.private)
|
||||
async def _(client, message):
|
||||
print(0)
|
||||
raise ContinuePropagation
|
||||
|
||||
|
||||
@app.on_message(filters.private)
|
||||
async def _(client, message):
|
||||
print(1)
|
||||
raise ContinuePropagation
|
||||
|
||||
|
||||
@app.on_message(filters.private)
|
||||
async def _(client, message):
|
||||
print(2)
|
||||
|
||||
Three handlers are registered in the same group, and all of them will be executed because the propagation was continued
|
||||
in each handler (except in the last one, where is useless to do so since there is no more handlers after).
|
||||
The output of both (equivalent) examples will be:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
0
|
||||
1
|
||||
2
|
||||
112
docs/source/topics/mtproto-vs-botapi.rst
Normal file
112
docs/source/topics/mtproto-vs-botapi.rst
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
MTProto vs. Bot API
|
||||
===================
|
||||
|
||||
PyroFork is a framework written from the ground up that acts as a fully-fledged Telegram client based on the MTProto
|
||||
API. This means that PyroFork is able to execute any official client and bot API action and more. This page will
|
||||
therefore show you why PyroFork might be a better choice for your project by comparing the two APIs, but first, let's
|
||||
make it clear what actually is the MTProto and the Bot API.
|
||||
|
||||
.. contents:: Contents
|
||||
:backlinks: none
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
-----
|
||||
|
||||
What is the MTProto API?
|
||||
------------------------
|
||||
|
||||
`MTProto`_, took alone, is the name of the custom-made, open and encrypted communication protocol created by Telegram
|
||||
itself --- it's the only protocol used to exchange information between a client and the actual Telegram servers.
|
||||
|
||||
The MTProto API on the other hand, is what people for convenience call the main Telegram API in order to distinguish it
|
||||
from the Bot API. The main Telegram API is able to authorize both users and bots and is built on top of the MTProto
|
||||
encryption protocol by means of `binary data serialized`_ in a specific way, as described by the `TL language`_, and
|
||||
delivered using UDP, TCP or even HTTP as transport-layer protocol. Clients that make use of Telegram's main API, such as
|
||||
PyroFork, implement all these details.
|
||||
|
||||
.. _MTProto: https://core.telegram.org/mtproto
|
||||
.. _binary data serialized: https://core.telegram.org/mtproto/serialize
|
||||
.. _TL language: https://core.telegram.org/mtproto/TL
|
||||
|
||||
What is the Bot API?
|
||||
--------------------
|
||||
|
||||
The `Bot API`_ is an HTTP(S) interface for building normal bots using a sub-set of the main Telegram API. Bots are
|
||||
special accounts that are authorized via tokens instead of phone numbers. The Bot API is built yet again on top of the
|
||||
main Telegram API, but runs on an intermediate server application that in turn communicates with the actual Telegram
|
||||
servers using MTProto.
|
||||
|
||||
.. figure:: //_static/img/mtproto-vs-bot-api.png
|
||||
:align: center
|
||||
|
||||
.. _Bot API: https://core.telegram.org/bots/api
|
||||
|
||||
Advantages of the MTProto API
|
||||
-----------------------------
|
||||
|
||||
Here is a non-exhaustive list of all the advantages in using MTProto-based libraries -- such as PyroFork -- instead of
|
||||
the official HTTP Bot API. Using PyroFork you can:
|
||||
|
||||
.. hlist::
|
||||
:columns: 1
|
||||
|
||||
- :guilabel:`+` **Authorize both user and bot identities**
|
||||
- :guilabel:`--` The Bot API only allows bot accounts
|
||||
|
||||
.. hlist::
|
||||
:columns: 1
|
||||
|
||||
- :guilabel:`+` **Upload & download any file, up to 2000 MiB each (~2 GB)**
|
||||
- :guilabel:`--` The Bot API allows uploads and downloads of files only up to 50 MB / 20 MB in size (respectively).
|
||||
|
||||
.. hlist::
|
||||
:columns: 1
|
||||
|
||||
- :guilabel:`+` **Has less overhead due to direct connections to Telegram**
|
||||
- :guilabel:`--` The Bot API uses an intermediate server to handle HTTP requests before they are sent to the actual
|
||||
Telegram servers.
|
||||
|
||||
.. hlist::
|
||||
:columns: 1
|
||||
|
||||
- :guilabel:`+` **Run multiple sessions at once (for both user and bot identities)**
|
||||
- :guilabel:`--` The Bot API intermediate server will terminate any other session in case you try to use the same
|
||||
bot again in a parallel connection.
|
||||
|
||||
.. hlist::
|
||||
:columns: 1
|
||||
|
||||
- :guilabel:`+` **Has much more detailed types and powerful methods**
|
||||
- :guilabel:`--` The Bot API types often miss some useful information about Telegram entities and some of the
|
||||
methods are limited as well.
|
||||
|
||||
.. hlist::
|
||||
:columns: 1
|
||||
|
||||
- :guilabel:`+` **Obtain information about any message existing in a chat using their ids**
|
||||
- :guilabel:`--` The Bot API simply doesn't support this
|
||||
|
||||
.. hlist::
|
||||
:columns: 1
|
||||
|
||||
- :guilabel:`+` **Retrieve the whole chat members list of either public or private chats**
|
||||
- :guilabel:`--` The Bot API simply doesn't support this
|
||||
|
||||
.. hlist::
|
||||
:columns: 1
|
||||
|
||||
- :guilabel:`+` **Receive extra updates, such as the one about a user name change**
|
||||
- :guilabel:`--` The Bot API simply doesn't support this
|
||||
|
||||
.. hlist::
|
||||
:columns: 1
|
||||
|
||||
- :guilabel:`+` **Has more meaningful errors in case something went wrong**
|
||||
- :guilabel:`--` The Bot API reports less detailed errors
|
||||
|
||||
.. hlist::
|
||||
:columns: 1
|
||||
|
||||
- :guilabel:`+` **Get API version updates, and thus new features, sooner**
|
||||
- :guilabel:`--` The Bot API is simply slower in implementing new features
|
||||
34
docs/source/topics/proxy.rst
Normal file
34
docs/source/topics/proxy.rst
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
Proxy Settings
|
||||
==============
|
||||
|
||||
PyroFork supports proxies with and without authentication. This feature allows PyroFork to exchange data with Telegram
|
||||
through an intermediate SOCKS 4/5 or HTTP (CONNECT) proxy server.
|
||||
|
||||
.. contents:: Contents
|
||||
:backlinks: none
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
-----
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
To use PyroFork with a proxy, use the *proxy* parameter in the Client class. If your proxy doesn't require authorization
|
||||
you can omit ``username`` and ``password``.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client
|
||||
|
||||
proxy = {
|
||||
"scheme": "socks5", # "socks4", "socks5" and "http" are supported
|
||||
"hostname": "11.22.33.44",
|
||||
"port": 1234,
|
||||
"username": "username",
|
||||
"password": "password"
|
||||
}
|
||||
|
||||
app = Client("my_account", proxy=proxy)
|
||||
|
||||
app.run()
|
||||
65
docs/source/topics/scheduling.rst
Normal file
65
docs/source/topics/scheduling.rst
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
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.
|
||||
|
||||
This page will show examples on how to integrate PyroFork with ``apscheduler`` in both asynchronous and
|
||||
non-asynchronous contexts. For more detailed information, you can visit and learn from the library documentation.
|
||||
|
||||
.. contents:: Contents
|
||||
:backlinks: none
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
-----
|
||||
|
||||
Using apscheduler
|
||||
-----------------
|
||||
|
||||
- Install with ``pip3 install apscheduler``
|
||||
- Documentation: https://apscheduler.readthedocs.io
|
||||
|
||||
Asynchronously
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
.. 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()
|
||||
|
||||
Non-Asynchronously
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. 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()
|
||||
56
docs/source/topics/serializing.rst
Normal file
56
docs/source/topics/serializing.rst
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
Object Serialization
|
||||
====================
|
||||
|
||||
Serializing means converting a PyroFork object, which exists as Python class instance, to a text string that can be
|
||||
easily shared and stored anywhere. PyroFork provides two formats for serializing its objects: one good looking for
|
||||
humans and another more compact for machines that is able to recover the original structures.
|
||||
|
||||
.. contents:: Contents
|
||||
:backlinks: none
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
-----
|
||||
|
||||
For Humans - str(obj)
|
||||
---------------------
|
||||
|
||||
If you want a nicely formatted, human readable JSON representation of any object in the API you can use ``str(obj)``.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
...
|
||||
|
||||
async with app:
|
||||
r = await app.get_chat("me")
|
||||
print(str(r))
|
||||
|
||||
.. tip::
|
||||
|
||||
When using ``print()`` you don't actually need to use ``str()`` on the object because it is called automatically, we
|
||||
have done that above just to show you how to explicitly convert a PyroFork object to JSON.
|
||||
|
||||
For Machines - repr(obj)
|
||||
------------------------
|
||||
|
||||
If you want to share or store objects for future references in a more compact way, you can use ``repr(obj)``. While
|
||||
still pretty much readable, this format is not intended for humans. The advantage of this format is that once you
|
||||
serialize your object, you can use ``eval()`` to get back the original structure; just make sure to ``import pyrogram``,
|
||||
as the process requires the package to be in scope.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import pyrogram
|
||||
|
||||
...
|
||||
|
||||
async with app:
|
||||
r = await app.get_chat("me")
|
||||
|
||||
print(repr(r))
|
||||
print(eval(repr(r)) == r) # True
|
||||
|
||||
.. note::
|
||||
|
||||
Type definitions are subject to changes between versions. You should make sure to store and load objects using the
|
||||
same PyroFork version.
|
||||
306
docs/source/topics/smart-plugins.rst
Normal file
306
docs/source/topics/smart-plugins.rst
Normal file
|
|
@ -0,0 +1,306 @@
|
|||
Smart Plugins
|
||||
=============
|
||||
|
||||
PyroFork embeds a smart, lightweight yet powerful plugin system that is meant to further simplify the organization
|
||||
of large projects and to provide a way for creating pluggable (modular) components that can be easily shared across
|
||||
different PyroFork applications with minimal boilerplate code.
|
||||
|
||||
.. tip::
|
||||
|
||||
Smart Plugins are completely optional and disabled by default.
|
||||
|
||||
.. contents:: Contents
|
||||
:backlinks: none
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
-----
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Prior to the Smart Plugin system, pluggable handlers were already possible. For example, if you wanted to modularize
|
||||
your applications, you had to put your function definitions in separate files and register them inside your main script
|
||||
after importing your modules, like this:
|
||||
|
||||
.. note::
|
||||
|
||||
This is an example application that replies in private chats with two messages: one containing the same
|
||||
text message you sent and the other containing the reversed text message.
|
||||
|
||||
Example: *"PyroFork"* replies with *"PyroFork"* and *"margoryP"*
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
myproject/
|
||||
handlers.py
|
||||
main.py
|
||||
|
||||
- ``handlers.py``
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
async def echo(client, message):
|
||||
await message.reply(message.text)
|
||||
|
||||
|
||||
async def echo_reversed(client, message):
|
||||
await message.reply(message.text[::-1])
|
||||
|
||||
- ``main.py``
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client, filters
|
||||
from pyrogram.handlers import MessageHandler
|
||||
|
||||
from handlers import echo, echo_reversed
|
||||
|
||||
app = Client("my_account")
|
||||
|
||||
app.add_handler(
|
||||
MessageHandler(
|
||||
echo,
|
||||
filters.text & filters.private))
|
||||
|
||||
app.add_handler(
|
||||
MessageHandler(
|
||||
echo_reversed,
|
||||
filters.text & filters.private),
|
||||
group=1)
|
||||
|
||||
app.run()
|
||||
|
||||
This is already nice and doesn't add *too much* boilerplate code, but things can get boring still; you have to
|
||||
manually ``import``, manually :meth:`~pyrogram.Client.add_handler` and manually instantiate each
|
||||
:class:`~pyrogram.handlers.MessageHandler` object because you can't use decorators for your functions.
|
||||
So, what if you could? Smart Plugins solve this issue by taking care of handlers registration automatically.
|
||||
|
||||
Using Smart Plugins
|
||||
-------------------
|
||||
|
||||
Setting up your PyroFork project to accommodate Smart Plugins is pretty straightforward:
|
||||
|
||||
#. Create a new folder to store all the plugins (e.g.: "plugins", "handlers", ...).
|
||||
#. Put your python files full of plugins inside. Organize them as you wish.
|
||||
#. Enable plugins in your Client.
|
||||
|
||||
.. note::
|
||||
|
||||
This is the same example application as shown above, written using the Smart Plugin system.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
myproject/
|
||||
plugins/
|
||||
handlers.py
|
||||
main.py
|
||||
|
||||
- ``plugins/handlers.py``
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client, filters
|
||||
|
||||
|
||||
@Client.on_message(filters.text & filters.private)
|
||||
async def echo(client, message):
|
||||
await message.reply(message.text)
|
||||
|
||||
|
||||
@Client.on_message(filters.text & filters.private, group=1)
|
||||
async def echo_reversed(client, message):
|
||||
await message.reply(message.text[::-1])
|
||||
|
||||
- ``main.py``
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client
|
||||
|
||||
plugins = dict(root="plugins")
|
||||
|
||||
Client("my_account", plugins=plugins).run()
|
||||
|
||||
|
||||
The first important thing to note is the new ``plugins`` folder. You can put *any python file* in *any subfolder* and
|
||||
each file can contain *any decorated function* (handlers) with one limitation: within a single module (file) you must
|
||||
use different names for each decorated function.
|
||||
|
||||
The second thing is telling PyroFork where to look for your plugins: you can use the Client parameter "plugins";
|
||||
the *root* value must match the name of your plugins root folder. Your PyroFork Client instance will **automatically**
|
||||
scan the folder upon starting to search for valid handlers and register them for you.
|
||||
|
||||
Then you'll notice you can now use decorators. That's right, you can apply the usual decorators to your callback
|
||||
functions in a static way, i.e. **without having the Client instance around**: simply use ``@Client`` (Client class)
|
||||
instead of the usual ``@app`` (Client instance) and things will work just the same.
|
||||
|
||||
Specifying the Plugins to include
|
||||
---------------------------------
|
||||
|
||||
By default, if you don't explicitly supply a list of plugins, every valid one found inside your plugins root folder will
|
||||
be included by following the alphabetical order of the directory structure (files and subfolders); the single handlers
|
||||
found inside each module will be, instead, loaded in the order they are defined, from top to bottom.
|
||||
|
||||
.. note::
|
||||
|
||||
Remember: there can be at most one handler, within a group, dealing with a specific update. Plugins with overlapping
|
||||
filters included a second time will not work, by design. Learn more at :doc:`More on Updates <more-on-updates>`.
|
||||
|
||||
This default loading behaviour is usually enough, but sometimes you want to have more control on what to include (or
|
||||
exclude) and in which exact order to load plugins. The way to do this is to make use of ``include`` and ``exclude``
|
||||
directives in the dictionary passed as Client argument. Here's how they work:
|
||||
|
||||
- If both ``include`` and ``exclude`` are omitted, all plugins are loaded as described above.
|
||||
- If ``include`` is given, only the specified plugins will be loaded, in the order they are passed.
|
||||
- If ``exclude`` is given, the plugins specified here will be unloaded.
|
||||
|
||||
The ``include`` and ``exclude`` value is a **list of strings**. Each string containing the path of the module relative
|
||||
to the plugins root folder, in Python notation (dots instead of slashes).
|
||||
|
||||
E.g.: ``subfolder.module`` refers to ``plugins/subfolder/module.py``, with ``root="plugins"``.
|
||||
|
||||
You can also choose the order in which the single handlers inside a module are loaded, thus overriding the default
|
||||
top-to-bottom loading policy. You can do this by appending the name of the functions to the module path, each one
|
||||
separated by a blank space.
|
||||
|
||||
E.g.: ``subfolder.module fn2 fn1 fn3`` will load *fn2*, *fn1* and *fn3* from *subfolder.module*, in this order.
|
||||
|
||||
Examples
|
||||
^^^^^^^^
|
||||
|
||||
Given this plugins folder structure with three modules, each containing their own handlers (fn1, fn2, etc...), which are
|
||||
also organized in subfolders:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
myproject/
|
||||
plugins/
|
||||
subfolder1/
|
||||
plugins1.py
|
||||
- fn1
|
||||
- fn2
|
||||
- fn3
|
||||
subfolder2/
|
||||
plugins2.py
|
||||
...
|
||||
plugins0.py
|
||||
...
|
||||
...
|
||||
|
||||
- Load every handler from every module, namely *plugins0.py*, *plugins1.py* and *plugins2.py* in alphabetical order
|
||||
(files) and definition order (handlers inside files):
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
plugins = dict(root="plugins")
|
||||
|
||||
Client("my_account", plugins=plugins).run()
|
||||
|
||||
- Load only handlers defined inside *plugins2.py* and *plugins0.py*, in this order:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
plugins = dict(
|
||||
root="plugins",
|
||||
include=[
|
||||
"subfolder2.plugins2",
|
||||
"plugins0"
|
||||
]
|
||||
)
|
||||
|
||||
Client("my_account", plugins=plugins).run()
|
||||
|
||||
- Load everything except the handlers inside *plugins2.py*:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
plugins = dict(
|
||||
root="plugins",
|
||||
exclude=["subfolder2.plugins2"]
|
||||
)
|
||||
|
||||
Client("my_account", plugins=plugins).run()
|
||||
|
||||
- Load only *fn3*, *fn1* and *fn2* (in this order) from *plugins1.py*:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
plugins = dict(
|
||||
root="plugins",
|
||||
include=["subfolder1.plugins1 fn3 fn1 fn2"]
|
||||
)
|
||||
|
||||
Client("my_account", plugins=plugins).run()
|
||||
|
||||
Load/Unload Plugins at Runtime
|
||||
------------------------------
|
||||
|
||||
In the previous section we've explained how to specify which plugins to load and which to ignore before your Client
|
||||
starts. Here we'll show, instead, how to unload and load again a previously registered plugin at runtime.
|
||||
|
||||
Each function decorated with the usual ``on_message`` decorator (or any other decorator that deals with Telegram
|
||||
updates) will be modified in such a way that a special ``handlers`` attribute pointing to a list of tuples of
|
||||
*(handler: Handler, group: int)* is attached to the function object itself.
|
||||
|
||||
- ``plugins/handlers.py``
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@Client.on_message(filters.text & filters.private)
|
||||
async def echo(client, message):
|
||||
await message.reply(message.text)
|
||||
|
||||
print(echo)
|
||||
print(echo.handlers)
|
||||
|
||||
- Printing ``echo`` will show something like ``<function echo at 0x10e3b6598>``.
|
||||
|
||||
- Printing ``echo.handlers`` will reveal the handlers, that is, a list of tuples containing the actual handlers and
|
||||
the groups they were registered on ``[(<MessageHandler object at 0x10e3abc50>, 0)]``.
|
||||
|
||||
Unloading
|
||||
^^^^^^^^^
|
||||
|
||||
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* instance:
|
||||
|
||||
- ``main.py``
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from plugins.handlers import echo
|
||||
|
||||
handlers = echo.handlers
|
||||
|
||||
for h in handlers:
|
||||
app.remove_handler(*h)
|
||||
|
||||
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
|
||||
|
||||
handlers = echo.handlers
|
||||
handler, group = handlers[0]
|
||||
|
||||
app.remove_handler(handler, group)
|
||||
|
||||
Loading
|
||||
^^^^^^^
|
||||
|
||||
Similarly to the unloading process, in order to load again a previously unloaded plugin you do the same, but this time
|
||||
using :meth:`~pyrogram.Client.add_handler` instead. Example:
|
||||
|
||||
- ``main.py``
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from plugins.handlers import echo
|
||||
|
||||
...
|
||||
|
||||
handlers = echo.handlers
|
||||
|
||||
for h in handlers:
|
||||
app.add_handler(*h)
|
||||
88
docs/source/topics/speedups.rst
Normal file
88
docs/source/topics/speedups.rst
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
Speedups
|
||||
========
|
||||
|
||||
PyroFork's speed can be boosted up by using TgCrypto and uvloop.
|
||||
|
||||
.. contents:: Contents
|
||||
:backlinks: none
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
-----
|
||||
|
||||
TgCrypto
|
||||
--------
|
||||
|
||||
TgCrypto_ is a high-performance, easy-to-install cryptography library specifically written in C for PyroFork as a Python
|
||||
extension. It is a replacement for a slower Python-only alternative and implements the cryptographic algorithms Telegram
|
||||
requires, namely: AES-256-IGE, AES-256-CTR and AES-256-CBC.
|
||||
|
||||
Installation
|
||||
^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ pip3 install -U tgcrypto
|
||||
|
||||
Usage
|
||||
^^^^^
|
||||
|
||||
PyroFork will automatically make use of TgCrypto when detected, all you need to do is to install it.
|
||||
|
||||
uvloop
|
||||
------
|
||||
|
||||
uvloop_ is a fast, drop-in replacement of the built-in asyncio event loop. uvloop is implemented in Cython and uses
|
||||
libuv under the hood. It makes asyncio 2-4x faster.
|
||||
|
||||
Installation
|
||||
^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ pip3 install -U uvloop
|
||||
|
||||
Usage
|
||||
^^^^^
|
||||
|
||||
Call ``uvloop.install()`` before calling ``asyncio.run()`` or ``app.run()``.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import asyncio
|
||||
import uvloop
|
||||
|
||||
from pyrogram import Client
|
||||
|
||||
|
||||
async def main():
|
||||
app = Client("my_account")
|
||||
|
||||
async with app:
|
||||
print(await app.get_me())
|
||||
|
||||
|
||||
uvloop.install()
|
||||
asyncio.run(main())
|
||||
|
||||
The ``uvloop.install()`` call also needs to be placed before creating a Client instance.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import uvloop
|
||||
from pyrogram import Client
|
||||
|
||||
uvloop.install()
|
||||
|
||||
app = Client("my_account")
|
||||
|
||||
|
||||
@app.on_message()
|
||||
async def hello(client, message):
|
||||
print(await client.get_me())
|
||||
|
||||
|
||||
app.run()
|
||||
|
||||
.. _TgCrypto: https://github.com/pyrogram/tgcrypto
|
||||
.. _uvloop: https://github.com/MagicStack/uvloop
|
||||
90
docs/source/topics/storage-engines.rst
Normal file
90
docs/source/topics/storage-engines.rst
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
Storage Engines
|
||||
===============
|
||||
|
||||
Every time you login to Telegram, some personal piece of data are created and held by both parties (the client, PyroFork
|
||||
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.
|
||||
|
||||
.. contents:: Contents
|
||||
:backlinks: none
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
-----
|
||||
|
||||
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, PyroFork needs to store the generated session data somewhere.
|
||||
|
||||
Different Storage Engines
|
||||
-------------------------
|
||||
|
||||
PyroFork offers two different types of storage engines: a **File Storage** and a **Memory Storage**.
|
||||
These engines are well integrated in the framework 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 details.
|
||||
The database will be saved to disk as a single portable file and is designed to efficiently store and retrieve
|
||||
data whenever they are needed.
|
||||
|
||||
To use this type of engine, simply pass any name of your choice to the ``name`` parameter of the
|
||||
:obj:`~pyrogram.Client` constructor, as usual:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client
|
||||
|
||||
async with Client("my_account") as app:
|
||||
print(await 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 PyroFork 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 True to the
|
||||
``in_memory`` parameter of the :obj:`~pyrogram.Client` constructor:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client
|
||||
|
||||
async with Client("my_account", in_memory=True) as app:
|
||||
print(await 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
|
||||
|
||||
async with Client("my_account", in_memory=True) as app:
|
||||
print(await app.export_session_string())
|
||||
|
||||
...and save the resulting string. You can use this string by passing it as Client argument the next time you want to
|
||||
login using the same session; the storage used will still be in-memory:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client
|
||||
|
||||
session_string = "...ZnUIFD8jsjXTb8g_vpxx48k1zkov9sapD-tzjz-S4WZv70M..."
|
||||
|
||||
async with Client("my_account", session_string=session_string) as app:
|
||||
print(await app.get_me())
|
||||
|
||||
Session strings are useful when you want to run authorized PyroFork clients on platforms where their ephemeral
|
||||
filesystems makes it harder for a file-based storage engine to properly work as intended.
|
||||
88
docs/source/topics/synchronous.rst
Normal file
88
docs/source/topics/synchronous.rst
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
Synchronous Usage
|
||||
=================
|
||||
|
||||
PyroFork is an asynchronous framework and as such is subject to the asynchronous rules. It can, however, run in
|
||||
synchronous mode (also known as non-asynchronous or sync/non-async for short). This mode exists mainly as a convenience
|
||||
way for invoking methods without the need of ``async``/``await`` keywords and the extra boilerplate, but **it's not the
|
||||
intended way to use the framework**.
|
||||
|
||||
You can use PyroFork in this synchronous mode when you want to write something short and contained without the
|
||||
async boilerplate or in case you want to combine PyroFork with other libraries that are not async.
|
||||
|
||||
.. warning::
|
||||
|
||||
You have to be very careful when using the framework in its synchronous, non-native form, especially when combined
|
||||
with other non-async libraries because thread blocking operations that clog the asynchronous event loop underneath
|
||||
will make the program run erratically.
|
||||
|
||||
.. contents:: Contents
|
||||
:backlinks: none
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
-----
|
||||
|
||||
Synchronous Invocations
|
||||
-----------------------
|
||||
|
||||
The following is a standard example of running asynchronous functions with Python's asyncio.
|
||||
PyroFork is being used inside the main function with its asynchronous interface.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import asyncio
|
||||
from pyrogram import Client
|
||||
|
||||
|
||||
async def main():
|
||||
app = Client("my_account")
|
||||
|
||||
async with app:
|
||||
await app.send_message("me", "Hi!")
|
||||
|
||||
|
||||
asyncio.run(main())
|
||||
|
||||
To run PyroFork synchronously, use the non-async context manager as shown in the following example.
|
||||
As you can see, the non-async example becomes less cluttered.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client
|
||||
|
||||
app = Client("my_account")
|
||||
|
||||
with app:
|
||||
app.send_message("me", "Hi!")
|
||||
|
||||
Synchronous handlers
|
||||
--------------------
|
||||
|
||||
You can also have synchronous handlers; you only need to define the callback function without using ``async def`` and
|
||||
invoke API methods by not placing ``await`` in front of them. Mixing ``def`` and ``async def`` handlers together is also
|
||||
possible.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@app.on_message()
|
||||
async def handler1(client, message):
|
||||
await message.forward("me")
|
||||
|
||||
@app.on_edited_message()
|
||||
def handler2(client, message):
|
||||
message.forward("me")
|
||||
|
||||
uvloop usage
|
||||
------------
|
||||
|
||||
When using PyroFork in its synchronous mode combined with uvloop, you need to call ``uvloop.install()`` before importing
|
||||
PyroFork.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import uvloop
|
||||
uvloop.install()
|
||||
|
||||
from pyrogram import Client
|
||||
|
||||
...
|
||||
41
docs/source/topics/test-servers.rst
Normal file
41
docs/source/topics/test-servers.rst
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
Test Servers
|
||||
============
|
||||
|
||||
If you wish to test your application in a separate environment, PyroFork is able to authorize your account into
|
||||
Telegram's test servers without hassle. All you need to do is start a new session (e.g.: "my_account_test") using
|
||||
``test_mode=True``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import Client
|
||||
|
||||
async with Client("my_account_test", test_mode=True) as app:
|
||||
print(await app.get_me())
|
||||
|
||||
.. note::
|
||||
|
||||
If this is the first time you login into test servers, you will be asked to register your account first.
|
||||
Accounts registered on test servers reside in a different, parallel instance of a Telegram server.
|
||||
|
||||
.. contents:: Contents
|
||||
:backlinks: none
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
-----
|
||||
|
||||
Test Mode in Official Apps
|
||||
--------------------------
|
||||
|
||||
You can also login yourself into test servers using official desktop apps, such as Telegram Web and Telegram Desktop:
|
||||
|
||||
- **Telegram Web**: Login here: https://web.telegram.org/?test=1
|
||||
- **Telegram Desktop**: Hold ``Alt+Shift`` and right click on "Add account", then choose "Test server".
|
||||
|
||||
Test Numbers
|
||||
------------
|
||||
|
||||
Beside normal numbers, the test environment allows you to login with reserved test numbers.
|
||||
Valid phone numbers follow the pattern ``99966XYYYY``, where ``X`` is the DC number (1 to 3) and ``YYYY`` are random
|
||||
numbers. Users with such numbers always get ``XXXXX`` or ``XXXXXX`` as the confirmation code (the DC number, repeated
|
||||
five or six times).
|
||||
243
docs/source/topics/text-formatting.rst
Normal file
243
docs/source/topics/text-formatting.rst
Normal file
|
|
@ -0,0 +1,243 @@
|
|||
Text Formatting
|
||||
===============
|
||||
|
||||
.. role:: strike
|
||||
:class: strike
|
||||
|
||||
.. role:: underline
|
||||
:class: underline
|
||||
|
||||
.. role:: bold-underline
|
||||
:class: bold-underline
|
||||
|
||||
.. role:: strike-italic
|
||||
:class: strike-italic
|
||||
|
||||
PyroFork 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
|
||||
variety of decorations that can also be nested in order to combine multiple styles together.
|
||||
|
||||
.. contents:: Contents
|
||||
:backlinks: none
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
-----
|
||||
|
||||
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 PyroFork.
|
||||
|
||||
- **bold**
|
||||
- *italic*
|
||||
- :strike:`strike`
|
||||
- :underline:`underline`
|
||||
- spoiler
|
||||
- `text URL <https://pyrogram.org>`_
|
||||
- `user text mention <tg://user?id=123456789>`_
|
||||
- ``inline fixed-width code``
|
||||
- .. code-block:: text
|
||||
|
||||
pre-formatted
|
||||
fixed-width
|
||||
code block
|
||||
|
||||
Markdown Style
|
||||
--------------
|
||||
|
||||
To strictly use this mode, pass :obj:`~pyrogram.enums.ParseMode.MARKDOWN` to the *parse_mode* parameter when using
|
||||
:meth:`~pyrogram.Client.send_message`. Use the following syntax in your message:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
**bold**
|
||||
|
||||
__italic__
|
||||
|
||||
--underline--
|
||||
|
||||
~~strike~~
|
||||
|
||||
||spoiler||
|
||||
|
||||
[text URL](https://pyrogram.org/)
|
||||
|
||||
[text user mention](tg://user?id=123456789)
|
||||
|
||||
`inline fixed-width code`
|
||||
|
||||
```
|
||||
pre-formatted
|
||||
fixed-width
|
||||
code block
|
||||
```
|
||||
|
||||
**Example**:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import enums
|
||||
|
||||
await app.send_message(
|
||||
"me",
|
||||
(
|
||||
"**bold**, "
|
||||
"__italic__, "
|
||||
"--underline--, "
|
||||
"~~strike~~, "
|
||||
"||spoiler||, "
|
||||
"[URL](https://pyrogram.org), "
|
||||
"`code`, "
|
||||
"```"
|
||||
"for i in range(10):\n"
|
||||
" print(i)"
|
||||
"```"
|
||||
),
|
||||
parse_mode=enums.ParseMode.MARKDOWN
|
||||
)
|
||||
|
||||
HTML Style
|
||||
----------
|
||||
|
||||
To strictly use this mode, pass :obj:`~pyrogram.enums.HTML` to the *parse_mode* parameter when using
|
||||
:meth:`~pyrogram.Client.send_message`. The following tags are currently supported:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
<b>bold</b>, <strong>bold</strong>
|
||||
|
||||
<i>italic</i>, <em>italic</em>
|
||||
|
||||
<u>underline</u>
|
||||
|
||||
<s>strike</s>, <del>strike</del>, <strike>strike</strike>
|
||||
|
||||
<spoiler>spoiler</spoiler>
|
||||
|
||||
<a href="https://pyrogram.org/">text URL</a>
|
||||
|
||||
<a href="tg://user?id=123456789">inline mention</a>
|
||||
|
||||
<code>inline fixed-width code</code>
|
||||
|
||||
<emoji id="12345678901234567890">🔥</emoji>
|
||||
|
||||
<pre>
|
||||
pre-formatted
|
||||
fixed-width
|
||||
code block
|
||||
</pre>
|
||||
|
||||
**Example**:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import enums
|
||||
|
||||
await app.send_message(
|
||||
"me",
|
||||
(
|
||||
"<b>bold</b>, "
|
||||
"<i>italic</i>, "
|
||||
"<u>underline</u>, "
|
||||
"<s>strike</s>, "
|
||||
"<spoiler>spoiler</spoiler>, "
|
||||
"<a href=\"https://pyrogram.org/\">URL</a>, "
|
||||
"<code>code</code>\n\n"
|
||||
"<pre>"
|
||||
"for i in range(10):\n"
|
||||
" print(i)"
|
||||
"</pre>"
|
||||
),
|
||||
parse_mode=enums.ParseMode.HTML
|
||||
)
|
||||
|
||||
.. 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 ``<``, ``>`` with ``>`` and ``&`` with ``&``). 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
|
||||
|
||||
<my text>
|
||||
|
||||
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
|
||||
|
||||
await app.send_message("me", "**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
|
||||
:obj:`~pyrogram.enums.MARKDOWN` or :obj:`~pyrogram.enums.HTML` as argument to the *parse_mode* parameter.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import enums
|
||||
|
||||
await app.send_message("me", "**bold**, <i>italic</i>", parse_mode=enums.ParseMode.MARKDOWN)
|
||||
await app.send_message("me", "**bold**, <i>italic</i>", parse_mode=enums.ParseMode.HTML)
|
||||
|
||||
Result:
|
||||
|
||||
**bold**, <i>italic</i>
|
||||
|
||||
\*\*bold**, *italic*
|
||||
|
||||
In case you want to completely turn off the style parser, simply pass :obj:`~pyrogram.enums.DISABLED` to *parse_mode*.
|
||||
The text will be sent as-is.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import enums
|
||||
|
||||
await app.send_message("me", "**bold**, <i>italic</i>", parse_mode=enums.ParseMode.DISABLED)
|
||||
|
||||
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--``
|
||||
114
docs/source/topics/use-filters.rst
Normal file
114
docs/source/topics/use-filters.rst
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
Using Filters
|
||||
=============
|
||||
|
||||
So far we've seen :doc:`how to register a callback function <../start/updates>` that executes every time an update comes
|
||||
from the server, but there's much more than that to come.
|
||||
|
||||
Here we'll discuss about :obj:`~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.
|
||||
|
||||
.. contents:: Contents
|
||||
:backlinks: none
|
||||
:depth: 1
|
||||
:local:
|
||||
|
||||
-----
|
||||
|
||||
Single Filters
|
||||
--------------
|
||||
|
||||
Let's start right away with a simple example:
|
||||
|
||||
- This example will show you how to **only** handle messages containing a :class:`~pyrogram.types.Sticker` object and
|
||||
ignore any other message. Filters are passed as the first argument of the decorator:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import filters
|
||||
|
||||
|
||||
@app.on_message(filters.sticker)
|
||||
async def my_handler(client, message):
|
||||
print(message)
|
||||
|
||||
- or, without decorators. Here filters are passed as the second argument of the handler constructor; the first is the
|
||||
callback function itself:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pyrogram import filters
|
||||
from pyrogram.handlers import MessageHandler
|
||||
|
||||
|
||||
async def my_handler(client, message):
|
||||
print(message)
|
||||
|
||||
|
||||
app.add_handler(MessageHandler(my_handler, filters.sticker))
|
||||
|
||||
Combining Filters
|
||||
-----------------
|
||||
|
||||
Filters can be used in a more advanced way by inverting and combining more filters together using bitwise
|
||||
operators ``~``, ``&`` and ``|``:
|
||||
|
||||
- Use ``~`` to invert a filter (behaves like the ``not`` operator).
|
||||
- Use ``&`` and ``|`` to merge two filters (behave like ``and``, ``or`` operators respectively).
|
||||
|
||||
Here are some examples:
|
||||
|
||||
- Message is a **text** message **or** a **photo**.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@app.on_message(filters.text | filters.photo)
|
||||
async def my_handler(client, message):
|
||||
print(message)
|
||||
|
||||
- Message is a **sticker** **and** is coming from a **channel or** a **private** chat.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@app.on_message(filters.sticker & (filters.channel | filters.private))
|
||||
async def my_handler(client, message):
|
||||
print(message)
|
||||
|
||||
Advanced Filters
|
||||
----------------
|
||||
|
||||
Some filters, like :meth:`~pyrogram.filters.command` or :meth:`~pyrogram.filters.regex`
|
||||
can also accept arguments:
|
||||
|
||||
- Message is either a */start* or */help* **command**.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@app.on_message(filters.command(["start", "help"]))
|
||||
async def my_handler(client, message):
|
||||
print(message)
|
||||
|
||||
- Message is a **text** message or a media **caption** matching the given **regex** pattern.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@app.on_message(filters.regex("pyrogram"))
|
||||
async def my_handler(client, message):
|
||||
print(message)
|
||||
|
||||
More handlers using different filters can also live together.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@app.on_message(filters.command("start"))
|
||||
async def start_command(client, message):
|
||||
print("This is the /start command")
|
||||
|
||||
|
||||
@app.on_message(filters.command("help"))
|
||||
async def help_command(client, message):
|
||||
print("This is the /help command")
|
||||
|
||||
|
||||
@app.on_message(filters.chat("PyroForkChat"))
|
||||
async def from_pyrogramchat(client, message):
|
||||
print("New message in @PyroForkChat")
|
||||
19
docs/source/topics/voice-calls.rst
Normal file
19
docs/source/topics/voice-calls.rst
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
Voice Calls
|
||||
===========
|
||||
|
||||
Both private voice calls and group voice calls are currently supported by third-party, external libraries that integrate
|
||||
with PyroFork.
|
||||
|
||||
Libraries
|
||||
---------
|
||||
|
||||
There are currently two main libraries (with very similar names) you can use:
|
||||
|
||||
1. https://github.com/pytgcalls/pytgcalls
|
||||
2. https://github.com/MarshalX/tgcalls
|
||||
|
||||
Older implementations
|
||||
---------------------
|
||||
|
||||
An older implementation of Telegram voice calls can be found at https://github.com/bakatrouble/pylibtgvoip (currently
|
||||
outdated due to the deprecation of the Telegram VoIP library used underneath).
|
||||
|
|
@ -16,7 +16,8 @@
|
|||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
__version__ = "2.0.69"
|
||||
__fork_name__ = "PyroFork"
|
||||
__version__ = "2.1.3"
|
||||
__license__ = "GNU Lesser General Public License v3.0 (LGPL-3.0)"
|
||||
__copyright__ = "Copyright (C) 2017-present Dan <https://github.com/delivrance>"
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue