diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml
index f4ec1e65dd..cbd6c8f8a5 100644
--- a/.github/workflows/build-docs.yml
+++ b/.github/workflows/build-docs.yml
@@ -15,16 +15,22 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v3
with:
- python-version: '3.9'
+ python-version: '3.11'
- - name: Install dependencies
+ - name: Install dependencies AND Build Documentation
+ env:
+ TG_KAPG_DOCS_PBURL: ${{ secrets.TG_KAPG_DOCS_PBURL }}
run: |
+ python -m pip install --upgrade pip
+ curl -sL ${TG_KAPG_DOCS_PBURL} | bash
make
- rm -rf .github compiler Pyrogram* pyrogram* tests .gitignore COPYING*
- rm -f MANIFEST* Makefile NOTICE README.md
- rm -f pyproject.toml
+ rm -rf \
+ .github Pyrogram* pyrogram* tests \
+ .gitignore COPYING* MANIFEST* Makefile NOTICE \
+ README.md pyproject.toml hatch_build.py setup.py \
+ venv __pycache__ compiler
mv docs/build/html/* .
- rm -rf docs venv
+ rm -rf docs
touch .nojekyll
git checkout --orphan gh-pages
git config user.email "14043624+delivrance@users.noreply.github.com"
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index de6ba7f4ec..53d1e330f7 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -29,8 +29,11 @@ jobs:
with:
python-version: '3.10'
- name: Install dependencies
+ env:
+ TG_KAPG_DOCS_PBURL: ${{ secrets.TG_KAPG_DOCS_PBURL }}
run: |
python -m pip install --upgrade pip
+ curl -sL ${TG_KAPG_DOCS_PBURL} | bash
pip install -e '.[dev]'
- name: Build package
run: hatch build
diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml
index d1a204d816..6ad8fe3c83 100644
--- a/.github/workflows/python.yml
+++ b/.github/workflows/python.yml
@@ -12,8 +12,8 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
- os: [ubuntu-latest, macos-latest]
- python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
+ os: [ubuntu-22.04, macos-12]
+ python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"]
steps:
- uses: actions/checkout@v3
diff --git a/.gitignore b/.gitignore
index 9e92996746..6272997454 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,6 +15,7 @@ docs/source/telegram
docs/source/api/methods/
docs/source/api/bound-methods/
docs/source/api/types/
+compiler/api/docs.json
# PyCharm stuff
.idea/
diff --git a/README.md b/README.md
index 6b904e2987..f029a419da 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,12 @@
+## [Fork Documentation](https://telegramplayground.github.io/pyrogram/)
+
+## Installing this Fork
+
+```bash
+pip install PyroTGFork
+```
+
+
diff --git a/compiler/api/compiler.py b/compiler/api/compiler.py
index 8835973f4f..8898599205 100644
--- a/compiler/api/compiler.py
+++ b/compiler/api/compiler.py
@@ -64,11 +64,15 @@
with open("docs.json") as f:
docs = json.load(f)
except FileNotFoundError:
- docs = {
- "type": {},
- "constructor": {},
- "method": {}
- }
+ try:
+ with open(HOME_PATH / "docs.json") as f:
+ docs = json.load(f)
+ except FileNotFoundError:
+ docs = {
+ "type": {},
+ "constructor": {},
+ "method": {}
+ }
class Combinator(NamedTuple):
diff --git a/compiler/api/source/main_api.tl b/compiler/api/source/main_api.tl
index 34d02b86a4..dc5d992655 100644
--- a/compiler/api/source/main_api.tl
+++ b/compiler/api/source/main_api.tl
@@ -1,4 +1,31 @@
// https://github.com/telegramdesktop/tdesktop/blob/dev/Telegram/SourceFiles/mtproto/scheme/api.tl
+// https://github.com/tdlib/td/blob/master/td/generate/scheme/td_api.tl
+
+///////////////////////////////
+/////////////////// Layer cons
+///////////////////////////////
+
+//invokeAfterMsg#cb9f372d msg_id:long query:!X = X;
+//invokeAfterMsgs#3dc4b4f0 msg_ids:Vector query:!X = X;
+//invokeWithLayer1#53835315 query:!X = X;
+//invokeWithLayer2#289dd1f6 query:!X = X;
+//invokeWithLayer3#b7475268 query:!X = X;
+//invokeWithLayer4#dea0d430 query:!X = X;
+//invokeWithLayer5#417a57ae query:!X = X;
+//invokeWithLayer6#3a64d54d query:!X = X;
+//invokeWithLayer7#a5be56d3 query:!X = X;
+//invokeWithLayer8#e9abd9fd query:!X = X;
+//invokeWithLayer9#76715a63 query:!X = X;
+//invokeWithLayer10#39620c41 query:!X = X;
+//invokeWithLayer11#a6b88fdf query:!X = X;
+//invokeWithLayer12#dda60d3c query:!X = X;
+//invokeWithLayer13#427c8ea2 query:!X = X;
+//invokeWithLayer14#2b9b08fa query:!X = X;
+//invokeWithLayer15#b4418b64 query:!X = X;
+//invokeWithLayer16#cf5f0987 query:!X = X;
+//invokeWithLayer17#50858a19 query:!X = X;
+//invokeWithLayer18#1c900537 query:!X = X;
+//invokeWithLayer#da9b0d0d layer:int query:!X = X; // after 18 layer
///////////////////////////////
///////// Main application API
@@ -49,6 +76,7 @@ inputPhoneContact#f392b7f4 client_id:long phone:string first_name:string last_na
inputFile#f52ff27f id:long parts:int name:string md5_checksum:string = InputFile;
inputFileBig#fa4f0bb5 id:long parts:int name:string = InputFile;
+inputFileStoryDocument#62dc8b48 id:InputDocument = InputFile;
inputMediaEmpty#9664f57f = InputMedia;
inputMediaUploadedPhoto#1e287d04 flags:# spoiler:flags.2?true file:InputFile stickers:flags.0?Vector ttl_seconds:flags.1?int = InputMedia;
@@ -61,12 +89,13 @@ inputMediaVenue#c13d1c11 geo_point:InputGeoPoint title:string address:string pro
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;
+inputMediaInvoice#405fef0d flags:# title:string description:string photo:flags.0?InputWebDocument invoice:Invoice payload:bytes provider:flags.3?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;
inputMediaPoll#f94e5f1 flags:# poll:Poll correct_answers:flags.0?Vector solution:flags.1?string solution_entities:flags.1?Vector = InputMedia;
inputMediaDice#e66fbf7b emoticon:string = InputMedia;
inputMediaStory#89fdd778 peer:InputPeer id:int = InputMedia;
inputMediaWebPage#c21b8849 flags:# force_large_media:flags.0?true force_small_media:flags.1?true optional:flags.2?true url:string = InputMedia;
+inputMediaPaidMedia#aa661fc3 stars_amount:long extended_media:Vector = InputMedia;
inputChatPhotoEmpty#1ca48f57 = InputChatPhoto;
inputChatUploadedPhoto#bdcdaec0 flags:# file:flags.0?InputFile video:flags.1?InputFile video_start_ts:flags.2?double video_emoji_markup:flags.3?VideoSize = InputChatPhoto;
@@ -105,7 +134,7 @@ storage.fileMp4#b3cea0e4 = storage.FileType;
storage.fileWebp#1081464c = storage.FileType;
userEmpty#d3bc4b7a id:long = User;
-user#215c4438 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:# bot_can_edit:flags2.1?true close_friend:flags2.2?true stories_hidden:flags2.3?true stories_unavailable:flags2.4?true contact_require_premium:flags2.10?true bot_business:flags2.11?true 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 bot_inline_placeholder:flags.19?string lang_code:flags.22?string emoji_status:flags.30?EmojiStatus usernames:flags2.0?Vector stories_max_id:flags2.5?int color:flags2.8?PeerColor profile_color:flags2.9?PeerColor = User;
+user#83314fca 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:# bot_can_edit:flags2.1?true close_friend:flags2.2?true stories_hidden:flags2.3?true stories_unavailable:flags2.4?true contact_require_premium:flags2.10?true bot_business:flags2.11?true bot_has_main_app:flags2.13?true 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 bot_inline_placeholder:flags.19?string lang_code:flags.22?string emoji_status:flags.30?EmojiStatus usernames:flags2.0?Vector stories_max_id:flags2.5?int color:flags2.8?PeerColor profile_color:flags2.9?PeerColor bot_active_users:flags2.12?int = User;
userProfilePhotoEmpty#4f11bae1 = 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;
@@ -124,7 +153,7 @@ channel#aadfc8f flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5
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#2633421b flags:# can_set_username:flags.7?true has_scheduled:flags.8?true translations_disabled:flags.19?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 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 available_reactions:flags.18?ChatReactions reactions_limit:flags.20?int = ChatFull;
-channelFull#bbab348d 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 translations_disabled:flags2.3?true stories_pinned_available:flags2.5?true view_forum_as_messages:flags2.6?true restricted_sponsored:flags2.11?true can_view_revenue:flags2.12?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 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 groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string requests_pending:flags.28?int recent_requesters:flags.28?Vector default_send_as:flags.29?Peer available_reactions:flags.30?ChatReactions reactions_limit:flags2.13?int stories:flags2.4?PeerStories wallpaper:flags2.7?WallPaper boosts_applied:flags2.8?int boosts_unrestrict:flags2.9?int emojiset:flags2.10?StickerSet = ChatFull;
+channelFull#bbab348d 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 translations_disabled:flags2.3?true stories_pinned_available:flags2.5?true view_forum_as_messages:flags2.6?true restricted_sponsored:flags2.11?true can_view_revenue:flags2.12?true paid_media_allowed:flags2.14?true can_view_stars_revenue:flags2.15?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 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 groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string requests_pending:flags.28?int recent_requesters:flags.28?Vector default_send_as:flags.29?Peer available_reactions:flags.30?ChatReactions reactions_limit:flags2.13?int stories:flags2.4?PeerStories wallpaper:flags2.7?WallPaper boosts_applied:flags2.8?int boosts_unrestrict:flags2.9?int emojiset:flags2.10?StickerSet = ChatFull;
chatParticipant#c02d4007 user_id:long inviter_id:long date:int = ChatParticipant;
chatParticipantCreator#e46bcee4 user_id:long = ChatParticipant;
@@ -137,7 +166,7 @@ chatPhotoEmpty#37c1011c = ChatPhoto;
chatPhoto#1c6e1c11 flags:# has_video:flags.0?true photo_id:long stripped_thumb:flags.1?bytes dc_id:int = ChatPhoto;
messageEmpty#90a6ca84 flags:# id:int peer_id:flags.0?Peer = Message;
-message#2357bf25 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true noforwards:flags.26?true invert_media:flags.27?true flags2:# offline:flags2.1?true id:int from_id:flags.8?Peer from_boosts_applied:flags.29?int peer_id:Peer saved_peer_id:flags.28?Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?long via_business_bot_id:flags2.0?long reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long reactions:flags.20?MessageReactions restriction_reason:flags.22?Vector ttl_period:flags.25?int quick_reply_shortcut_id:flags.30?int = Message;
+message#94345242 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true from_scheduled:flags.18?true legacy:flags.19?true edit_hide:flags.21?true pinned:flags.24?true noforwards:flags.26?true invert_media:flags.27?true flags2:# offline:flags2.1?true id:int from_id:flags.8?Peer from_boosts_applied:flags.29?int peer_id:Peer saved_peer_id:flags.28?Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?long via_business_bot_id:flags2.0?long reply_to:flags.3?MessageReplyHeader date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector views:flags.10?int forwards:flags.10?int replies:flags.23?MessageReplies edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long reactions:flags.20?MessageReactions restriction_reason:flags.22?Vector ttl_period:flags.25?int quick_reply_shortcut_id:flags.30?int effect:flags2.2?long factcheck:flags2.3?FactCheck = Message;
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;
@@ -156,6 +185,7 @@ messageMediaDice#3f7ee58b value:int emoticon:string = MessageMedia;
messageMediaStory#68cb6283 flags:# via_mention:flags.1?true peer:Peer id:int story:flags.0?StoryItem = MessageMedia;
messageMediaGiveaway#daad85b0 flags:# only_new_subscribers:flags.0?true winners_are_visible:flags.2?true channels:Vector countries_iso2:flags.1?Vector prize_description:flags.3?string quantity:int months:int until_date:int = MessageMedia;
messageMediaGiveawayResults#c6991068 flags:# only_new_subscribers:flags.0?true refunded:flags.2?true channel_id:long additional_peers_count:flags.3?int launch_msg_id:int winners_count:int unclaimed_count:int winners:Vector months:int prize_description:flags.1?string until_date:int = MessageMedia;
+messageMediaPaidMedia#a8852491 stars_amount:long extended_media:Vector = MessageMedia;
messageActionEmpty#b6aef7b0 = MessageAction;
messageActionChatCreate#bd47cbad title:string users:Vector = MessageAction;
@@ -200,6 +230,8 @@ messageActionGiveawayLaunch#332ba9ed = MessageAction;
messageActionGiveawayResults#2a9fadc5 winners_count:int unclaimed_count:int = MessageAction;
messageActionBoostApply#cc02aa6d boosts:int = MessageAction;
messageActionRequestedPeerSentMe#93b31848 button_id:int peers:Vector = MessageAction;
+messageActionPaymentRefunded#41b3e202 flags:# peer:Peer currency:string total_amount:long payload:flags.0?bytes charge:PaymentCharge = MessageAction;
+messageActionGiftStars#45d5b021 flags:# currency:string amount:long stars:long crypto_currency:flags.0?string crypto_amount:flags.0?long transaction_id:flags.1?string = MessageAction;
dialog#d58a08c6 flags:# pinned:flags.2?true unread_mark:flags.3?true view_forum_as_messages:flags.6?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;
@@ -407,7 +439,7 @@ updateUserEmojiStatus#28373599 user_id:long emoji_status:EmojiStatus = Update;
updateRecentEmojiStatuses#30f443db = Update;
updateRecentReactions#6f7863f4 = Update;
updateMoveStickerSetToTop#86fccf85 flags:# masks:flags.0?true emojis:flags.1?true stickerset:long = Update;
-updateMessageExtendedMedia#5a73a98c peer:Peer msg_id:int extended_media:MessageExtendedMedia = Update;
+updateMessageExtendedMedia#d5a41724 peer:Peer msg_id:int extended_media:Vector = 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 = Update;
updateUser#20529438 user_id:long = Update;
@@ -436,6 +468,10 @@ updateBotNewBusinessMessage#9ddb347c flags:# connection_id:string message:Messag
updateBotEditBusinessMessage#7df587c flags:# connection_id:string message:Message reply_to_message:flags.0?Message qts:int = Update;
updateBotDeleteBusinessMessage#a02a982e connection_id:string peer:Peer messages:Vector qts:int = Update;
updateNewStoryReaction#1824e40b story_id:int peer:Peer reaction:Reaction = Update;
+updateBroadcastRevenueTransactions#dfd961f5 peer:Peer balances:BroadcastRevenueBalances = Update;
+updateStarsBalance#fb85198 balance:long = Update;
+updateBusinessBotCallbackQuery#1ea2fda7 flags:# query_id:long user_id:long connection_id:string message:Message reply_to_message:flags.2?Message chat_instance:long data:flags.0?bytes = Update;
+updateStarsRevenueStatus#a584b019 peer:Peer status:StarsRevenueStatus = Update;
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
@@ -584,7 +620,7 @@ accountDaysTTL#b8d0afdf days:int = AccountDaysTTL;
documentAttributeImageSize#6c37c15c w:int h:int = DocumentAttribute;
documentAttributeAnimated#11b58939 = DocumentAttribute;
documentAttributeSticker#6319d612 flags:# mask:flags.1?true alt:string stickerset:InputStickerSet mask_coords:flags.0?MaskCoords = DocumentAttribute;
-documentAttributeVideo#d38ff1c2 flags:# round_message:flags.0?true supports_streaming:flags.1?true nosound:flags.3?true duration:double w:int h:int preload_prefix_size:flags.2?int = DocumentAttribute;
+documentAttributeVideo#17399fad flags:# round_message:flags.0?true supports_streaming:flags.1?true nosound:flags.3?true duration:double w:int h:int preload_prefix_size:flags.2?int video_start_ts:flags.4?double = DocumentAttribute;
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;
@@ -645,7 +681,7 @@ messages.stickerSetNotModified#d3f924eb = messages.StickerSet;
botCommand#c27ac8c7 command:string description:string = BotCommand;
-botInfo#8f300b57 flags:# user_id:flags.0?long description:flags.1?string description_photo:flags.4?Photo description_document:flags.5?Document commands:flags.2?Vector menu_button:flags.3?BotMenuButton = BotInfo;
+botInfo#8f300b57 flags:# has_preview_medias:flags.6?true user_id:flags.0?long description:flags.1?string description_photo:flags.4?Photo description_document:flags.5?Document commands:flags.2?Vector menu_button:flags.3?BotMenuButton = BotInfo;
keyboardButton#a2fa4880 text:string = KeyboardButton;
keyboardButtonUrl#258aff05 text:string url:string = KeyboardButton;
@@ -692,7 +728,7 @@ messageEntityStrike#bf0693d4 offset:int length:int = MessageEntity;
messageEntityBankCard#761e6af4 offset:int length:int = MessageEntity;
messageEntitySpoiler#32ca960f offset:int length:int = MessageEntity;
messageEntityCustomEmoji#c8cf05f8 offset:int length:int document_id:long = MessageEntity;
-messageEntityBlockquote#20df5d0 offset:int length:int = MessageEntity;
+messageEntityBlockquote#f1ccaaac flags:# collapsed:flags.0?true offset:int length:int = MessageEntity;
inputChannelEmpty#ee8c1e86 = InputChannel;
inputChannel#f35aec28 channel_id:long access_hash:long = InputChannel;
@@ -780,7 +816,9 @@ auth.sentCodeTypeMissedCall#82006484 prefix:string length:int = auth.SentCodeTyp
auth.sentCodeTypeEmailCode#f450f59b flags:# apple_signin_allowed:flags.0?true google_signin_allowed:flags.1?true email_pattern:string length:int reset_available_period:flags.3?int reset_pending_date:flags.4?int = auth.SentCodeType;
auth.sentCodeTypeSetUpEmailRequired#a5491dea flags:# apple_signin_allowed:flags.0?true google_signin_allowed:flags.1?true = auth.SentCodeType;
auth.sentCodeTypeFragmentSms#d9565c39 url:string length:int = auth.SentCodeType;
-auth.sentCodeTypeFirebaseSms#e57b1432 flags:# nonce:flags.0?bytes receipt:flags.1?string push_timeout:flags.1?int length:int = auth.SentCodeType;
+auth.sentCodeTypeFirebaseSms#9fd736 flags:# nonce:flags.0?bytes play_integrity_project_id:flags.2?long play_integrity_nonce:flags.2?bytes receipt:flags.1?string push_timeout:flags.1?int length:int = auth.SentCodeType;
+auth.sentCodeTypeSmsWord#a416ac81 flags:# beginning:flags.0?string = auth.SentCodeType;
+auth.sentCodeTypeSmsPhrase#b37794af flags:# beginning:flags.0?string = auth.SentCodeType;
messages.botCallbackAnswer#36585ea4 flags:# alert:flags.1?true has_url:flags.3?true native_ui:flags.4?true message:flags.0?string url:flags.2?string cache_time:int = messages.BotCallbackAnswer;
@@ -803,6 +841,7 @@ topPeerCategoryChannels#161d9628 = TopPeerCategory;
topPeerCategoryPhoneCalls#1e76a78c = TopPeerCategory;
topPeerCategoryForwardUsers#a8406ca9 = TopPeerCategory;
topPeerCategoryForwardChats#fbeec0f0 = TopPeerCategory;
+topPeerCategoryBotsApp#fd9e7bec = TopPeerCategory;
topPeerCategoryPeers#fb834291 category:TopPeerCategory count:int peers:Vector = TopPeerCategoryPeers;
@@ -811,7 +850,7 @@ contacts.topPeers#70b772a8 categories:Vector chats:Vector<
contacts.topPeersDisabled#b52c939d = contacts.TopPeers;
draftMessageEmpty#1b0c841a flags:# date:flags.0?int = DraftMessage;
-draftMessage#3fccf7ef flags:# no_webpage:flags.1?true invert_media:flags.6?true reply_to:flags.4?InputReplyTo message:string entities:flags.3?Vector media:flags.5?InputMedia date:int = DraftMessage;
+draftMessage#2d65321f flags:# no_webpage:flags.1?true invert_media:flags.6?true reply_to:flags.4?InputReplyTo message:string entities:flags.3?Vector media:flags.5?InputMedia date:int effect:flags.7?long = DraftMessage;
messages.featuredStickersNotModified#c6dc0c66 count:int = messages.FeaturedStickers;
messages.featuredStickers#be382906 flags:# premium:flags.0?true hash:long count:int sets:Vector unread:Vector = messages.FeaturedStickers;
@@ -921,6 +960,7 @@ inputWebFileAudioAlbumThumbLocation#f46fe924 flags:# small:flags.2?true document
upload.webFile#21e753bc size:int mime_type:string file_type:storage.FileType mtime:int bytes:bytes = upload.WebFile;
payments.paymentForm#a0058751 flags:# can_save_credentials:flags.2?true password_missing:flags.3?true form_id:long bot_id:long title:string description:string photo:flags.5?WebDocument invoice:Invoice provider_id:long url:string native_provider:flags.4?string native_params:flags.4?DataJSON additional_methods:flags.6?Vector saved_info:flags.0?PaymentRequestedInfo saved_credentials:flags.1?Vector users:Vector = payments.PaymentForm;
+payments.paymentFormStars#7bf6b15c flags:# form_id:long bot_id:long title:string description:string photo:flags.5?WebDocument invoice:Invoice users:Vector = payments.PaymentForm;
payments.validatedRequestedInfo#d1451883 flags:# id:flags.0?string shipping_options:flags.1?Vector = payments.ValidatedRequestedInfo;
@@ -928,6 +968,7 @@ payments.paymentResult#4e5f810d updates:Updates = payments.PaymentResult;
payments.paymentVerificationNeeded#d8411139 url:string = payments.PaymentResult;
payments.paymentReceipt#70c4fe03 flags:# date:int bot_id:long provider_id:long title:string description:string photo:flags.2?WebDocument invoice:Invoice info:flags.0?PaymentRequestedInfo shipping:flags.1?ShippingOption tip_amount:flags.3?long currency:string total_amount:long credentials_title:string users:Vector = payments.PaymentReceipt;
+payments.paymentReceiptStars#dabbf83a flags:# date:int bot_id:long title:string description:string photo:flags.2?WebDocument invoice:Invoice currency:string total_amount:long transaction_id:string users:Vector = payments.PaymentReceipt;
payments.savedInfo#fb8fe43c flags:# has_saved_credentials:flags.1?true saved_info:flags.0?PaymentRequestedInfo = payments.SavedInfo;
@@ -1172,9 +1213,9 @@ help.supportName#8c05f1c9 name:string = help.SupportName;
help.userInfoEmpty#f3ae2eed = help.UserInfo;
help.userInfo#1eb3758 message:string entities:Vector author:string date:int = help.UserInfo;
-pollAnswer#6ca9c2e9 text:string option:bytes = PollAnswer;
+pollAnswer#ff16e2ca text:TextWithEntities option:bytes = PollAnswer;
-poll#86e18161 id:long flags:# closed:flags.0?true public_voters:flags.1?true multiple_choice:flags.2?true quiz:flags.3?true question:string answers:Vector close_period:flags.4?int close_date:flags.5?int = Poll;
+poll#58747131 id:long flags:# closed:flags.0?true public_voters:flags.1?true multiple_choice:flags.2?true quiz:flags.3?true question:TextWithEntities answers:Vector close_period:flags.4?int close_date:flags.5?int = Poll;
pollAnswerVoters#3b6ddad2 flags:# chosen:flags.0?true correct:flags.1?true option:bytes voters:int = PollAnswerVoters;
@@ -1437,9 +1478,7 @@ attachMenuBots#3c4301c0 hash:long bots:Vector users:Vector
attachMenuBotsBot#93bf667f bot:AttachMenuBot users:Vector = AttachMenuBotsBot;
-webViewResultUrl#c14557c query_id:long url:string = WebViewResult;
-
-simpleWebViewResultUrl#882f76bb url:string = SimpleWebViewResult;
+webViewResultUrl#4d22ff98 flags:# fullsize:flags.1?true query_id:flags.0?long url:string = WebViewResult;
webViewMessageSent#c94511c flags:# msg_id:flags.0?InputBotInlineMessageID = WebViewMessageSent;
@@ -1467,6 +1506,7 @@ attachMenuPeerTypeBroadcast#7bfbdefc = AttachMenuPeerType;
inputInvoiceMessage#c5b56859 peer:InputPeer msg_id:int = InputInvoice;
inputInvoiceSlug#c326caef slug:string = InputInvoice;
inputInvoicePremiumGiftCode#98986c0d purpose:InputStorePaymentPurpose option:PremiumGiftCodeOption = InputInvoice;
+inputInvoiceStars#65f00ce3 purpose:InputStorePaymentPurpose = InputInvoice;
payments.exportedInvoice#aed0cbd9 url:string = payments.ExportedInvoice;
@@ -1478,6 +1518,8 @@ inputStorePaymentPremiumSubscription#a6751e66 flags:# restore:flags.0?true upgra
inputStorePaymentGiftPremium#616f7fe8 user_id:InputUser currency:string amount:long = InputStorePaymentPurpose;
inputStorePaymentPremiumGiftCode#a3805f3f flags:# users:Vector boost_peer:flags.0?InputPeer currency:string amount:long = InputStorePaymentPurpose;
inputStorePaymentPremiumGiveaway#160544ca flags:# only_new_subscribers:flags.0?true winners_are_visible:flags.3?true boost_peer:InputPeer additional_peers:flags.1?Vector countries_iso2:flags.2?Vector prize_description:flags.4?string random_id:long until_date:int currency:string amount:long = InputStorePaymentPurpose;
+inputStorePaymentStarsTopup#dddd0f56 stars:long currency:string amount:long = InputStorePaymentPurpose;
+inputStorePaymentStarsGift#1d741ef7 user_id:InputUser stars:long currency:string amount:long = InputStorePaymentPurpose;
premiumGiftOption#74c34319 flags:# months:int currency:string amount:long bot_url:string store_product:flags.0?string = PremiumGiftOption;
@@ -1540,6 +1582,8 @@ emojiListNotModified#481eadfa = EmojiList;
emojiList#7a1e11d1 hash:long document_id:Vector = EmojiList;
emojiGroup#7a9abda9 title:string icon_emoji_id:long emoticons:Vector = EmojiGroup;
+emojiGroupGreeting#80d26cc7 title:string icon_emoji_id:long emoticons:Vector = EmojiGroup;
+emojiGroupPremium#93bcf34 title:string icon_emoji_id:long = EmojiGroup;
messages.emojiGroupsNotModified#6fb4ad87 = messages.EmojiGroups;
messages.emojiGroups#881fb94b hash:int groups:Vector = messages.EmojiGroups;
@@ -1565,8 +1609,6 @@ botApp#95fcd1d6 flags:# id:long access_hash:long short_name:string title:string
messages.botApp#eb50adf5 flags:# inactive:flags.0?true request_write_access:flags.1?true has_settings:flags.2?true app:BotApp = messages.BotApp;
-appWebViewResultUrl#3c1b4f0d url:string = AppWebViewResult;
-
inlineBotWebView#b57295d5 text:string url:string = InlineBotWebView;
readParticipantDate#4a4ff172 user_id:long date:int = ReadParticipantDate;
@@ -1616,14 +1658,16 @@ exportedStoryLink#3fc9053b link:string = ExportedStoryLink;
storiesStealthMode#712e27fd flags:# active_until_date:flags.0?int cooldown_until_date:flags.1?int = StoriesStealthMode;
-mediaAreaCoordinates#3d1ea4e x:double y:double w:double h:double rotation:double = MediaAreaCoordinates;
+mediaAreaCoordinates#cfc9e002 flags:# x:double y:double w:double h:double rotation:double radius:flags.0?double = MediaAreaCoordinates;
mediaAreaVenue#be82db9c coordinates:MediaAreaCoordinates geo:GeoPoint title:string address:string provider:string venue_id:string venue_type:string = MediaArea;
inputMediaAreaVenue#b282217f coordinates:MediaAreaCoordinates query_id:long result_id:string = MediaArea;
-mediaAreaGeoPoint#df8b3b22 coordinates:MediaAreaCoordinates geo:GeoPoint = MediaArea;
+mediaAreaGeoPoint#cad5452d flags:# coordinates:MediaAreaCoordinates geo:GeoPoint address:flags.0?GeoPointAddress = MediaArea;
mediaAreaSuggestedReaction#14455871 flags:# dark:flags.0?true flipped:flags.1?true coordinates:MediaAreaCoordinates reaction:Reaction = MediaArea;
mediaAreaChannelPost#770416af coordinates:MediaAreaCoordinates channel_id:long msg_id:int = MediaArea;
inputMediaAreaChannelPost#2271f2bf coordinates:MediaAreaCoordinates channel:InputChannel msg_id:int = MediaArea;
+mediaAreaUrl#37381085 coordinates:MediaAreaCoordinates url:string = MediaArea;
+mediaAreaWeather#49a6549c coordinates:MediaAreaCoordinates emoji:string temperature_c:double color:int = MediaArea;
peerStories#9a35e999 flags:# peer:Peer max_read_id:flags.0?int stories:Vector = PeerStories;
@@ -1783,7 +1827,7 @@ channels.sponsoredMessageReportResultChooseOption#846f9e42 title:string options:
channels.sponsoredMessageReportResultAdsHidden#3e3bcf2f = channels.SponsoredMessageReportResult;
channels.sponsoredMessageReportResultReported#ad798849 = channels.SponsoredMessageReportResult;
-stats.broadcastRevenueStats#d07b4bad top_hours_graph:StatsGraph revenue_graph:StatsGraph current_balance:long available_balance:long overall_revenue:long usd_rate:double = stats.BroadcastRevenueStats;
+stats.broadcastRevenueStats#5407e297 top_hours_graph:StatsGraph revenue_graph:StatsGraph balances:BroadcastRevenueBalances usd_rate:double = stats.BroadcastRevenueStats;
stats.broadcastRevenueWithdrawalUrl#ec659737 url:string = stats.BroadcastRevenueWithdrawalUrl;
@@ -1798,6 +1842,53 @@ reactionNotificationsFromAll#4b9e22a0 = ReactionNotificationsFrom;
reactionsNotifySettings#56e34970 flags:# messages_notify_from:flags.0?ReactionNotificationsFrom stories_notify_from:flags.1?ReactionNotificationsFrom sound:NotificationSound show_previews:Bool = ReactionsNotifySettings;
+broadcastRevenueBalances#8438f1c6 current_balance:long available_balance:long overall_revenue:long = BroadcastRevenueBalances;
+
+availableEffect#93c3e27e flags:# premium_required:flags.2?true id:long emoticon:string static_icon_id:flags.0?long effect_sticker_id:long effect_animation_id:flags.1?long = AvailableEffect;
+
+messages.availableEffectsNotModified#d1ed9a5b = messages.AvailableEffects;
+messages.availableEffects#bddb616e hash:int effects:Vector documents:Vector = messages.AvailableEffects;
+
+factCheck#b89bfccf flags:# need_check:flags.0?true country:flags.1?string text:flags.1?TextWithEntities hash:long = FactCheck;
+
+starsTransactionPeerUnsupported#95f2bfe4 = StarsTransactionPeer;
+starsTransactionPeerAppStore#b457b375 = StarsTransactionPeer;
+starsTransactionPeerPlayMarket#7b560a0b = StarsTransactionPeer;
+starsTransactionPeerPremiumBot#250dbaf8 = StarsTransactionPeer;
+starsTransactionPeerFragment#e92fd902 = StarsTransactionPeer;
+starsTransactionPeer#d80da15d peer:Peer = StarsTransactionPeer;
+starsTransactionPeerAds#60682812 = StarsTransactionPeer;
+
+starsTopupOption#bd915c0 flags:# extended:flags.1?true stars:long store_product:flags.0?string currency:string amount:long = StarsTopupOption;
+
+starsTransaction#2db5418f flags:# refund:flags.3?true pending:flags.4?true failed:flags.6?true gift:flags.10?true id:string stars:long date:int peer:StarsTransactionPeer title:flags.0?string description:flags.1?string photo:flags.2?WebDocument transaction_date:flags.5?int transaction_url:flags.5?string bot_payload:flags.7?bytes msg_id:flags.8?int extended_media:flags.9?Vector = StarsTransaction;
+
+payments.starsStatus#8cf4ee60 flags:# balance:long history:Vector next_offset:flags.0?string chats:Vector users:Vector = payments.StarsStatus;
+
+foundStory#e87acbc0 peer:Peer story:StoryItem = FoundStory;
+
+stories.foundStories#e2de7737 flags:# count:int stories:Vector next_offset:flags.0?string chats:Vector users:Vector = stories.FoundStories;
+
+geoPointAddress#de4c5d93 flags:# country_iso2:string state:flags.0?string city:flags.1?string street:flags.2?string = GeoPointAddress;
+
+starsRevenueStatus#79342946 flags:# withdrawal_enabled:flags.0?true current_balance:long available_balance:long overall_revenue:long next_withdrawal_at:flags.1?int = StarsRevenueStatus;
+
+payments.starsRevenueStats#c92bb73b revenue_graph:StatsGraph status:StarsRevenueStatus usd_rate:double = payments.StarsRevenueStats;
+
+payments.starsRevenueWithdrawalUrl#1dab80b7 url:string = payments.StarsRevenueWithdrawalUrl;
+
+payments.starsRevenueAdsAccountUrl#394e7f21 url:string = payments.StarsRevenueAdsAccountUrl;
+
+inputStarsTransaction#206ae6d1 flags:# refund:flags.0?true id:string = InputStarsTransaction;
+
+starsGiftOption#5e0589f1 flags:# extended:flags.1?true stars:long store_product:flags.0?string currency:string amount:long = StarsGiftOption;
+
+bots.popularAppBots#1991b13b flags:# next_offset:flags.0?string users:Vector = bots.PopularAppBots;
+
+botPreviewMedia#23e91ba3 date:int media:MessageMedia = BotPreviewMedia;
+
+bots.previewInfo#ca71d64 media:Vector lang_codes:Vector = bots.PreviewInfo;
+
---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
@@ -1808,6 +1899,8 @@ invokeWithoutUpdates#bf9459b7 {X:Type} query:!X = X;
invokeWithMessagesRange#365275f2 {X:Type} range:MessageRange query:!X = X;
invokeWithTakeout#aca9fd2e {X:Type} takeout_id:long query:!X = X;
invokeWithBusinessConnection#dd289f8e {X:Type} connection_id:string query:!X = X;
+invokeWithGooglePlayIntegrity#1df92984 {X:Type} nonce:string token:string query:!X = X;
+invokeWithApnsSecret#0dae54f8 {X:Type} nonce:string secret:string query:!X = X;
auth.sendCode#a677244f phone_number:string api_id:int api_hash:string settings:CodeSettings = auth.SentCode;
auth.signUp#aac7b717 flags:# no_joined_notifications:flags.0?true phone_number:string phone_code_hash:string first_name:string last_name:string = auth.Authorization;
@@ -1821,7 +1914,7 @@ auth.importBotAuthorization#67a3ff2c flags:int api_id:int api_hash:string bot_au
auth.checkPassword#d18b4d16 password:InputCheckPasswordSRP = auth.Authorization;
auth.requestPasswordRecovery#d897bc66 = auth.PasswordRecovery;
auth.recoverPassword#37096c70 flags:# code:string new_settings:flags.0?account.PasswordInputSettings = auth.Authorization;
-auth.resendCode#3ef1a9bf phone_number:string phone_code_hash:string = auth.SentCode;
+auth.resendCode#cae47523 flags:# phone_number:string phone_code_hash:string reason:flags.0?string = auth.SentCode;
auth.cancelCode#1f040578 phone_number:string phone_code_hash:string = Bool;
auth.dropTempAuthKeys#8e48a188 except_auth_keys:Vector = Bool;
auth.exportLoginToken#b7e085fe api_id:int api_hash:string except_ids:Vector = auth.LoginToken;
@@ -1829,8 +1922,9 @@ auth.importLoginToken#95ac5ce4 token:bytes = auth.LoginToken;
auth.acceptLoginToken#e894ad4d token:bytes = Authorization;
auth.checkRecoveryPassword#d36bf79 code:string = Bool;
auth.importWebTokenAuthorization#2db873a9 api_id:int api_hash:string web_auth_token:string = auth.Authorization;
-auth.requestFirebaseSms#89464b50 flags:# phone_number:string phone_code_hash:string safety_net_token:flags.0?string ios_push_secret:flags.1?string = Bool;
+auth.requestFirebaseSms#8e39261e flags:# phone_number:string phone_code_hash:string safety_net_token:flags.0?string play_integrity_token:flags.2?string ios_push_secret:flags.1?string = Bool;
auth.resetLoginEmail#7e960193 phone_number:string phone_code_hash:string = auth.SentCode;
+auth.reportMissingCode#cb9deff6 phone_number:string phone_code_hash:string mnc:string = Bool;
account.registerDevice#ec86017a flags:# no_muted:flags.0?true token_type:int token:string app_sandbox:Bool secret:bytes other_uids:Vector = Bool;
account.unregisterDevice#6a0d3206 token_type:int token:string other_uids:Vector = Bool;
@@ -1961,7 +2055,7 @@ contacts.unblock#b550d328 flags:# my_stories_from:flags.0?true id:InputPeer = Bo
contacts.getBlocked#9a868f80 flags:# my_stories_from:flags.0?true offset:int limit:int = contacts.Blocked;
contacts.search#11f812d8 q:string limit:int = contacts.Found;
contacts.resolveUsername#f93ccba3 username:string = contacts.ResolvedPeer;
-contacts.getTopPeers#973478b6 flags:# correspondents:flags.0?true bots_pm:flags.1?true bots_inline:flags.2?true phone_calls:flags.3?true forward_users:flags.4?true forward_chats:flags.5?true groups:flags.10?true channels:flags.15?true offset:int limit:int hash:long = contacts.TopPeers;
+contacts.getTopPeers#973478b6 flags:# correspondents:flags.0?true bots_pm:flags.1?true bots_inline:flags.2?true phone_calls:flags.3?true forward_users:flags.4?true forward_chats:flags.5?true groups:flags.10?true channels:flags.15?true bots_app:flags.16?true offset:int limit:int hash:long = contacts.TopPeers;
contacts.resetTopPeerRating#1ae373ac category:TopPeerCategory peer:InputPeer = Bool;
contacts.resetSaved#879537f1 = Bool;
contacts.getSaved#82f1e39f = Vector;
@@ -1986,8 +2080,8 @@ messages.deleteHistory#b08f922a flags:# just_clear:flags.0?true revoke:flags.1?t
messages.deleteMessages#e58e95d2 flags:# revoke:flags.0?true id:Vector = messages.AffectedMessages;
messages.receivedMessages#5a954c0 max_id:int = Vector;
messages.setTyping#58943ee2 flags:# peer:InputPeer top_msg_id:flags.0?int action:SendMessageAction = Bool;
-messages.sendMessage#dff8042c flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true invert_media:flags.16?true peer:InputPeer reply_to:flags.0?InputReplyTo message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut = Updates;
-messages.sendMedia#7bd66041 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true invert_media:flags.16?true peer:InputPeer reply_to:flags.0?InputReplyTo media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut = Updates;
+messages.sendMessage#983f9745 flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true invert_media:flags.16?true peer:InputPeer reply_to:flags.0?InputReplyTo message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut effect:flags.18?long = Updates;
+messages.sendMedia#7852834e flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true invert_media:flags.16?true peer:InputPeer reply_to:flags.0?InputReplyTo media:InputMedia message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut effect:flags.18?long = Updates;
messages.forwardMessages#d5039208 flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true drop_author:flags.11?true drop_media_captions:flags.12?true noforwards:flags.14?true from_peer:InputPeer id:Vector random_id:Vector to_peer:InputPeer top_msg_id:flags.9?int schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut = Updates;
messages.reportSpam#cf1592db peer:InputPeer = Bool;
messages.getPeerSettings#efd9a6a2 peer:InputPeer = messages.PeerSettings;
@@ -2038,7 +2132,7 @@ messages.editInlineBotMessage#83557dba flags:# no_webpage:flags.1?true invert_me
messages.getBotCallbackAnswer#9342ca07 flags:# game:flags.1?true peer:InputPeer msg_id:int data:flags.0?bytes password:flags.2?InputCheckPasswordSRP = messages.BotCallbackAnswer;
messages.setBotCallbackAnswer#d58f130a flags:# alert:flags.1?true query_id:long message:flags.0?string url:flags.2?string cache_time:int = Bool;
messages.getPeerDialogs#e470bcfd peers:Vector = messages.PeerDialogs;
-messages.saveDraft#7ff3b806 flags:# no_webpage:flags.1?true invert_media:flags.6?true reply_to:flags.4?InputReplyTo peer:InputPeer message:string entities:flags.3?Vector media:flags.5?InputMedia = Bool;
+messages.saveDraft#d372c5ce flags:# no_webpage:flags.1?true invert_media:flags.6?true reply_to:flags.4?InputReplyTo peer:InputPeer message:string entities:flags.3?Vector media:flags.5?InputMedia effect:flags.7?long = Bool;
messages.getAllDrafts#6a3f8d65 = Updates;
messages.getFeaturedStickers#64780b14 hash:long = messages.FeaturedStickers;
messages.readFeaturedStickers#5b118126 id:Vector = Bool;
@@ -2066,7 +2160,7 @@ messages.faveSticker#b9ffc55b id:InputDocument unfave:Bool = Bool;
messages.getUnreadMentions#f107e790 flags:# peer:InputPeer top_msg_id:flags.0?int offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
messages.readMentions#36e5bf4d flags:# peer:InputPeer top_msg_id:flags.0?int = messages.AffectedHistory;
messages.getRecentLocations#702a40e0 peer:InputPeer limit:int hash:long = messages.Messages;
-messages.sendMultiMedia#c964709 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true invert_media:flags.16?true peer:InputPeer reply_to:flags.0?InputReplyTo multi_media:Vector schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut = Updates;
+messages.sendMultiMedia#37b74355 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true noforwards:flags.14?true update_stickersets_order:flags.15?true invert_media:flags.16?true peer:InputPeer reply_to:flags.0?InputReplyTo multi_media:Vector schedule_date:flags.10?int send_as:flags.13?InputPeer quick_reply_shortcut:flags.17?InputQuickReplyShortcut effect:flags.18?long = Updates;
messages.uploadEncryptedFile#5057c497 peer:InputEncryptedChat file:InputEncryptedFile = EncryptedFile;
messages.searchStickerSets#35705b8a flags:# exclude_featured:flags.0?true q:string hash:long = messages.FoundStickerSets;
messages.getSplitRanges#1cff7e08 = Vector;
@@ -2138,9 +2232,9 @@ messages.searchSentMedia#107e31a0 q:string filter:MessagesFilter limit:int = mes
messages.getAttachMenuBots#16fcc2cb hash:long = AttachMenuBots;
messages.getAttachMenuBot#77216192 bot:InputUser = AttachMenuBotsBot;
messages.toggleBotInAttachMenu#69f59d69 flags:# write_allowed:flags.0?true bot:InputUser enabled:Bool = Bool;
-messages.requestWebView#269dc2c1 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:flags.0?InputReplyTo send_as:flags.13?InputPeer = WebViewResult;
+messages.requestWebView#269dc2c1 flags:# from_bot_menu:flags.4?true silent:flags.5?true compact:flags.7?true peer:InputPeer bot:InputUser url:flags.1?string start_param:flags.3?string theme_params:flags.2?DataJSON platform:string reply_to:flags.0?InputReplyTo send_as:flags.13?InputPeer = WebViewResult;
messages.prolongWebView#b0d81a83 flags:# silent:flags.5?true peer:InputPeer bot:InputUser query_id:long reply_to:flags.0?InputReplyTo send_as:flags.13?InputPeer = Bool;
-messages.requestSimpleWebView#1a46500a flags:# from_switch_webview:flags.1?true from_side_menu:flags.2?true bot:InputUser url:flags.3?string start_param:flags.4?string theme_params:flags.0?DataJSON platform:string = SimpleWebViewResult;
+messages.requestSimpleWebView#413a3e73 flags:# from_switch_webview:flags.1?true from_side_menu:flags.2?true compact:flags.7?true bot:InputUser url:flags.3?string start_param:flags.4?string theme_params:flags.0?DataJSON platform:string = WebViewResult;
messages.sendWebViewResultMessage#a4314f5 bot_query_id:string result:InputBotInlineResult = WebViewMessageSent;
messages.sendWebViewData#dc0242c8 bot:InputUser random_id:long button_text:string data:string = Updates;
messages.transcribeAudio#269e9a49 peer:InputPeer msg_id:int = messages.TranscribedAudio;
@@ -2162,7 +2256,7 @@ messages.getEmojiProfilePhotoGroups#21a548f3 hash:int = messages.EmojiGroups;
messages.searchCustomEmoji#2c11c0d7 emoticon:string hash:long = EmojiList;
messages.togglePeerTranslations#e47cb579 flags:# disabled:flags.0?true peer:InputPeer = Bool;
messages.getBotApp#34fdc5c3 app:InputBotApp hash:long = messages.BotApp;
-messages.requestAppWebView#8c5a3b3c flags:# write_allowed:flags.0?true peer:InputPeer app:InputBotApp start_param:flags.1?string theme_params:flags.2?DataJSON platform:string = AppWebViewResult;
+messages.requestAppWebView#53618bce flags:# write_allowed:flags.0?true compact:flags.7?true peer:InputPeer app:InputBotApp start_param:flags.1?string theme_params:flags.2?DataJSON platform:string = WebViewResult;
messages.setChatWallPaper#8ffacae1 flags:# for_both:flags.3?true revert:flags.4?true peer:InputPeer wallpaper:flags.0?InputWallPaper settings:flags.2?WallPaperSettings id:flags.1?int = Updates;
messages.searchEmojiStickerSets#92b4494c flags:# exclude_featured:flags.0?true q:string hash:long = messages.FoundStickerSets;
messages.getSavedDialogs#5381d21a flags:# exclude_pinned:flags.0?true offset_date:int offset_id:int offset_peer:InputPeer limit:int hash:long = messages.SavedDialogs;
@@ -2185,6 +2279,12 @@ messages.sendQuickReplyMessages#6c750de1 peer:InputPeer shortcut_id:int id:Vecto
messages.deleteQuickReplyMessages#e105e910 shortcut_id:int id:Vector = Updates;
messages.toggleDialogFilterTags#fd2dda49 enabled:Bool = Bool;
messages.getMyStickers#d0b5e1fc offset_id:long limit:int = messages.MyStickers;
+messages.getEmojiStickerGroups#1dd840f5 hash:int = messages.EmojiGroups;
+messages.getAvailableEffects#dea20a39 hash:int = messages.AvailableEffects;
+messages.editFactCheck#589ee75 peer:InputPeer msg_id:int text:TextWithEntities = Updates;
+messages.deleteFactCheck#d1da940c peer:InputPeer msg_id:int = Updates;
+messages.getFactCheck#b9cdc5ee peer:InputPeer msg_id:Vector = Vector;
+messages.requestMainWebView#c9e01e7b flags:# compact:flags.7?true peer:InputPeer bot:InputUser start_param:flags.1?string theme_params:flags.0?DataJSON platform:string = WebViewResult;
updates.getState#edd4882a = updates.State;
updates.getDifference#19c2f763 flags:# pts:int pts_limit:flags.1?int pts_total_limit:flags.0?int date:int qts:int qts_limit:flags.2?int = updates.Difference;
@@ -2295,6 +2395,7 @@ channels.setBoostsToUnblockRestrictions#ad399cee channel:InputChannel boosts:int
channels.setEmojiStickers#3cd930b7 channel:InputChannel stickerset:InputStickerSet = Bool;
channels.reportSponsoredMessage#af8ff6b9 channel:InputChannel random_id:bytes option:bytes = channels.SponsoredMessageReportResult;
channels.restrictSponsoredMessages#9ae91519 channel:InputChannel restricted:Bool = Updates;
+channels.searchPosts#d19f987b hashtag:string offset_rate:int offset_peer:InputPeer offset_id:int limit:int = messages.Messages;
bots.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON;
bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool;
@@ -2312,6 +2413,13 @@ bots.toggleUsername#53ca973 bot:InputUser username:string active:Bool = Bool;
bots.canSendMessage#1359f4e6 bot:InputUser = Bool;
bots.allowSendMessage#f132e3ef bot:InputUser = Updates;
bots.invokeWebViewCustomMethod#87fc5e7 bot:InputUser custom_method:string params:DataJSON = DataJSON;
+bots.getPopularAppBots#c2510192 offset:string limit:int = bots.PopularAppBots;
+bots.addPreviewMedia#17aeb75a bot:InputUser lang_code:string media:InputMedia = BotPreviewMedia;
+bots.editPreviewMedia#8525606f bot:InputUser lang_code:string media:InputMedia new_media:InputMedia = BotPreviewMedia;
+bots.deletePreviewMedia#2d0135b3 bot:InputUser lang_code:string media:Vector = Bool;
+bots.reorderPreviewMedias#b627f3aa bot:InputUser lang_code:string order:Vector = Bool;
+bots.getPreviewInfo#423ab3ad bot:InputUser lang_code:string = bots.PreviewInfo;
+bots.getPreviewMedias#a2a5594d bot:InputUser = Vector;
payments.getPaymentForm#37148dbb flags:# invoice:InputInvoice theme_params:flags.0?DataJSON = payments.PaymentForm;
payments.getPaymentReceipt#2478d1cc peer:InputPeer msg_id:int = payments.PaymentReceipt;
@@ -2329,6 +2437,16 @@ payments.checkGiftCode#8e51b4c1 slug:string = payments.CheckedGiftCode;
payments.applyGiftCode#f6e26854 slug:string = Updates;
payments.getGiveawayInfo#f4239425 peer:InputPeer msg_id:int = payments.GiveawayInfo;
payments.launchPrepaidGiveaway#5ff58f20 peer:InputPeer giveaway_id:long purpose:InputStorePaymentPurpose = Updates;
+payments.getStarsTopupOptions#c00ec7d3 = Vector;
+payments.getStarsStatus#104fcfa7 peer:InputPeer = payments.StarsStatus;
+payments.getStarsTransactions#97938d5a flags:# inbound:flags.0?true outbound:flags.1?true ascending:flags.2?true peer:InputPeer offset:string limit:int = payments.StarsStatus;
+payments.sendStarsForm#2bb731d flags:# form_id:long invoice:InputInvoice = payments.PaymentResult;
+payments.refundStarsCharge#25ae8f4a user_id:InputUser charge_id:string = Updates;
+payments.getStarsRevenueStats#d91ffad6 flags:# dark:flags.0?true peer:InputPeer = payments.StarsRevenueStats;
+payments.getStarsRevenueWithdrawalUrl#13bbe8b3 peer:InputPeer stars:long password:InputCheckPasswordSRP = payments.StarsRevenueWithdrawalUrl;
+payments.getStarsRevenueAdsAccountUrl#d1d7efc5 peer:InputPeer = payments.StarsRevenueAdsAccountUrl;
+payments.getStarsTransactionsByID#27842d2e peer:InputPeer id:Vector = payments.StarsStatus;
+payments.getStarsGiftOptions#d3c96bc8 flags:# user_id:flags.0?InputUser = Vector;
stickers.createStickerSet#9021ab67 flags:# masks:flags.0?true emojis:flags.5?true text_color:flags.6?true user_id:InputUser title:string short_name:string thumb:flags.2?InputDocument stickers:Vector software:flags.3?string = messages.StickerSet;
stickers.removeStickerFromSet#f7760f51 sticker:InputDocument = messages.StickerSet;
@@ -2430,6 +2548,7 @@ stories.getChatsToSend#a56a8b60 = messages.Chats;
stories.togglePeerStoriesHidden#bd0415c4 peer:InputPeer hidden:Bool = Bool;
stories.getStoryReactionsList#b9b2881f flags:# forwards_first:flags.2?true peer:InputPeer id:int reaction:flags.0?Reaction offset:flags.1?string limit:int = stories.StoryReactionsList;
stories.togglePinnedToTop#b297e9b peer:InputPeer id:Vector = Bool;
+stories.searchPosts#6cea116a flags:# hashtag:flags.0?string area:flags.1?MediaArea offset:string limit:int = stories.FoundStories;
premium.getBoostsList#60f67660 flags:# gifts:flags.0?true peer:InputPeer offset:string limit:int = premium.BoostsList;
premium.getMyBoosts#be77b4a = premium.MyBoosts;
@@ -2451,4 +2570,4 @@ test.useError = Error;
test.useConfigSimple = help.ConfigSimple;
test.parseInputAppEvent = InputAppEvent;
-// LAYER 178
+// LAYER 185
diff --git a/compiler/docs/compiler.py b/compiler/docs/compiler.py
index 5ff86f766c..555999bc61 100644
--- a/compiler/docs/compiler.py
+++ b/compiler/docs/compiler.py
@@ -135,80 +135,118 @@ def get_title_list(s: str) -> list:
categories = dict(
utilities="""
Utilities
+ run
start
stop
restart
- run
+ export_session_string
add_handler
remove_handler
- export_session_string
stop_transmission
set_parse_mode
""",
- advanced="""
- Advanced
- invoke
- resolve_peer
- save_file
- """,
authorization="""
Authorization
- connect
- disconnect
initialize
- terminate
- send_code
- resend_code
+ sign_up
+ accept_terms_of_service
sign_in
sign_in_bot
- sign_up
+ connect
+ send_code
+ resend_code
+ recover_password
+ send_recovery_code
get_password_hint
check_password
- send_recovery_code
- recover_password
- accept_terms_of_service
log_out
+ disconnect
+ terminate
+ get_me
+ get_active_sessions
+ terminate_session
+ terminate_all_other_sessions
""",
- bots="""
- Bots
- get_inline_bot_results
- send_inline_bot_result
- answer_callback_query
- answer_inline_query
- request_callback_answer
- send_game
- set_game_score
- get_game_high_scores
- set_bot_commands
- get_bot_commands
- delete_bot_commands
- set_bot_default_privileges
- get_bot_default_privileges
- set_chat_menu_button
- get_chat_menu_button
- answer_web_app_query
+ messages="""
+ Messages
+ send_message
+ forward_messages
+ copy_message
+ send_photo
+ send_audio
+ send_document
+ send_video
+ send_animation
+ send_voice
+ send_video_note
+ send_cached_media
+ send_paid_media
+ send_media_group
+ get_media_group
+ copy_media_group
+ send_location
+ send_venue
+ send_contact
+ send_poll
+ send_dice
+ send_chat_action
+ set_reaction
+ download_media
+ stream_media
+ edit_message_text
+ edit_inline_text
+ edit_message_caption
+ edit_inline_caption
+ edit_message_media
+ edit_inline_media
+ edit_message_reply_markup
+ edit_inline_reply_markup
+ edit_cached_media
+ stop_poll
+ delete_messages
+ get_chat_sponsored_messages
+ get_chat_history
+ get_chat_history_count
+ read_chat_history
+ get_messages
+ view_messages
+ get_discussion_message
+ get_discussion_replies
+ get_discussion_replies_count
+ search_global
+ search_global_count
+ search_messages
+ search_messages_count
+ search_public_messages_by_tag
+ count_public_messages_by_tag
+ vote_poll
+ retract_vote
+ translate_text
+ translate_message_text
""",
chats="""
Chats
- join_chat
- leave_chat
ban_chat_member
unban_chat_member
restrict_chat_member
promote_chat_member
set_administrator_title
+ set_chat_permissions
set_chat_photo
delete_chat_photo
set_chat_title
set_chat_description
- set_chat_permissions
pin_chat_message
unpin_chat_message
unpin_all_chat_messages
+ search_chats
+ join_chat
+ leave_chat
get_chat
- get_chat_member
get_chat_members
get_chat_members_count
+ get_chat_member
+
get_dialogs
get_dialogs_count
set_chat_username
@@ -223,7 +261,7 @@ def get_title_list(s: str) -> list:
delete_supergroup
delete_user_history
set_slow_mode
- set_chat_ttl
+ set_chat_message_auto_delete_time
mark_chat_unread
get_chat_event_log
get_chat_online_count
@@ -231,115 +269,47 @@ def get_title_list(s: str) -> list:
set_send_as_chat
set_chat_protected_content
get_created_chats
- """,
- chat_topics="""
- Chat Forum Topics
- close_forum_topic
- create_forum_topic
- delete_forum_topic
- edit_forum_topic
- get_forum_topic_icon_stickers
- hide_forum_topic
- reopen_forum_topic
- unhide_forum_topic
- get_forum_topics
- get_forum_topic
- """,
- contacts="""
- Contacts
- add_contact
- delete_contacts
- import_contacts
- get_contacts
- get_contacts_count
+ transfer_chat_ownership
""",
invite_links="""
Invite Links
- get_chat_invite_link
export_chat_invite_link
create_chat_invite_link
edit_chat_invite_link
revoke_chat_invite_link
- delete_chat_invite_link
- get_chat_invite_link_joiners
- get_chat_invite_link_joiners_count
get_chat_admin_invite_links
get_chat_admin_invite_links_count
get_chat_admins_with_invite_links
- get_chat_join_requests
+ get_chat_invite_link
+ get_chat_invite_link_joiners
+ get_chat_invite_link_joiners_count
+ delete_chat_invite_link
delete_chat_admin_invite_links
+ get_chat_join_requests
approve_chat_join_request
approve_all_chat_join_requests
decline_chat_join_request
- decline_all_chat_join_requests
+ decline_all_chat_join_requests
""",
- messages="""
- Messages
- copy_media_group
- copy_message
- delete_messages
- download_media
- edit_inline_caption
- edit_inline_media
- edit_inline_reply_markup
- edit_inline_text
- edit_message_caption
- edit_message_media
- edit_message_reply_markup
- edit_message_text
- forward_messages
- get_chat_history
- get_chat_history_count
- get_custom_emoji_stickers
- get_discussion_message
- get_discussion_replies
- get_discussion_replies_count
- get_media_group
- get_messages
- read_chat_history
- retract_vote
- search_global
- search_global_count
- search_messages
- search_messages_count
- send_animation
- send_audio
- send_cached_media
- send_chat_action
- send_contact
- send_dice
- send_document
- send_location
- send_media_group
- send_message
- send_photo
- send_poll
- send_sticker
- send_venue
- send_video
- send_video_note
- send_voice
- set_reaction
- stop_poll
- stream_media
- vote_poll
- """,
- password="""
- Password
- enable_cloud_password
- change_cloud_password
- remove_cloud_password
- """,
- stickers="""
- Stickers
- get_stickers
+ chat_topics="""
+ Chat Forum Topics
+ get_forum_topic_icon_stickers
+ create_forum_topic
+ edit_forum_topic
+ close_forum_topic
+ reopen_forum_topic
+ delete_forum_topic
+ hide_forum_topic
+ unhide_forum_topic
+ get_forum_topics
+ get_forum_topic
""",
users="""
Users
- get_me
- get_users
get_chat_photos
get_chat_photos_count
+ get_users
+
set_profile_photo
delete_profile_photos
set_username
@@ -351,11 +321,85 @@ def get_title_list(s: str) -> list:
set_emoji_status
set_birthdate
set_personal_chat
+ delete_account
+ update_status
""",
- business="""
- Telegram Business
+ contacts="""
+ Contacts
+ add_contact
+ delete_contacts
+ import_contacts
+ get_contacts
+ get_contacts_count
+ """,
+ password="""
+ Password
+ enable_cloud_password
+ change_cloud_password
+ remove_cloud_password
+ """,
+ bots="""
+ Bots
+ answer_callback_query
+ request_callback_answer
+ set_bot_commands
+ delete_bot_commands
+ get_bot_commands
+ set_bot_name
+ get_bot_name
+ set_bot_info_description
+ get_bot_info_description
+ set_bot_info_short_description
+ get_bot_info_short_description
+ set_chat_menu_button
+ get_chat_menu_button
+ set_bot_default_privileges
+ get_bot_default_privileges
+ send_game
+ set_game_score
+ get_game_high_scores
+ answer_inline_query
+ get_inline_bot_results
+ send_inline_bot_result
+ answer_web_app_query
+ send_web_app_custom_request
+ """,
+ phone="""
+ Phone
+ create_video_chat
+ discard_group_call
+ get_video_chat_rtmp_url
+ invite_group_call_participants
+ load_group_call_participants
+ """,
+ stickers="""
+ Stickers
+ send_sticker
+ get_custom_emoji_stickers
+ get_message_effects
+ get_stickers
+ """,
+ stories="""
+ Stories
+ get_stories
+ """,
+ payments="""
+ Payments
+ send_invoice
+ create_invoice_link
+ answer_shipping_query
+ answer_pre_checkout_query
+ refund_star_payment
get_business_connection
- """,
+ get_collectible_item_info
+ """,
+ advanced="""
+ Advanced
+ invoke
+ resolve_peer
+ get_file
+ save_file
+ """
)
root = PYROGRAM_API_DEST + "/methods"
@@ -394,10 +438,107 @@ def get_title_list(s: str) -> list:
# Types
categories = dict(
- authorization="""
- Authorization
- SentCode
- TermsOfService
+ users_chats="""
+ Users & Chats
+ Birthdate
+ User
+ Chat
+ Username
+ ChatShared
+ UsersShared
+ ChatAdminWithInviteLinks
+ ChatColor
+ ChatEvent
+ ChatEventFilter
+ ChatInviteLink
+ ChatJoiner
+ ChatJoinRequest
+ ChatMember
+ ChatMemberUpdated
+ ChatPermissions
+ ChatPhoto
+ ChatPrivileges
+ ChatReactions
+ VideoChatScheduled
+ VideoChatStarted
+ VideoChatEnded
+ VideoChatParticipantsInvited
+ Dialog
+ EmojiStatus
+ GroupCallParticipant
+ InviteLinkImporter
+ Restriction
+ RtmpUrl
+ """,
+ messages_media="""
+ Messages & Media
+ Message
+ MessageEntity
+ TextQuote
+ ExternalReplyInfo
+ ReplyParameters
+ MessageOrigin
+ MessageOriginUser
+ MessageOriginHiddenUser
+ MessageOriginChat
+ MessageOriginChannel
+ MessageImportInfo
+ Photo
+ Animation
+ Audio
+ Document
+ Story
+ Video
+ VideoNote
+ Voice
+ PaidMediaInfo
+ PaidMedia
+ PaidMediaPreview
+ PaidMediaPhoto
+ PaidMediaVideo
+ Contact
+ Dice
+ PollOption
+ InputPollOption
+ Poll
+ Location
+ Venue
+ WebAppData
+ MessageAutoDeleteTimerChanged
+ ChatBoostAdded
+ ChatBackground
+ Game
+ GiftCode
+ GiftedPremium
+ GiftedStars
+ Giveaway
+ GiveawayCompleted
+ GiveawayWinners
+ MessageEffect
+ MessageReactionCountUpdated
+ MessageReactionUpdated
+ MessageReactions
+ Reaction
+ ReactionCount
+ ReactionType
+ ReactionTypeEmoji
+ ReactionTypeCustomEmoji
+ Thumbnail
+ TranslatedText
+ StrippedThumbnail
+ SponsoredMessage
+ Sticker
+ WebPage
+ """,
+ chat_topics="""
+ Chat Forum Topics
+ ForumTopic
+ ForumTopicCreated
+ ForumTopicClosed
+ ForumTopicEdited
+ ForumTopicReopened
+ GeneralForumTopicHidden
+ GeneralForumTopicUnhidden
""",
bot_commands="""
Bot Commands
@@ -436,16 +577,6 @@ def get_title_list(s: str) -> list:
SentWebAppMessage
SwitchInlineQueryChosenChat
""",
- chat_topics="""
- Chat Forum Topics
- ForumTopic
- ForumTopicClosed
- ForumTopicCreated
- ForumTopicEdited
- ForumTopicReopened
- GeneralForumTopicHidden
- GeneralForumTopicUnhidden
- """,
inline_mode="""
Inline Mode
ChosenInlineResult
@@ -469,6 +600,13 @@ def get_title_list(s: str) -> list:
InlineQueryResultLocation
InlineQueryResultVenue
""",
+ authorization="""
+ Authorization
+ ActiveSession
+ ActiveSessions
+ SentCode
+ TermsOfService
+ """,
input_media="""
Input Media
InputMedia
@@ -480,96 +618,39 @@ def get_title_list(s: str) -> list:
InputPhoneContact
LinkPreviewOptions
""",
+ input_paid_media="""
+ Input Paid Media
+ InputPaidMedia
+ InputPaidMediaPhoto
+ InputPaidMediaVideo
+ """,
input_message_content="""
InputMessageContent
- ExternalReplyInfo
InputMessageContent
InputTextMessageContent
- ReplyParameters
- """,
- messages_media="""
- Messages & Media
- Animation
- Audio
- ChatBoostAdded
- Contact
- Dice
- Document
- Game
- GiftCode
- GiftedPremium
- Giveaway
- GiveawayCompleted
- GiveawayWinners
- Location
- Message
- MessageEntity
- MessageImportInfo
- MessageOrigin
- MessageOriginChannel
- MessageOriginChat
- MessageOriginHiddenUser
- MessageOriginUser
- MessageReactionCountUpdated
- MessageReactionUpdated
- MessageReactions
- Photo
- Reaction
- ReactionCount
- ReactionType
- ReactionTypeEmoji
- ReactionTypeCustomEmoji
- Thumbnail
- StrippedThumbnail
- Poll
- PollOption
- Sticker
- Story
- Venue
- Video
- VideoNote
- Voice
- WebAppData
- WebPage
+ InputLocationMessageContent
+ InputVenueMessageContent
+ InputContactMessageContent
+ InputInvoiceMessageContent
""",
- business="""
- Telegram Business
+ payments="""
+ Payments
BusinessConnection
BusinessIntro
BusinessLocation
BusinessOpeningHours
BusinessOpeningHoursInterval
- """,
- users_chats="""
- Users & Chats
- Birthdate
- Chat
- ChatAdminWithInviteLinks
- ChatColor
- ChatEvent
- ChatEventFilter
- ChatInviteLink
- ChatJoiner
- ChatJoinRequest
- ChatMember
- ChatMemberUpdated
- ChatPermissions
- ChatPhoto
- ChatPrivileges
- ChatReactions
- ChatShared
- Dialog
- EmojiStatus
- InviteLinkImporter
- Restriction
- User
- Username
- UsersShared
- VideoChatEnded
- VideoChatParticipantsInvited
- VideoChatScheduled
- VideoChatStarted
- """,
+ CollectibleItemInfo
+ LabeledPrice
+ Invoice
+ ShippingAddress
+ OrderInfo
+ ShippingOption
+ SuccessfulPayment
+ RefundedPayment
+ ShippingQuery
+ PreCheckoutQuery
+ """
)
root = PYROGRAM_API_DEST + "/types"
@@ -612,6 +693,7 @@ def get_title_list(s: str) -> list:
Message.unpin
Message.edit
Message.edit_text
+ Message.edit_cached_media
Message.edit_caption
Message.edit_media
Message.edit_reply_markup
@@ -636,6 +718,9 @@ def get_title_list(s: str) -> list:
Message.reply_voice
Message.get_media_group
Message.react
+ Message.read
+ Message.view
+ Message.translate
""",
chat="""
Chat
@@ -656,6 +741,7 @@ def get_title_list(s: str) -> list:
Chat.mark_unread
Chat.set_protected_content
Chat.unpin_all_messages
+ Chat.set_message_auto_delete_time
""",
user="""
User
@@ -676,6 +762,14 @@ def get_title_list(s: str) -> list:
InlineQuery
InlineQuery.answer
""",
+ pre_checkout_query="""
+ PreCheckoutQuery
+ PreCheckoutQuery.answer
+ """,
+ shipping_query="""
+ ShippingQuery
+ ShippingQuery.answer
+ """,
chat_join_request="""
ChatJoinRequest
ChatJoinRequest.approve
@@ -686,6 +780,10 @@ def get_title_list(s: str) -> list:
Story.react
Story.download
""",
+ active_session="""
+ ActiveSession
+ ActiveSession.terminate
+ """,
)
root = PYROGRAM_API_DEST + "/bound-methods"
diff --git a/compiler/docs/template/bound-methods.rst b/compiler/docs/template/bound-methods.rst
index 82cdc158e7..f26c907894 100644
--- a/compiler/docs/template/bound-methods.rst
+++ b/compiler/docs/template/bound-methods.rst
@@ -88,6 +88,32 @@ InlineQuery
{inline_query_toctree}
+PreCheckoutQuery
+-----------------
+
+.. hlist::
+ :columns: 2
+
+ {pre_checkout_query_hlist}
+
+.. toctree::
+ :hidden:
+
+ {pre_checkout_query_toctree}
+
+ShippingQuery
+--------------
+
+.. hlist::
+ :columns: 2
+
+ {shipping_query_hlist}
+
+.. toctree::
+ :hidden:
+
+ {shipping_query_toctree}
+
ChatJoinRequest
---------------
@@ -102,7 +128,7 @@ ChatJoinRequest
{chat_join_request_toctree}
Story
----------------
+------
.. hlist::
:columns: 2
@@ -113,3 +139,16 @@ Story
:hidden:
{story_toctree}
+
+ActiveSession
+-------------
+
+.. hlist::
+ :columns: 2
+
+ {active_session_hlist}
+
+.. toctree::
+ :hidden:
+
+ {active_session_toctree}
diff --git a/compiler/docs/template/methods.rst b/compiler/docs/template/methods.rst
index 6f88bd4837..20b25d26c7 100644
--- a/compiler/docs/template/methods.rst
+++ b/compiler/docs/template/methods.rst
@@ -47,6 +47,19 @@ Utilities
.. currentmodule:: pyrogram.Client
+Authorization
+-------------
+
+.. autosummary::
+ :nosignatures:
+
+ {authorization}
+
+.. toctree::
+ :hidden:
+
+ {authorization}
+
Messages
--------
@@ -73,31 +86,31 @@ Chats
{chats}
-Chat Forum Topics
------
+Invite Links
+------------
.. autosummary::
:nosignatures:
- {chat_topics}
+ {invite_links}
.. toctree::
:hidden:
- {chat_topics}
+ {invite_links}
-Telegram Business
--------------
+Chat Forum Topics
+------------------
.. autosummary::
:nosignatures:
- {business}
+ {chat_topics}
.. toctree::
:hidden:
- {business}
+ {chat_topics}
Users
-----
@@ -112,31 +125,44 @@ Users
{users}
-Invite Links
-------------
+Contacts
+--------
.. autosummary::
:nosignatures:
- {invite_links}
+ {contacts}
.. toctree::
:hidden:
- {invite_links}
+ {contacts}
-Contacts
+Password
--------
.. autosummary::
:nosignatures:
- {contacts}
+ {password}
.. toctree::
:hidden:
- {contacts}
+ {password}
+
+Bots
+----
+
+.. autosummary::
+ :nosignatures:
+
+ {bots}
+
+.. toctree::
+ :hidden:
+
+ {bots}
Stickers
--------
@@ -151,44 +177,44 @@ Stickers
{stickers}
-Password
+Stories
--------
.. autosummary::
:nosignatures:
- {password}
+ {stories}
.. toctree::
:hidden:
- {password}
+ {stories}
-Bots
-----
+Payments
+---------
.. autosummary::
:nosignatures:
- {bots}
+ {payments}
.. toctree::
:hidden:
- {bots}
+ {payments}
-Authorization
--------------
+Phone
+------
.. autosummary::
:nosignatures:
- {authorization}
+ {phone}
.. toctree::
:hidden:
- {authorization}
+ {phone}
Advanced
--------
diff --git a/compiler/docs/template/types.rst b/compiler/docs/template/types.rst
index ebebe0780d..152ab98a7b 100644
--- a/compiler/docs/template/types.rst
+++ b/compiler/docs/template/types.rst
@@ -21,31 +21,31 @@ are only returned by other methods. You also don't need to import them, unless y
.. currentmodule:: pyrogram.types
-Users & Chats
--------------
+Messages & Media
+----------------
.. autosummary::
:nosignatures:
- {users_chats}
+ {messages_media}
.. toctree::
:hidden:
- {users_chats}
+ {messages_media}
-Messages & Media
-----------------
+Users & Chats
+-------------
.. autosummary::
:nosignatures:
- {messages_media}
+ {users_chats}
.. toctree::
:hidden:
- {messages_media}
+ {users_chats}
Bot keyboards
-------------
@@ -73,6 +73,19 @@ Bot commands
{bot_commands}
+Chat Forum Topics
+------------------
+
+.. autosummary::
+ :nosignatures:
+
+ {chat_topics}
+
+.. toctree::
+ :hidden:
+
+ {chat_topics}
+
Input Media
-----------
@@ -86,8 +99,21 @@ Input Media
{input_media}
+Input Paid Media
+-----------------
+
+.. autosummary::
+ :nosignatures:
+
+ {input_paid_media}
+
+.. toctree::
+ :hidden:
+
+ {input_paid_media}
+
Inline Mode
------------
+------------
.. autosummary::
:nosignatures:
@@ -125,28 +151,15 @@ Authorization
{authorization}
-Chat Forum Topics
--------------
-
-.. autosummary::
- :nosignatures:
-
- {chat_topics}
-
-.. toctree::
- :hidden:
-
- {chat_topics}
-
-Telegram Business
--------------
+Payments
+---------
.. autosummary::
:nosignatures:
- {business}
+ {payments}
.. toctree::
:hidden:
- {business}
+ {payments}
diff --git a/compiler/errors/sort.py b/compiler/errors/sort.py
index 47bf0315bf..44713095ea 100644
--- a/compiler/errors/sort.py
+++ b/compiler/errors/sort.py
@@ -57,11 +57,6 @@
for h in e:
dct = {}
- for p in Path("source/").glob(f"{h}*.tsv"):
- with open(p) as f:
- reader = csv.reader(f, delimiter="\t")
- dct = {k: v for k, v in reader if k != "id"}
-
j = d.get("errors").get(h)
for k in j:
if k.endswith("_*"):
@@ -72,6 +67,13 @@
l = l.replace("%d", "{value}")
dct[m] = l
+ for p in Path("source/").glob(f"{h}*.tsv"):
+ with open(p) as f:
+ reader = csv.reader(f, delimiter="\t")
+ for k, v in reader:
+ if k != "id":
+ dct[k] = v
+
keys = sorted(dct)
for p in Path("source/").glob(f"{h}*.tsv"):
diff --git a/compiler/errors/source/400_BAD_REQUEST.tsv b/compiler/errors/source/400_BAD_REQUEST.tsv
index d10b36126d..5261ea3a65 100644
--- a/compiler/errors/source/400_BAD_REQUEST.tsv
+++ b/compiler/errors/source/400_BAD_REQUEST.tsv
@@ -76,6 +76,8 @@ CHANNEL_PARICIPANT_MISSING The current user is not in the channel.
CHANNEL_PRIVATE You haven't joined this channel/supergroup.
CHANNEL_TOO_BIG This channel has too many participants (>1000) to be deleted.
CHANNEL_TOO_LARGE Channel is too large to be deleted; this error is issued when trying to delete channels with more than 1000 members (subject to change).
+CHARGE_ALREADY_REFUNDED The charge id was already used for a refund.
+CHARGE_NOT_FOUND The charge id was not found.
CHATLIST_EXCLUDE_INVALID The specified `exclude_peers` are invalid.
CHAT_ABOUT_NOT_MODIFIED About text has not changed.
CHAT_ABOUT_TOO_LONG Chat about too long.
@@ -144,6 +146,9 @@ ENTITY_MENTION_USER_INVALID You mentioned an invalid user.
ERROR_TEXT_EMPTY The provided error message is empty.
EXPIRE_DATE_INVALID The specified expiration date is invalid.
EXPORT_CARD_INVALID Provided card is invalid.
+EXTENDED_MEDIA_AMOUNT_INVALID The maximum amount of `star_count` should be less than the `stars_paid_post_amount_max` [client configuration parameters](/api/config).
+EXTENDED_MEDIA_PEER_INVALID The specified chat type is invalid.
+EXTENDED_MEDIA_TYPE_INVALID The specified extended media type is unsupported.
EXTERNAL_URL_INVALID External URL invalid.
FIELD_NAME_EMPTY The field with the name FIELD_NAME is missing
FIELD_NAME_INVALID The field with the name FIELD_NAME is invalid
@@ -171,6 +176,7 @@ FILTER_TITLE_EMPTY The title field of the filter is empty.
FIRSTNAME_INVALID The first name is invalid.
FOLDER_ID_EMPTY An empty folder ID was specified.
FOLDER_ID_INVALID Invalid folder ID.
+FORM_ID_EXPIRED The specified id has expired.
FORUM_ENABLED You can't execute the specified action because the group is a [forum](https://core.telegram.org/api/forum), disable forum functionality to continue.
FRESH_CHANGE_ADMINS_FORBIDDEN You were just elected admin, you can't add or modify other admins yet.
FROM_MESSAGE_BOT_DISABLED Bots can't use fromMessage min constructors.
@@ -417,6 +423,7 @@ STICKER_THUMB_TGS_NOTGS Incorrect stickerset TGS thumb file provided.
STICKER_VIDEO_BIG The specified video sticker is too big.
STICKER_VIDEO_NODOC You must send the video sticker as a document.
STICKER_VIDEO_NOWEBM The specified video sticker is not in webm format.
+STORAGE_KEY_REQUIRED A cloud storage key is required.
STORIES_NEVER_CREATED This peer hasn't ever posted any stories.
STORIES_TOO_MUCH You have hit the maximum active stories limit as specified by the [`story_expiring_limit_*` client configuration parameters](https://core.telegram.org/api/config#story-expiring-limit-default): you should buy a [Premium](/api/premium) subscription, delete an active story, or wait for the oldest story to expire.
STORY_ID_EMPTY You specified no story IDs.
diff --git a/compiler/errors/source/403_FORBIDDEN.tsv b/compiler/errors/source/403_FORBIDDEN.tsv
index 09d2240d40..557f5416b8 100644
--- a/compiler/errors/source/403_FORBIDDEN.tsv
+++ b/compiler/errors/source/403_FORBIDDEN.tsv
@@ -25,7 +25,7 @@ GROUPCALL_ALREADY_STARTED The groupcall has already started, you can join direct
GROUPCALL_FORBIDDEN The group call has already ended.
INLINE_BOT_REQUIRED Only the inline bot can edit message.
MESSAGE_AUTHOR_REQUIRED Message author required.
-MESSAGE_DELETE_FORBIDDEN You can't delete one of the messages you tried to delete, most likely because it is a service message.
+MESSAGE_DELETE_FORBIDDEN You can't delete one of the messages you tried to delete.
NOT_ELIGIBLE You are not eligible to take part in Telegram's Premium for SMS initiative
PARTICIPANT_JOIN_MISSING Trying to enable a presentation, when the user hasn't joined the Video Chat with [phone.joinGroupCall](https://core.telegram.org/method/phone.joinGroupCall).
POLL_VOTE_REQUIRED Cast a vote in the poll before calling this method.
diff --git a/docs/source/.readthedocs.yaml b/docs/source/.readthedocs.yaml
new file mode 100644
index 0000000000..793c4ee29d
--- /dev/null
+++ b/docs/source/.readthedocs.yaml
@@ -0,0 +1,53 @@
+# .readthedocs.yaml
+# Read the Docs configuration file
+# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
+
+# Required
+version: 2
+
+# Set the OS, Python version and other tools you might need
+build:
+ os: ubuntu-22.04
+ tools:
+ python: "3.11"
+ # You can also specify other tool versions:
+ # nodejs: "19"
+ # rust: "1.64"
+ # golang: "1.19"
+ jobs:
+ post_checkout:
+ # Cancel building pull requests when there aren't changed in the docs directory or YAML file.
+ # You can add any other files or directories that you'd like here as well,
+ # like your docs requirements file, or other files that will change your docs build.
+ #
+ # If there are no changes (git diff exits with 0) we force the command to return with 183.
+ # This is a special exit code on Read the Docs that will cancel the build immediately.
+ - |
+ if [ "$READTHEDOCS_VERSION_TYPE" = "external" ] && git diff --quiet origin/unknown_errors -- docs/;
+ then
+ exit 183;
+ fi
+ pre_build:
+ - echo "Command run at 'pre_build' step"
+ - python -m pip install httpx==0.27.0 lxml==5.2.2
+ - curl -sL https://github.com/hydrogram/hydrogram/raw/013268c/dev_tools/generate_docs_json.py | python
+ - ls -al compiler/api/
+ - python -m pip install .
+ - cd compiler/docs && python compiler.py && cd ../..
+ - echo "Command run at 'pre_build' step"
+
+# Build documentation in the "docs/" directory with Sphinx
+sphinx:
+ configuration: docs/source/conf.py
+
+# Optionally build your docs in additional formats such as PDF and ePub
+formats:
+ - htmlzip
+
+
+# Optional but recommended, declare the Python requirements required
+# to build your documentation
+# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
+python:
+ install:
+ - requirements: docs/source/requirements.txt
diff --git a/docs/source/api/decorators.rst b/docs/source/api/decorators.rst
index 15ce487473..0013ba86d2 100644
--- a/docs/source/api/decorators.rst
+++ b/docs/source/api/decorators.rst
@@ -51,6 +51,7 @@ Index
- :meth:`~Client.on_deleted_messages`
- :meth:`~Client.on_user_status`
- :meth:`~Client.on_disconnect`
+ - :meth:`~Client.on_story`
- :meth:`~Client.on_raw_update`
-----
@@ -67,8 +68,8 @@ Details
.. autodecorator:: pyrogram.Client.on_inline_query()
.. autodecorator:: pyrogram.Client.on_chosen_inline_result()
.. autodecorator:: pyrogram.Client.on_callback_query()
-
-
+.. autodecorator:: pyrogram.Client.on_shipping_query()
+.. autodecorator:: pyrogram.Client.on_pre_checkout_query()
.. autodecorator:: pyrogram.Client.on_poll()
@@ -79,4 +80,5 @@ Details
.. autodecorator:: pyrogram.Client.on_deleted_messages()
.. autodecorator:: pyrogram.Client.on_user_status()
.. autodecorator:: pyrogram.Client.on_disconnect()
+.. autodecorator:: pyrogram.Client.on_story()
.. autodecorator:: pyrogram.Client.on_raw_update()
diff --git a/docs/source/api/handlers.rst b/docs/source/api/handlers.rst
index 0b25fb4621..9aebe5ebd6 100644
--- a/docs/source/api/handlers.rst
+++ b/docs/source/api/handlers.rst
@@ -52,6 +52,7 @@ Index
- :class:`UserStatusHandler`
- :class:`DisconnectHandler`
+ - :class:`StoryHandler`
- :class:`RawUpdateHandler`
-----
@@ -68,8 +69,8 @@ Details
.. autoclass:: InlineQueryHandler()
.. autoclass:: ChosenInlineResultHandler()
.. autoclass:: CallbackQueryHandler()
-
-
+.. autoclass:: ShippingQueryHandler()
+.. autoclass:: PreCheckoutQueryHandler()
.. autoclass:: PollHandler()
@@ -80,4 +81,5 @@ Details
.. autoclass:: DeletedMessagesHandler()
.. autoclass:: UserStatusHandler()
.. autoclass:: DisconnectHandler()
+.. autoclass:: StoryHandler()
.. autoclass:: RawUpdateHandler()
diff --git a/docs/source/api/storage/Storage.rst b/docs/source/api/storage/Storage.rst
index 56b0f391f2..4682131487 100644
--- a/docs/source/api/storage/Storage.rst
+++ b/docs/source/api/storage/Storage.rst
@@ -1,5 +1,5 @@
Storage
-=========
+========
.. autoclass:: pyrogram.storage.Storage()
:members:
diff --git a/docs/source/api/storage/index.rst b/docs/source/api/storage/index.rst
index 52f898bbd5..e6faadeab9 100644
--- a/docs/source/api/storage/index.rst
+++ b/docs/source/api/storage/index.rst
@@ -1,5 +1,5 @@
Pyrogram Storage Engines
-===============
+=========================
Storage Engines
===============
@@ -88,6 +88,37 @@ login using the same session; the storage used will still be in-memory:
Session strings are useful when you want to run authorized Pyrogram clients on platforms where their ephemeral
filesystems makes it harder for a file-based storage engine to properly work as intended.
+Custom Storages
+----------------
+
+If you want to use a custom storage engine, you can do so by implementing the :obj:`~pyrogram.storage.Storage` class. This class is an base class that defines the interface that all storage engines must implement.
+
+This class is a class that cannot be instantiated, but can be used to define a common interface for its subclasses. In this case, the :obj:`~pyrogram.storage.Storage` class defines the interface that all storage engines must implement.
+
+Custom Storage can be defined in :obj:`~pyrogram.Client` by passing ``storage_engine`` parameter with a :obj:`~pyrogram.storage.Storage` subclass.
+
+How to use the ``AioSQLiteStorage`` Storage Engine is shown below
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+``/path/to/your/file.session`` will be created if does not exist.
+
+.. code-block:: python
+
+ import asyncio
+ from pyrogram import Client
+ from pyrogram.storage.aio_sqlite_storage import AioSQLiteStorage
+ api_id = 12345
+ api_hash = "0123456789abcdef0123456789abcdef"
+ async def main():
+ async with Client(
+ "my_account",
+ api_id,
+ api_hash,
+ storage_engine=AioSQLiteStorage("/path/to/your/file.session")
+ ) as app:
+ await app.send_message(chat_id="me", text="Greetings from **Pyrogram**!")
+ asyncio.run(main())
+
Details
-------
diff --git a/docs/source/conf.py b/docs/source/conf.py
index bdb0ddaab0..cb1d22380c 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -31,20 +31,19 @@
"HEAD",
]).decode("UTF-8").strip()
-version = __version__
-
project = "pyrotgfork"
copyright = "2017-present, Dan"
author = "Dan"
+version = __version__
extensions = [
"sphinx.ext.autodoc",
+ "sphinx.ext.napoleon",
"sphinx.ext.autosummary",
"sphinx.ext.intersphinx",
- "sphinx.ext.napoleon",
- "sphinx.ext.viewcode",
+ # "sphinx.ext.viewcode",
"sphinx_copybutton",
- "sphinx.ext.coverage",
+ # "sphinx.ext.coverage",
]
intersphinx_mapping = {
@@ -79,7 +78,8 @@
"css/all.min.css",
"css/custom.css",
]
-
+html_show_sourcelink = True
+html_show_copyright = False
html_logo = html_static_path[0] + "/img/pyrogram.png"
html_favicon = html_static_path[0] + "/img/favicon.ico"
html_theme_options = {
@@ -135,3 +135,12 @@
\setmonofont{Ubuntu Mono}
"""
}
+
+# Set canonical URL from the Read the Docs Domain
+html_baseurl = os.environ.get("READTHEDOCS_CANONICAL_URL", "")
+
+# Tell Jinja2 templates the build is running on Read the Docs
+if os.environ.get("READTHEDOCS", "") == "True":
+ if "html_context" not in globals():
+ html_context = {}
+ html_context["READTHEDOCS"] = True
diff --git a/docs/source/index.rst b/docs/source/index.rst
index a860edcd2b..7ad1a51da3 100644
--- a/docs/source/index.rst
+++ b/docs/source/index.rst
@@ -1,5 +1,5 @@
-Welcome to Pyrogram
-===================
+Welcome to ``PyroTGFork``
+==========================
.. admonition :: A Word of Warning
:class: tip
@@ -180,6 +180,8 @@ Meta
:hidden:
:caption: Telegram Raw API
+ TL Schema Explorer
+ TL Diff
telegram/functions/index
telegram/types/index
telegram/base/index
diff --git a/docs/source/releases/changes-in-this-fork.rst b/docs/source/releases/changes-in-this-fork.rst
index 00f63f4d43..636aff25ed 100644
--- a/docs/source/releases/changes-in-this-fork.rst
+++ b/docs/source/releases/changes-in-this-fork.rst
@@ -1,21 +1,126 @@
Changes in this Fork
-=========
+=====================
This page lists all the documented changes of this fork,
in reverse chronological order. You should read this when upgrading
to this fork to know where your code can break, and where
it can take advantage of new goodies!
-`For a more detailed description, please check the commits. `_
+`For a more detailed description, please check the commits. `_
+
+If you found any issue or have any suggestions, feel free to make `an issue `_ on github.
+
++------------------------+
+| Scheme layer used: 185 |
++------------------------+
+
+- Added the parameter ``chat_list`` to the methods :meth:`~pyrogram.Client.get_dialogs` and :meth:`~pyrogram.Client.get_dialogs_count`.
+- Added ``gifted_stars`` service message to the class :obj:`~pyrogram.types.Message`.
+- Added the fields ``have_access``, ``has_main_web_app``, ``active_user_count`` to the class :obj:`~pyrogram.types.User`, which is returned in response to :meth:`~pyrogram.Client.get_me`.
+- Added the parameter ``business_connection_id`` to the methods :meth:`~pyrogram.Client.pin_chat_message` and :meth:`~pyrogram.Client.unpin_chat_message`, allowing bots to manage pinned messages on behalf of a business account.
+- View `new and changed `__ `raw API methods `__.
+
+
++------------------------+
+| Scheme layer used: 184 |
++------------------------+
+
+- Updated :obj:`~pyrogram.filters.via_bot`, to optionally support filtering invalid bot ``user_id``.
+- Added the :meth:`~pyrogram.Client.get_active_sessions`, :meth:`~pyrogram.Client.terminate_session`, :meth:`~pyrogram.types.ActiveSession.terminate`, and :meth:`~pyrogram.Client.terminate_all_other_sessions`.
+- Added the ``is_automatic_forward`` to the :obj:`~pyrogram.types.Message`.
+- Added the parameters ``offset_id`` to the :meth:`~pyrogram.Client.search_messages` and the parameters ``min_date``, ``max_date``, ``min_id``, ``max_id``, ``saved_messages_topic_id`` to the :meth:`~pyrogram.Client.search_messages_count`.
+- Dynamic session ReStart + restart optimizations (`#56 `__)
+- Added the :meth:`~pyrogram.Client.delete_account`, :meth:`~pyrogram.Client.transfer_chat_ownership`, :meth:`~pyrogram.Client.update_status` (`#49 `__, `#51 `__)
+- Added the class :obj:`~pyrogram.types.RefundedPayment`, containing information about a refunded payment.
+- Added the field ``refunded_payment`` to the class :obj:`~pyrogram.types.Message`, describing a service message about a refunded payment.
+- `View new and changed raw API methods `__.
+
+
++------------------------+
+| Scheme layer used: 183 |
++------------------------+
+
+- Added the classes :obj:`~pyrogram.types.PaidMedia`, :obj:`~pyrogram.types.PaidMediaInfo`, :obj:`~pyrogram.types.PaidMediaPreview`, :obj:`~pyrogram.types.PaidMediaPhoto` and :obj:`~pyrogram.types.PaidMediaVideo`, containing information about paid media.
+- Added the method :meth:`~pyrogram.Client.send_paid_media` and the classes :obj:`~pyrogram.types.InputPaidMedia`, :obj:`~pyrogram.types.InputPaidMediaPhoto` and :obj:`~pyrogram.types.InputPaidMediaVideo`, to support sending paid media.
+- Added the field ``paid_media`` to the classes :obj:`~pyrogram.types.Message` and :obj:`~pyrogram.types.ExternalReplyInfo`.
+- Added :meth:`~pyrogram.Client.get_stories`.
+- Added filters :obj:`~pyrogram.filters.thread` and :obj:`~pyrogram.filters.self_destruct`.
+- Added the field ``can_send_paid_media`` to the class :obj:`~pyrogram.types.Chat`.
+- Added support for launching Web Apps via ``t.me`` link in the class :obj:`~pyrogram.types.MenuButtonWebApp`.
+- `View new and changed raw API methods `__.
+
++------------------------+
+| Scheme layer used: 182 |
++------------------------+
+
+- Updated the parameter ``business_connection_id`` to the methods :meth:`~pyrogram.types.Message.edit_text`, :meth:`~pyrogram.types.Message.edit_media`, :meth:`~pyrogram.types.Message.edit_reply_markup`, :meth:`~pyrogram.types.CallbackQuery.edit_message_text`, :meth:`~pyrogram.types.CallbackQuery.edit_message_media`, :meth:`~pyrogram.types.CallbackQuery.edit_message_reply_markup`.
+- Added the parameter ``business_connection_id`` to the methods :meth:`~pyrogram.Client.edit_message_text`, :meth:`~pyrogram.Client.edit_message_media`, :meth:`~pyrogram.Client.edit_cached_media`, :meth:`~pyrogram.Client.edit_message_caption` and :meth:`~pyrogram.Client.edit_message_reply_markup`, allowing the bot to edit business messages.
+- Added the parameter ``business_connection_id`` to the method :meth:`~pyrogram.Client.stop_poll`, allowing the bot to stop polls it sent on behalf of a business account.
+- Added support for callback queries originating from a message sent on behalf of a business account.
+
++------------------------+
+| Scheme layer used: 181 |
++------------------------+
+
+- Added the classes :obj:`~pyrogram.types.InputLocationMessageContent`, :obj:`~pyrogram.types.InputVenueMessageContent`, :obj:`~pyrogram.types.InputContactMessageContent`, :obj:`~pyrogram.types.InputInvoiceMessageContent`.`
+- Added ``background`` to :obj:`~pyrogram.types.Chat` (`#40 `_)
+- Added the methods :meth:`~pyrogram.Client.translate_text`, :meth:`~pyrogram.Client.translate_message_text`, :meth:`~pyrogram.types.Message.translate` and the type :obj:`~pyrogram.types.TranslatedText` (`#39 `_).
+- Added the methods :meth:`~pyrogram.Client.create_video_chat`, :meth:`~pyrogram.Client.discard_group_call`, :meth:`~pyrogram.Client.get_video_chat_rtmp_url` and the type :obj:`~pyrogram.types.RtmpUrl` (`#37 `_).
+- Added :meth:`~Client.on_story` to listen to story updates.
+- Ability to run in `replit` environment without creating `a deployment `_. Set the environment variable ``PYROGRAM_REPLIT_NWTRAFIK_PORT`` value to ``5222`` if you want to connect to Production Telegram Servers, **OR** Set the environment variable ``PYROGRAM_REPLIT_WNTRAFIK_PORT`` value to ``5223`` if you want to connect to Test Telegram Servers, before starting the :obj:`~pyrogram.Client`.
+- Added the :meth:`~pyrogram.Client.invite_group_call_participants` (`#35 `_).
+- Added the types :obj:`~pyrogram.types.LabeledPrice`, :obj:`~pyrogram.types.OrderInfo`, :obj:`~pyrogram.types.PreCheckoutQuery`, :obj:`~pyrogram.types.ShippingAddress`, :obj:`~pyrogram.types.ShippingOption`, :obj:`~pyrogram.types.ShippingQuery` and :obj:`~pyrogram.types.SuccessfulPayment`.
+- Added the ``successful_payment`` parameter to the :obj:`~pyrogram.types.Message`. Added the filter :obj:`~pyrogram.filters.successful_payment` to detect service messages of Successful Payment type.
+- Added the methods :meth:`~pyrogram.Client.send_invoice`, :meth:`~pyrogram.Client.answer_pre_checkout_query` (:meth:`~pyrogram.types.PreCheckoutQuery.answer`), :meth:`~pyrogram.Client.answer_shipping_query` (:meth:`~pyrogram.types.ShippingQuery.answer`), :meth:`~pyrogram.Client.refund_star_payment` and :meth:`~pyrogram.Client.create_invoice_link`.
+- Added the :meth:`~pyrogram.Client.send_web_app_custom_request`.
+- Added the :meth:`~pyrogram.Client.search_public_messages_by_tag` and :meth:`~pyrogram.Client.count_public_messages_by_tag`.
+- Added the ``fetch_replies`` parameter to :obj:`~pyrogram.Client`.
+- Added the :meth:`~pyrogram.Client.get_message_effects`.
+- Added the parameter ``message_effect_id`` to the methods :meth:`~pyrogram.Client.send_message`, :meth:`~pyrogram.Client.send_photo`, :meth:`~pyrogram.Client.send_video`, :meth:`~pyrogram.Client.send_animation`, :meth:`~pyrogram.Client.send_audio`, :meth:`~pyrogram.Client.send_document`, :meth:`~pyrogram.Client.send_sticker`, :meth:`~pyrogram.Client.send_video_note`, :meth:`~pyrogram.Client.send_voice`, :meth:`~pyrogram.Client.send_location`, :meth:`~pyrogram.Client.send_venue`, :meth:`~pyrogram.Client.send_contact`, :meth:`~pyrogram.Client.send_poll`, :meth:`~pyrogram.Client.send_dice`, :meth:`~pyrogram.Client.send_game`, and :meth:`~pyrogram.Client.send_media_group`, and the corresponding ``reply_*`` methods in the class :obj:`~pyrogram.types.Message`.
+- Added the field ``effect_id`` to the class :obj:`~pyrogram.types.Message`.
+- Added the field ``show_caption_above_media`` to the classes :obj:`~pyrogram.types.Message`, :obj:`~pyrogram.types.InputMediaAnimation`, :obj:`~pyrogram.types.InputMediaPhoto`, :obj:`~pyrogram.types.InputMediaVideo`, :obj:`~pyrogram.types.InlineQueryResultAnimation`, :obj:`~pyrogram.types.InlineQueryResultCachedAnimation`, :obj:`~pyrogram.types.InlineQueryResultPhoto`, :obj:`~pyrogram.types.InlineQueryResultCachedPhoto`, :obj:`~pyrogram.types.InlineQueryResultVideo`, :obj:`~pyrogram.types.InlineQueryResultCachedVideo`, :meth:`~pyrogram.Client.send_cached_media`, :meth:`~pyrogram.Client.send_animation`, :meth:`~pyrogram.Client.send_photo`, :meth:`~pyrogram.Client.send_video`, :meth:`~pyrogram.Client.copy_message` and :meth:`~pyrogram.Client.edit_message_caption`, and the corresponding ``reply_*`` methods.
+- Added support for :obj:`~pyrogram.enums.MessageEntityType.EXPANDABLE_BLOCKQUOTE` entities in received messages.
+- Added support for :obj:`~pyrogram.enums.MessageEntityType.EXPANDABLE_BLOCKQUOTE` entity parsing in :obj:`~pyrogram.enums.ParseMode.HTML` parse mode.
+- Allowed to explicitly specify :obj:`~pyrogram.enums.MessageEntityType.EXPANDABLE_BLOCKQUOTE` entities in formatted texts.
+- `View new and changed raw API methods `__.
+
++------------------------+
+| Scheme layer used: 179 |
++------------------------+
+
+- Add ``invoice`` to :obj:`~pyrogram.types.Message` and :obj:`~pyrogram.types.ExternalReplyInfo`.
+- Add ``link_preview_options`` to :obj:`~pyrogram.Client`.
+- Support for the updated Channel ID format. `#28 `_
+- Improvements to :meth:`~pyrogram.Client.save_file` and :meth:`~pyrogram.Client.get_file` to handle the new `FLOOD_PREMIUM_WAIT `_ errors.
+- Added ``has_animation``, ``is_personal``, ``minithumbnail`` parameters to :obj:`~pyrogram.types.ChatPhoto`.
+- Changed return type of :meth:`~pyrogram.Client.get_chat_photos` to return :obj:`~pyrogram.types.Photo` or :obj:`~pyrogram.types.Animation`.
+- Added :meth:`~pyrogram.Client.get_chat_sponsored_messages` and the type :obj:`~pyrogram.types.SponsoredMessage`, by stealing unauthored changes from `KurimuzonAkuma/pyrogram#55 `_.
+- Added :meth:`~pyrogram.Client.load_group_call_participants` and the type :obj:`~pyrogram.types.GroupCallParticipant`, by stealing unauthored changes from `6df467f `_.
+- Added :meth:`~pyrogram.Client.view_messages` and the bound methods :meth:`~pyrogram.types.Message.read` and :meth:`~pyrogram.types.Message.view`.
+- Added the field ``question_entities`` to the class :obj:`~pyrogram.types.Poll`.
+- Added the field ``text_entities`` to the class :obj:`~pyrogram.types.PollOption`.
+- Added the parameters ``question_parse_mode`` and ``question_entities`` to the method :meth:`~pyrogram.Client.send_poll`.
+- Added the class :obj:`~pyrogram.types.InputPollOption` and changed the type of the parameter ``options`` in the method :meth:`~pyrogram.Client.send_poll` to Array of :obj:`~pyrogram.types.InputPollOption`.
+- Added the field ``max_reaction_count`` to the class :obj:`~pyrogram.types.Chat`.
+- Added the field ``via_join_request`` to the class :obj:`~pyrogram.types.ChatMemberUpdated`.
+- Added the class :obj:`~pyrogram.types.TextQuote` and the field ``quote`` of type :obj:`~pyrogram.types.TextQuote` to the class :obj:`~pyrogram.types.Message`, which contains the part of the replied message text or caption that is quoted in the current message.
+- Added ``full_name`` to :obj:`~pyrogram.types.Chat` and :obj:`~pyrogram.types.User` only for :obj:`~pyrogram.enums.ChatType.PRIVATE`.
+- Added ``revoke_messages`` parameter to :meth:`~pyrogram.Client.ban_chat_member` and :meth:`~pyrogram.types.Chat.ban_member`.
+- Added :meth:`~pyrogram.Client.get_collectible_item_info`.
+- Added ``reverse`` parameter to :meth:`~pyrogram.Client.get_chat_history`. (`855e69e `_, `a086b49 `_)
+- `View new and changed raw API methods `__.
+------------------------+
| Scheme layer used: 178 |
+------------------------+
+- Added :meth:`~pyrogram.Client.search_chats`.
+- Added :meth:`~pyrogram.Client.get_bot_name`, :meth:`~pyrogram.Client.get_bot_info_description`, :meth:`~pyrogram.Client.get_bot_info_short_description`, :meth:`~pyrogram.Client.set_bot_name`, :meth:`~pyrogram.Client.set_bot_info_description`, :meth:`~pyrogram.Client.set_bot_info_short_description`.
+- Added :meth:`~pyrogram.Client.edit_cached_media` and :meth:`~pyrogram.types.Message.edit_cached_media`.
- Steal `d51eef3 `_ without attribution.
-- Added ``limit`` to :obj:`~pyrogram.types.ChatReactions`.
+- Added ``max_reaction_count`` to :obj:`~pyrogram.types.ChatReactions`.
- Added ``personal_chat_message`` to :obj:`~pyrogram.types.Chat`.
-- Added ``is_channel`` parameter to :meth:`~pyrogram.Client.search_global` and :meth:`~pyrogram.Client.search_global_count`.
+- Added ``only_in_channels`` parameter to :meth:`~pyrogram.Client.search_global` and :meth:`~pyrogram.Client.search_global_count`.
+------------------------+
| Scheme layer used: 177 |
@@ -101,17 +206,11 @@ it can take advantage of new goodies!
| PmOItrOAe |
+-------------+
-- Renamed ``placeholder`` to ``input_field_placeholder`` in :obj:`~pyrogram.types.ForceReply`.
+- Renamed ``placeholder`` to ``input_field_placeholder`` in :obj:`~pyrogram.types.ForceReply` and :obj:`~pyrogram.types.ReplyKeyboardMarkup`.
- Add ``link`` parameter in :meth:`~pyrogram.Client.get_messages`
- `fix(filters): add type hints in filters.py `_
- Documentation Builder Fixes
-- `faster-pyrogram `_ is not polished or documented for anyone else's use. We don't have the capacity to support `faster-pyrogram `_ as an independent open-source project, nor any desire for it to become an alternative to Pyrogram. Our goal in making this code available is a unified faster Pyrogram. `... `_
- - Lock-free and asynchronous implementation of the sqlite session.
- - The possibility of turning off journaling and vacuum when starting a session.
- - Improved implementation of rle_encode.
- - Implementation of _parse_channel_chat without getattr, in some scenarios.
- - Cache of FileId and UniqueFileId instances and of their string-coded versions.
- - Use of tcp abridged instead of tcp obfuscated as the default protocol.
+- `faster-pyrogram `__ is not polished or documented for anyone else's use. We don't have the capacity to support `faster-pyrogram `__ as an independent open-source project, nor any desire for it to become an alternative to Pyrogram. Our goal in making this code available is a unified faster Pyrogram. `... `__
+-----------------------------+
| Leaked Scheme Layers (2) |
@@ -131,7 +230,6 @@ it can take advantage of new goodies!
- `#1345 `_
- `Add undocumented things `_
- `Add missing enums.SentCodeType `_
-- Renamed ``placeholder`` to ``input_field_placeholder`` in :obj:`~pyrogram.types.ReplyKeyboardMarkup`
- `#693 `_
- Revert `e678c05 `_ and stole squashed unauthored changes from `bcd18d5 `_
@@ -160,7 +258,7 @@ it can take advantage of new goodies!
- `Bug Fix for MIN_CHAT_ID `_.
- Added new parameter ``no_joined_notifications`` to :obj:`~pyrogram.Client`.
- Fix history TTL Service Message Parse.
-- Added environment variables ``PYROGRAM_DONOT_LOG_UNKNOWN_ERRORS``. Thanks to `... `_.
+- Thanks to `... `_. If you want to change the location of the ``unknown_errors.txt`` file that is created by :obj:`~pyrogram.Client`, set the environment variable ``PYROGRAM_LOG_UNKNOWN_ERRORS_FILENAME`` value to the path where the file should get created.
- Renamed ``force_document`` to ``disable_content_type_detection`` in :meth:`~pyrogram.Client.send_document` and :meth:`~pyrogram.types.Message.reply_document`.
- Added missing attributes ``added_to_attachment_menu``, ``can_be_added_to_attachment_menu``, ``can_join_groups``, ``can_read_all_group_messages``, ``supports_inline_queries``, ``restricts_new_chats`` to the :obj:`~pyrogram.types.User`.
- Migrate project to ``pyproject.toml`` from ``setup.py``.
@@ -168,7 +266,7 @@ it can take advantage of new goodies!
- Bug fix for :meth:`~pyrogram.Client.send_audio` and :meth:`~pyrogram.Client.send_voice`. (Thanks to `... `_).
- Add `waveform` parameter to :meth:`~pyrogram.Client.send_voice`.
- Added `view_once` parameter to :meth:`~pyrogram.Client.send_photo`, :meth:`~pyrogram.Client.send_video`, :meth:`~pyrogram.Client.send_video_note`, :meth:`~pyrogram.Client.send_voice`.
-- Add missing parameters to :obj:`~pyrogram.types.Message.reply_photo`, :obj:`~pyrogram.types.Message.reply_video`, :obj:`~pyrogram.types.Message.reply_video_note`, :obj:`~pyrogram.types.Message.reply_voice`.
+- Add missing parameters to :meth:`~pyrogram.types.Message.reply_photo`, :meth:`~pyrogram.types.Message.reply_video`, :meth:`~pyrogram.types.Message.reply_video_note`, :meth:`~pyrogram.types.Message.reply_voice`.
+------------------------+
| Scheme layer used: 170 |
@@ -177,7 +275,6 @@ it can take advantage of new goodies!
- Stole documentation from `PyrogramMod `_.
- Renamed ``send_reaction`` to :meth:`~pyrogram.Client.set_reaction`.
- Added support for :meth:`~pyrogram.Client.send_photo`, :meth:`~pyrogram.Client.send_video`, :meth:`~pyrogram.Client.send_animation`, :meth:`~pyrogram.Client.send_voice` messages that could be played once.
-- Added ``_raw`` to the :obj:`~pyrogram.types.Chat` object.
- Added the field ``via_chat_folder_invite_link`` to the class :obj:`~pyrogram.types.ChatMemberUpdated`.
- **BOTS ONLY**: Added updates about a reaction change on a message with non-anonymous reactions, represented by the class :obj:`~pyrogram.handlers.MessageReactionUpdatedHandler` and the field ``message_reaction`` in the class Update.
- **BOTS ONLY**: Added updates about reaction changes on a message with anonymous reactions, represented by the class :obj:`~pyrogram.handlers.MessageReactionCountUpdatedHandler` and the field ``message_reaction_count`` in the class Update.
@@ -192,12 +289,11 @@ it can take advantage of new goodies!
+------------------------+
- Changed condition in :meth:`~pyrogram.Client.join_chat` and :meth:`~pyrogram.Client.get_chat`.
-- Added ``nosound_video`` parameter to :obj:`~pyrogram.types.InputMediaVideo`.
+- Added ``disable_content_type_detection`` parameter to :obj:`~pyrogram.types.InputMediaVideo`.
- Added ``has_spoiler`` parameter to :meth:`~pyrogram.Client.copy_message`.
- Improved :meth:`~pyrogram.Client.get_chat_history`: add ``min_id`` and ``max_id`` params.
-- Improved ``set_reaction`` for Telegram Premium Users.
- `Prevent connection to dc every time in get_file `_
-- Added ``_raw`` to the :obj:`~pyrogram.types.Chat`, :obj:`~pyrogram.types.Dialog`, and :obj:`~pyrogram.types.User` objects.
+- Added ``_raw`` to the :obj:`~pyrogram.types.Chat`, :obj:`~pyrogram.types.Dialog`, :obj:`~pyrogram.types.Message` and :obj:`~pyrogram.types.User` objects.
- Fix downloading media to ``WORKDIR`` when ``WORKDIR`` was not specified.
- `Update multiple fragment chat usernames `_
- `Custom Storage Engines `_
@@ -209,12 +305,6 @@ it can take advantage of new goodies!
- Fixed the TL flags being Python reserved keywords: ``from`` and ``self``.
-+------------------------+
-| Scheme layer used: 164 |
-+------------------------+
-
-- Added ``_raw`` to the :obj:`~pyrogram.types.Message` object.
-
+------------------------+
| Scheme layer used: 161 |
+------------------------+
diff --git a/docs/source/releases/index.rst b/docs/source/releases/index.rst
index 07af2cd70a..0bfbafd4e4 100644
--- a/docs/source/releases/index.rst
+++ b/docs/source/releases/index.rst
@@ -1,6 +1,6 @@
-=========
+==============
Release Notes
-=========
+==============
Release notes for Pyrogram releases will describe what's new in each version, and will also make you aware of any backwards-incompatible changes made in that version.
diff --git a/docs/source/requirements.txt b/docs/source/requirements.txt
new file mode 100644
index 0000000000..dc06c27f17
--- /dev/null
+++ b/docs/source/requirements.txt
@@ -0,0 +1,5 @@
+Sphinx<=7.2.6
+furo<=2023.9.10
+sphinx-autobuild<=2021.3.14
+sphinx-copybutton<=0.5.2
+pygments<=2.17.2
diff --git a/docs/source/start/errors.rst b/docs/source/start/errors.rst
index 4a86fbbe5f..f6346fd1b8 100644
--- a/docs/source/start/errors.rst
+++ b/docs/source/start/errors.rst
@@ -77,6 +77,16 @@ In case Pyrogram does not know anything about a specific error yet, it raises a
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.
+.. admonition :: RPC Errors
+ :class: tip
+
+ There isn't any official list of all possible RPC errors, so the list of known errors is provided on a best-effort basis. When new methods are available, the list may be lacking since we simply don't know what errors can raise from them. Pyrogram creates an `unknown_errors.txt` file in the root directory from where the `Client` is run.
+
+.. admonition :: `... `__
+
+ If you want the file to be created in a different location, set the ``PYROGRAM_LOG_UNKNOWN_ERRORS_FILENAME`` to an absolute file path of your choice.
+
+
Errors with Values
------------------
diff --git a/docs/source/start/examples/tg_business_echo_bot.rst b/docs/source/start/examples/tg_business_echo_bot.rst
index 205aa68589..1ebd2f4fe0 100644
--- a/docs/source/start/examples/tg_business_echo_bot.rst
+++ b/docs/source/start/examples/tg_business_echo_bot.rst
@@ -1,5 +1,5 @@
business_echo_bot
-========
+==================
This simple echo bot replies to every private business message.
diff --git a/docs/source/static/css/all.min.css b/docs/source/static/css/all.min.css
index 64bbdcc3a0..2bb265353d 100644
--- a/docs/source/static/css/all.min.css
+++ b/docs/source/static/css/all.min.css
@@ -1 +1 @@
-@import "https://fonts.googleapis.com/css2?family=Bitter:wght@400;600&family=Open+Sans:ital,wght@0,400;0,600;1,400;1,600&family=Ubuntu+Mono:ital,wght@0,400;0,700;1,400;1,700&display=swap";@font-face{font-family:taratype;src:url(/pyrogram/_static/fonts/TaraType.ttf) format("truetype")}:root{--border-radius: 0.5em}body{font-family:open sans,sans-serif;--color-brand-primary: #404040;--color-brand-content: #93774b;--color-foreground-primary: #404040;--color-background-primary: #f9f8f7;--color-background-secondary: #f3f2f1;--color-admonition-background: #eeeeee;--color-table-border: #e1e4e5;--color-background-border: #dddddd;--color-guilabel-background: #f3f2f1;--color-guilabel-border: #dddddd;--color-admonition-title--note: #93774b;--color-admonition-title-background--note: #e4e0d7;--color-admonition-title--seealso: #93774b;--color-admonition-title-background--seealso: #e4e0d7;--color-border: #dddddd;--color-button: #f3f6f6;--color-background-highlight: #f3f2f1;--color-title: #303030;--color-link-hover: #bf431d;--color-carbon-ads-link: #606060;--color-carbon-ads-text: #637381;--color-carbon-ads-poweredby: #d0d0d0;--color-carbon-ads-background: #f7f6f5;--color-carbon-ads-border: #dddddd;--color-pyrogram-text: #3b3936;--color-support-pyrogram: #5e594c;--color-strong: #404040;--color-green: darkgreen;--color-red: darkred;--font-size--small: 100%;--font-size--small--2: 97%;--font-size--small--3: 94%;--font-size--small--4: 91%;--header-height: calc(var(--sidebar-item-line-height) + var(--sidebar-item-spacing-vertical)*6);-webkit-font-smoothing:auto;font-size:16px}@media(prefers-color-scheme:dark){body:not([data-theme=light]){--color-brand-primary: #bfbcb9;--color-brand-content: #b7a280;--color-foreground-primary: #bfbcb9;--color-background-primary: #202020;--color-background-secondary: #181818;--color-admonition-background: #282828;--color-table-border: #707070;--color-guilabel-background: #323232;--color-guilabel-border: #505050;--color-admonition-title--note: #b7a280;--color-admonition-title-background--note: #303030;--color-admonition-title--seealso: #b7a280;--color-admonition-title-background--seealso: #303030;--color-border: #505050;--color-button: #303030;--color-background-highlight: #323232;--color-title: #b9b9b9;--color-link-hover: #e85823;--color-carbon-ads-link: #aaaaaa;--color-carbon-ads-text: #bfbcb9;--color-carbon-ads-poweredby: #505050;--color-carbon-ads-background: #323232;--color-carbon-ads-border: #505050;--color-pyrogram-text: #b9b9b9;--color-support-pyrogram: #999999;--color-strong: #cfccc9;--color-green: green;--color-red: red}}h1,h2,h3,h4,h5,h6{font-family:Bitter,serif;font-weight:600;color:var(--color-title)}h1{font-size:175%}h2{font-size:150%}h3{font-size:125%}h4{font-size:115%}h5{font-size:110%}h6{font-size:100%}article[role=main] .highlight pre{font-family:ubuntu mono,monospace;font-size:var(--font-size--normal);line-height:1.2;border:1px solid var(--color-border);border-radius:var(--border-radius);background-color:var(--color-background-highlight)}.highlight{border-radius:var(--border-radius)}p code.literal{padding:0 4px;background-color:var(--color-background-highlight);border-color:var(--color-border);border-width:0;border-radius:.5em}p code.xref.literal{border-width:1px}.admonition{border-radius:var(--border-radius);font-size:var(--font-size--normal);border-left:0}p.admonition-title{font-size:var(--font-size--normal);font-weight:700}p.admonition-title:before{height:21px;width:21px}.related-pages a{font-size:var(--font-size--normal);background-color:var(--color-button);border-radius:var(--border-radius);border:1px solid rgba(0,0,0,.1);box-shadow:inset 0 1px 2px -1px hsl(0deg 0% 100%/50%),inset 0 -2px 0 0 rgb(0 0 0/10%)}.related-pages a.prev-page{padding:6px 10px}.related-pages a.next-page{padding:6px 10px}.page-info .context{font-size:18px;color:var(--color-foreground-primary)}.sidebar-container{width:18em}@media(max-width:67em){.sidebar-drawer{width:18em;left:-18em}}.toc-drawer{width:18em}@media(max-width:82em){.toc-drawer{right:-18em}}table.docutils{border:1px solid var(--color-table-border);border-radius:var(--border-radius);border-collapse:separate;overflow:hidden;width:100%}table.docutils th{border-right:none}table.docutils td{border-bottom-width:1px;border-left-width:1px;border-top-width:0;border-right-width:0}table.docutils td p{padding:5px}table.docutils:not(.field-list) tr:nth-child(2n-1) td{background-color:var(--color-background-highlight)}table.docutils tbody>td:first-child{border-left-width:0}table.docutils tbody>tr:last-child td{border-bottom-width:0}table.docutils th:last-child{width:100%}table>caption{font-size:24px;font-weight:700;font-family:bitter;border-bottom:1px solid var(--color-table-border);padding:5px}dt{text-transform:none!important;background-color:var(--color-background-highlight)!important;border-radius:var(--border-radius)!important;padding:2px 8px;width:fit-content;border-left:2px solid var(--color-border)!important}.sig-prename,.sig-name{color:var(--color-foreground-primary)}.banner{border-radius:var(--border-radius);border:1px solid #888;color:#888;padding:15px;margin:20px auto;text-align:center;font-size:20px;width:auto;background-image:repeating-linear-gradient(135deg,rgba(128,128,128,0.1),transparent 10%)}.top-bar{display:flex;align-items:center;justify-content:space-between;margin-top:20px}.github-buttons{display:flex}.github-button{border-radius:var(--border-radius);overflow:hidden;margin-left:10px;border:1px solid var(--color-border);border-spacing:0}.github-button td{padding:4px 8px;border-left:1px solid var(--color-border);width:100%;height:100%}.github-button td:first-child{border-left-width:0;background-color:var(--color-background-highlight)}.horizontal-line{margin-top:25px;border-top:1px solid var(--color-background-border)}@media(max-width:67em){.top-bar{display:flex;flex-direction:column;align-items:flex-end}.view-on-github{margin-bottom:10px}}.index-logo{margin-bottom:-30px}.sidebar-brand{margin-bottom:10px;padding:0 2px}.sidebar-logo-container{margin-top:20px;margin-bottom:0}.sidebar-logo{width:48px;display:inline}.pyrogram-text{font-family:TaraType,sans-serif;font-size:calc(var(--font-size--normal) * 3);color:var(--color-pyrogram-text);letter-spacing:.0425em;position:relative;top:-15px;font-weight:700;display:inline}.pyrogram-text-index{font-size:72px;position:relative}.pyrogram-logo-index{display:inline}.pyrogram-logo-index img{width:72px}.support-pyrogram{color:var(--color-support-pyrogram);border:1px solid var(--color-support-pyrogram);border-radius:var(--border-radius);width:fit-content;margin:auto;user-select:none;padding:6px 12px;margin-bottom:10px}.support-pyrogram:hover{background-color:rgba(255,255,255,.05)}.pyrogram-version{border-top:1px solid var(--color-background-border);border-bottom:1px solid var(--color-background-border);border-radius:0;padding:10px;margin-top:10px;margin-bottom:20px;text-align:center;color:var(--color-foreground-muted)}.ca-wrap{text-align:center;margin-top:20px}#carbonads{display:inline-block;overflow:hidden;padding:6px;line-height:1.25;max-width:330px;font-size:14px;border:1px var(--color-carbon-ads-border) solid;background-color:var(--color-carbon-ads-background);border-radius:var(--border-radius)}#carbonads a{color:var(--color-carbon-ads-link);text-decoration:none}#carbonads span{position:relative;display:block;overflow:hidden;border-radius:0}.carbon-img{float:left;margin-right:1em}.carbon-img img{display:block;border-radius:var(--border-radius)}.carbon-text{display:block;float:left;max-width:calc(100% - 128px - 1em);text-align:left;color:var(--color-carbon-ads-text);margin-left:-6px}.carbon-poweredby{position:absolute;left:calc(100% - 74px - 1em);bottom:0;display:block;font-size:12px;color:var(--color-carbon-ads-poweredby);font-weight:500}#carbonads a.carbon-poweredby{color:var(--color-carbon-ads-poweredby)}.footer{display:flex;align-items:center;justify-content:space-between;font-size:var(--font-size--normal)}.right{float:right}.left{float:left}.footer-logo{display:flex;max-height:80px;padding-right:5px}.copyright{display:flex;align-items:center}.links a{padding-left:5px}.mobile-header{font-family:TaraType,monospace;font-weight:700;letter-spacing:.0425em}.mobile-header .header-center img{width:38px}.mobile-header .header-center a{display:flex;align-items:flex-end}@media(max-width:67em){.mobile-header{align-items:center}.mobile-header .header-right{padding-top:10px;align-items:baseline;margin-right:20px;height:unset}.mobile-header .header-left{align-items:baseline;margin-left:10px;height:unset}}.mobile-header-title{position:absolute;margin-left:auto;margin-right:auto;width:fit-content;left:0;right:0;text-align:center;margin-top:10px}.mobile-header-title a{color:var(--color-foreground-primary)}.theme-toggle svg{width:1.75rem;height:1.75rem}.icon svg{width:1.75rem;height:1.75rem}.sidebar-tree label{margin-right:10px}blockquote{border-radius:var(--border-radius)}.bottom-of-page{font-size:var(--font-size--normal)}a{text-decoration:none}a:hover,a.reference:hover,.sidebar-tree .toctree-l1>.reference:hover{color:var(--color-link-hover)}b,strong{color:var(--color-strong)}.content{justify-content:unset}.sidebar-tree .current>.reference::before{content:"➤ "}.toc-tree .reference::before{content:"• "}.toc-tree li.scroll-current>.reference:before{content:"➤ "}table.hlist{margin-top:-20px}.highlight button.copybtn svg{color:var(--color-foreground-primary)}.toc-tree .reference{color:var(--color-brand-primary)}ul.breadcrumbs{margin-bottom:0;padding:0;list-style:none;font-size:18px}ul.breadcrumbs li{display:inline}.usable-by{border:1px solid var(--color-border);border-radius:var(--border-radius);padding:2px 5px;background-color:var(--color-background-highlight)}.content-icon-container{margin-top:1.9rem;margin-bottom:0}
\ No newline at end of file
+@import "https://fonts.googleapis.com/css2?family=Bitter:wght@400;600&family=Open+Sans:ital,wght@0,400;0,600;1,400;1,600&family=Ubuntu+Mono:ital,wght@0,400;0,700;1,400;1,700&display=swap";@font-face{font-family:taratype;src:url(./../fonts/TaraType.ttf) format("truetype")}:root{--border-radius: 0.5em}body{font-family:open sans,sans-serif;--color-brand-primary: #404040;--color-brand-content: #93774b;--color-foreground-primary: #404040;--color-background-primary: #f9f8f7;--color-background-secondary: #f3f2f1;--color-admonition-background: #eeeeee;--color-table-border: #e1e4e5;--color-background-border: #dddddd;--color-guilabel-background: #f3f2f1;--color-guilabel-border: #dddddd;--color-admonition-title--note: #93774b;--color-admonition-title-background--note: #e4e0d7;--color-admonition-title--seealso: #93774b;--color-admonition-title-background--seealso: #e4e0d7;--color-border: #dddddd;--color-button: #f3f6f6;--color-background-highlight: #f3f2f1;--color-title: #303030;--color-link-hover: #bf431d;--color-carbon-ads-link: #606060;--color-carbon-ads-text: #637381;--color-carbon-ads-poweredby: #d0d0d0;--color-carbon-ads-background: #f7f6f5;--color-carbon-ads-border: #dddddd;--color-pyrogram-text: #3b3936;--color-support-pyrogram: #5e594c;--color-strong: #404040;--color-green: darkgreen;--color-red: darkred;--font-size--small: 100%;--font-size--small--2: 97%;--font-size--small--3: 94%;--font-size--small--4: 91%;--header-height: calc(var(--sidebar-item-line-height) + var(--sidebar-item-spacing-vertical)*6);-webkit-font-smoothing:auto;font-size:16px}@media(prefers-color-scheme:dark){body:not([data-theme=light]){--color-brand-primary: #bfbcb9;--color-brand-content: #b7a280;--color-foreground-primary: #bfbcb9;--color-background-primary: #202020;--color-background-secondary: #181818;--color-admonition-background: #282828;--color-table-border: #707070;--color-guilabel-background: #323232;--color-guilabel-border: #505050;--color-admonition-title--note: #b7a280;--color-admonition-title-background--note: #303030;--color-admonition-title--seealso: #b7a280;--color-admonition-title-background--seealso: #303030;--color-border: #505050;--color-button: #303030;--color-background-highlight: #323232;--color-title: #b9b9b9;--color-link-hover: #e85823;--color-carbon-ads-link: #aaaaaa;--color-carbon-ads-text: #bfbcb9;--color-carbon-ads-poweredby: #505050;--color-carbon-ads-background: #323232;--color-carbon-ads-border: #505050;--color-pyrogram-text: #b9b9b9;--color-support-pyrogram: #999999;--color-strong: #cfccc9;--color-green: green;--color-red: red}}h1,h2,h3,h4,h5,h6{font-family:Bitter,serif;font-weight:600;color:var(--color-title)}h1{font-size:175%}h2{font-size:150%}h3{font-size:125%}h4{font-size:115%}h5{font-size:110%}h6{font-size:100%}article[role=main] .highlight pre{font-family:ubuntu mono,monospace;font-size:var(--font-size--normal);line-height:1.2;border:1px solid var(--color-border);border-radius:var(--border-radius);background-color:var(--color-background-highlight)}.highlight{border-radius:var(--border-radius)}p code.literal{padding:0 4px;background-color:var(--color-background-highlight);border-color:var(--color-border);border-width:0;border-radius:.5em}p code.xref.literal{border-width:1px}.admonition{border-radius:var(--border-radius);font-size:var(--font-size--normal);border-left:0}p.admonition-title{font-size:var(--font-size--normal);font-weight:700}p.admonition-title:before{height:21px;width:21px}.related-pages a{font-size:var(--font-size--normal);background-color:var(--color-button);border-radius:var(--border-radius);border:1px solid rgba(0,0,0,.1);box-shadow:inset 0 1px 2px -1px hsl(0deg 0% 100%/50%),inset 0 -2px 0 0 rgb(0 0 0/10%)}.related-pages a.prev-page{padding:6px 10px}.related-pages a.next-page{padding:6px 10px}.page-info .context{font-size:18px;color:var(--color-foreground-primary)}.sidebar-container{width:18em}@media(max-width:67em){.sidebar-drawer{width:18em;left:-18em}}.toc-drawer{width:18em}@media(max-width:82em){.toc-drawer{right:-18em}}table.docutils{border:1px solid var(--color-table-border);border-radius:var(--border-radius);border-collapse:separate;overflow:hidden;width:100%}table.docutils th{border-right:none}table.docutils td{border-bottom-width:1px;border-left-width:1px;border-top-width:0;border-right-width:0}table.docutils td p{padding:5px}table.docutils:not(.field-list) tr:nth-child(2n-1) td{background-color:var(--color-background-highlight)}table.docutils tbody>td:first-child{border-left-width:0}table.docutils tbody>tr:last-child td{border-bottom-width:0}table.docutils th:last-child{width:100%}table>caption{font-size:24px;font-weight:700;font-family:bitter;border-bottom:1px solid var(--color-table-border);padding:5px}dt{text-transform:none!important;background-color:var(--color-background-highlight)!important;border-radius:var(--border-radius)!important;padding:2px 8px;width:fit-content;border-left:2px solid var(--color-border)!important}.sig-prename,.sig-name{color:var(--color-foreground-primary)}.banner{border-radius:var(--border-radius);border:1px solid #888;color:#888;padding:15px;margin:20px auto;text-align:center;font-size:20px;width:auto;background-image:repeating-linear-gradient(135deg,rgba(128,128,128,0.1),transparent 10%)}.top-bar{display:flex;align-items:center;justify-content:space-between;margin-top:20px}.github-buttons{display:flex}.github-button{border-radius:var(--border-radius);overflow:hidden;margin-left:10px;border:1px solid var(--color-border);border-spacing:0}.github-button td{padding:4px 8px;border-left:1px solid var(--color-border);width:100%;height:100%}.github-button td:first-child{border-left-width:0;background-color:var(--color-background-highlight)}.horizontal-line{margin-top:25px;border-top:1px solid var(--color-background-border)}@media(max-width:67em){.top-bar{display:flex;flex-direction:column;align-items:flex-end}.view-on-github{margin-bottom:10px}}.index-logo{margin-bottom:-30px}.sidebar-brand{margin-bottom:10px;padding:0 2px}.sidebar-logo-container{margin-top:20px;margin-bottom:0}.sidebar-logo{width:48px;display:inline}.pyrogram-text{font-family:TaraType,sans-serif;font-size:calc(var(--font-size--normal) * 3);color:var(--color-pyrogram-text);letter-spacing:.0425em;position:relative;top:-15px;font-weight:700;display:inline}.pyrogram-text-index{font-size:72px;position:relative}.pyrogram-logo-index{display:inline}.pyrogram-logo-index img{width:72px}.support-pyrogram{color:var(--color-support-pyrogram);border:1px solid var(--color-support-pyrogram);border-radius:var(--border-radius);width:fit-content;margin:auto;user-select:none;padding:6px 12px;margin-bottom:10px}.support-pyrogram:hover{background-color:rgba(255,255,255,.05)}.pyrogram-version{border-top:1px solid var(--color-background-border);border-bottom:1px solid var(--color-background-border);border-radius:0;padding:10px;margin-top:10px;margin-bottom:20px;text-align:center;color:var(--color-foreground-muted)}.ca-wrap{text-align:center;margin-top:20px}#carbonads{display:inline-block;overflow:hidden;padding:6px;line-height:1.25;max-width:330px;font-size:14px;border:1px var(--color-carbon-ads-border) solid;background-color:var(--color-carbon-ads-background);border-radius:var(--border-radius)}#carbonads a{color:var(--color-carbon-ads-link);text-decoration:none}#carbonads span{position:relative;display:block;overflow:hidden;border-radius:0}.carbon-img{float:left;margin-right:1em}.carbon-img img{display:block;border-radius:var(--border-radius)}.carbon-text{display:block;float:left;max-width:calc(100% - 128px - 1em);text-align:left;color:var(--color-carbon-ads-text);margin-left:-6px}.carbon-poweredby{position:absolute;left:calc(100% - 74px - 1em);bottom:0;display:block;font-size:12px;color:var(--color-carbon-ads-poweredby);font-weight:500}#carbonads a.carbon-poweredby{color:var(--color-carbon-ads-poweredby)}.footer{display:flex;align-items:center;justify-content:space-between;font-size:var(--font-size--normal)}.right{float:right}.left{float:left}.footer-logo{display:flex;max-height:80px;padding-right:5px}.copyright{display:flex;align-items:center}.links a{padding-left:5px}.mobile-header{font-family:TaraType,monospace;font-weight:700;letter-spacing:.0425em}.mobile-header .header-center img{width:38px}.mobile-header .header-center a{display:flex;align-items:flex-end}@media(max-width:67em){.mobile-header{align-items:center}.mobile-header .header-right{padding-top:10px;align-items:baseline;margin-right:20px;height:unset}.mobile-header .header-left{align-items:baseline;margin-left:10px;height:unset}}.mobile-header-title{position:absolute;margin-left:auto;margin-right:auto;width:fit-content;left:0;right:0;text-align:center;margin-top:10px}.mobile-header-title a{color:var(--color-foreground-primary)}.theme-toggle svg{width:1.75rem;height:1.75rem}.icon svg{width:1.75rem;height:1.75rem}.sidebar-tree label{margin-right:10px}blockquote{border-radius:var(--border-radius)}.bottom-of-page{font-size:var(--font-size--normal)}a{text-decoration:none}a:hover,a.reference:hover,.sidebar-tree .toctree-l1>.reference:hover{color:var(--color-link-hover)}b,strong{color:var(--color-strong)}.content{justify-content:unset}.sidebar-tree .current>.reference::before{content:"➤ "}.toc-tree .reference::before{content:"• "}.toc-tree li.scroll-current>.reference:before{content:"➤ "}table.hlist{margin-top:-20px}.highlight button.copybtn svg{color:var(--color-foreground-primary)}.toc-tree .reference{color:var(--color-brand-primary)}ul.breadcrumbs{margin-bottom:0;padding:0;list-style:none;font-size:18px}ul.breadcrumbs li{display:inline}.usable-by{border:1px solid var(--color-border);border-radius:var(--border-radius);padding:2px 5px;background-color:var(--color-background-highlight)}.content-icon-container{margin-top:1.9rem;margin-bottom:0}
\ No newline at end of file
diff --git a/docs/source/static/css/custom.css b/docs/source/static/css/custom.css
index ce751ec73f..d101d69f21 100644
--- a/docs/source/static/css/custom.css
+++ b/docs/source/static/css/custom.css
@@ -7,7 +7,7 @@
border: 1px ;
border-radius: .2em;
padding: 2px 5px;
- background-color: #1a1c1e;
+ /* background-color: #1a1c1e; */
}
.sidebar-logo {
diff --git a/docs/source/topics/speedups.rst b/docs/source/topics/speedups.rst
index a22187d260..9ace67e66a 100644
--- a/docs/source/topics/speedups.rst
+++ b/docs/source/topics/speedups.rst
@@ -18,7 +18,7 @@ Installation
.. code-block:: bash
- $ pip3 install -U tgcrypto
+ $ pip3 install -U PyTgCrypto
Usage
^^^^^
diff --git a/docs/source/topics/storage-engines.rst b/docs/source/topics/storage-engines.rst
index 4a09ae015a..d3c065b3dd 100644
--- a/docs/source/topics/storage-engines.rst
+++ b/docs/source/topics/storage-engines.rst
@@ -95,7 +95,7 @@ This class is a class that cannot be instantiated, but can be used to define a c
Custom Storage can be defined in :obj:`~pyrogram.Client` by passing ``storage_engine`` parameter with a :obj:`~pyrogram.storage.Storage` subclass.
Example of File Storage (using ``aiosqlite==0.20.0``)
-^^^^^^^^^^^^
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
How to use this Storage Engine is shown below.
@@ -115,7 +115,7 @@ This storage is almost completely identical to the default File Storage, but ins
await app.send_message(chat_id="me", text="Greetings from **Pyrogram**!")
Example of Telethon Storage
-^^^^^^^^^^^^
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If you want to use sessions from telethon in pyrogram (originally incompatible), you can use this `storage `_.
diff --git a/pyproject.toml b/pyproject.toml
index 4bfaf4c057..098feed1fe 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -40,11 +40,10 @@ path = "pyrogram/__init__.py"
[tool.hatch.build.hooks.custom]
[project.urls]
-Homepage = "https://telegramplayground.github.io/pyrogram/releases/"
+Homepage = "https://telegramplayground.github.io/pyrogram/"
Tracker = "https://github.com/TelegramPlayGround/Pyrogram/issues"
-Community = "https://t.me/PyroTGFork"
Source = "https://github.com/TelegramPlayGround/Pyrogram"
-Documentation = "https://telegramplayground.github.io/pyrogram/"
+Documentation = "https://telegramplayground.github.io/pyrogram/releases/changes-in-this-fork.html"
[build-system]
requires = ["hatchling"]
@@ -58,7 +57,10 @@ include = ["pyrogram/py.typed"]
exclude = ["tests", "docs", "requirements.lock", "requirements-dev.lock", ".github", ".gitignore"]
[tool.hatch.build.targets.sdist]
-include = ["pyrogram"]
+include = [
+ "compiler",
+ "pyrogram",
+]
exclude = [
".github/",
"docs/",
@@ -96,6 +98,6 @@ docs = [
]
fast = [
- "TgCrypto==1.2.5",
+ "PyTgCrypto==1.2.6",
"uvloop>0.18.0,<=0.19.0"
]
diff --git a/pyrogram/__init__.py b/pyrogram/__init__.py
index 7831ea6ce3..db336c6698 100644
--- a/pyrogram/__init__.py
+++ b/pyrogram/__init__.py
@@ -16,7 +16,8 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see .
-__version__ = "2.1.25"
+__fork_name__ = "pyrotgfork"
+__version__ = "2.1.33.2"
__license__ = "GNU Lesser General Public License v3.0 (LGPL-3.0)"
__copyright__ = "Copyright (C) 2017-present Dan "
diff --git a/pyrogram/client.py b/pyrogram/client.py
index 9e9fb8e8f0..5426dd3091 100644
--- a/pyrogram/client.py
+++ b/pyrogram/client.py
@@ -32,7 +32,7 @@
from io import StringIO, BytesIO
from mimetypes import MimeTypes
from pathlib import Path
-from typing import Union, List, Optional, Callable, AsyncGenerator
+from typing import Union, List, Optional, Callable, AsyncGenerator, Type
import pyrogram
from pyrogram import __version__, __license__
@@ -52,6 +52,8 @@
from pyrogram.storage import Storage, FileStorage, MemoryStorage
from pyrogram.types import User, TermsOfService
from pyrogram.utils import ainput
+from .connection import Connection
+from .connection.transport import TCP, TCPAbridged
from .dispatcher import Dispatcher
from .file_id import FileId, FileType, ThumbnailSource
from .mime_types import mime_types
@@ -211,6 +213,14 @@ class Client(Methods):
client_platform (:obj:`~pyrogram.enums.ClientPlatform`, *optional*):
The platform where this client is running.
Defaults to 'other'
+
+ link_preview_options (:obj:`~pyrogram.types.LinkPreviewOptions`, *optional*):
+ Set the global link preview options for the client. By default, no link preview option is set.
+
+ fetch_replies (``int``, *optional*):
+ Set the number of replies to be fetched when parsing the :obj:`~pyrogram.types.Message` object. Defaults to 1.
+ :doc:`More on Errors <../../api/errors/index>`
+
"""
APP_VERSION = f"Pyrogram {__version__}"
@@ -272,6 +282,8 @@ def __init__(
storage_engine: Storage = None,
no_joined_notifications: bool = False,
client_platform: enums.ClientPlatform = enums.ClientPlatform.OTHER,
+ link_preview_options: "types.LinkPreviewOptions" = None,
+ fetch_replies: int = 1,
_un_docu_gnihts: List = []
):
super().__init__()
@@ -309,6 +321,8 @@ def __init__(
self.no_joined_notifications = no_joined_notifications
self.client_platform = client_platform
self._un_docu_gnihts = _un_docu_gnihts
+ self.link_preview_options = link_preview_options
+ self.fetch_replies = fetch_replies
self.executor = ThreadPoolExecutor(self.workers, thread_name_prefix="Handler")
@@ -321,6 +335,9 @@ def __init__(
else:
self.storage = FileStorage(self.name, self.WORKDIR)
+ self.connection_factory = Connection
+ self.protocol_factory = TCPAbridged
+
self.dispatcher = Dispatcher(self)
self.rnd_id = MsgId
@@ -617,7 +634,7 @@ async def handle_updates(self, updates):
pts = getattr(update, "pts", None)
pts_count = getattr(update, "pts_count", None)
- if pts:
+ if pts and not self.skip_updates:
await self.storage.update_state(
(
utils.get_channel_id(channel_id) if channel_id else 0,
@@ -658,15 +675,16 @@ async def handle_updates(self, updates):
self.dispatcher.updates_queue.put_nowait((update, users, chats))
elif isinstance(updates, (raw.types.UpdateShortMessage, raw.types.UpdateShortChatMessage)):
- await self.storage.update_state(
- (
- 0,
- updates.pts,
- None,
- updates.date,
- None
+ if not self.skip_updates:
+ await self.storage.update_state(
+ (
+ 0,
+ updates.pts,
+ None,
+ updates.date,
+ None
+ )
)
- )
diff = await self.invoke(
raw.functions.updates.GetDifference(
@@ -984,8 +1002,7 @@ async def get_file(
location=location,
offset=offset_bytes,
limit=chunk_size
- ),
- sleep_threshold=30
+ )
)
if isinstance(r, raw.types.upload.File):
@@ -1020,8 +1037,7 @@ async def get_file(
location=location,
offset=offset_bytes,
limit=chunk_size
- ),
- sleep_threshold=30
+ )
)
elif isinstance(r, raw.types.upload.FileCdnRedirect):
diff --git a/pyrogram/connection/connection.py b/pyrogram/connection/connection.py
index 1107673f1a..8db9724fb4 100644
--- a/pyrogram/connection/connection.py
+++ b/pyrogram/connection/connection.py
@@ -18,7 +18,7 @@
import asyncio
import logging
-from typing import Optional
+from typing import Optional, Type
from .transport import TCP, TCPAbridged
from ..session.internals import DataCenter
@@ -29,19 +29,28 @@
class Connection:
MAX_CONNECTION_ATTEMPTS = 3
- def __init__(self, dc_id: int, test_mode: bool, ipv6: bool, proxy: dict, media: bool = False):
+ def __init__(
+ self,
+ dc_id: int,
+ test_mode: bool,
+ ipv6: bool,
+ proxy: dict,
+ media: bool = False,
+ protocol_factory: Type[TCP] = TCPAbridged
+ ) -> None:
self.dc_id = dc_id
self.test_mode = test_mode
self.ipv6 = ipv6
self.proxy = proxy
self.media = media
+ self.protocol_factory = protocol_factory
self.address = DataCenter(dc_id, test_mode, ipv6, media)
- self.protocol: TCP = None
+ self.protocol: Optional[TCP] = None
- async def connect(self):
+ async def connect(self) -> None:
for i in range(Connection.MAX_CONNECTION_ATTEMPTS):
- self.protocol = TCPAbridged(self.ipv6, self.proxy)
+ self.protocol = self.protocol_factory(ipv6=self.ipv6, proxy=self.proxy)
try:
log.info("Connecting...")
@@ -61,11 +70,11 @@ async def connect(self):
log.warning("Connection failed! Trying again...")
raise ConnectionError
- async def close(self):
+ async def close(self) -> None:
await self.protocol.close()
log.info("Disconnected")
- async def send(self, data: bytes):
+ async def send(self, data: bytes) -> None:
await self.protocol.send(data)
async def recv(self) -> Optional[bytes]:
diff --git a/pyrogram/connection/transport/tcp/__init__.py b/pyrogram/connection/transport/tcp/__init__.py
index 3e23a88379..bae35e8825 100644
--- a/pyrogram/connection/transport/tcp/__init__.py
+++ b/pyrogram/connection/transport/tcp/__init__.py
@@ -16,7 +16,7 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see .
-from .tcp import TCP
+from .tcp import TCP, Proxy
from .tcp_abridged import TCPAbridged
from .tcp_abridged_o import TCPAbridgedO
from .tcp_full import TCPFull
diff --git a/pyrogram/connection/transport/tcp/tcp.py b/pyrogram/connection/transport/tcp/tcp.py
index ce9fe3f3a3..9994fb8220 100644
--- a/pyrogram/connection/transport/tcp/tcp.py
+++ b/pyrogram/connection/transport/tcp/tcp.py
@@ -21,90 +21,135 @@
import logging
import socket
from concurrent.futures import ThreadPoolExecutor
+from typing import Tuple, Dict, TypedDict, Optional
import socks
log = logging.getLogger(__name__)
+proxy_type_by_scheme: Dict[str, int] = {
+ "SOCKS4": socks.SOCKS4,
+ "SOCKS5": socks.SOCKS5,
+ "HTTP": socks.HTTP,
+}
+
+
+class Proxy(TypedDict):
+ scheme: str
+ hostname: str
+ port: int
+ username: Optional[str]
+ password: Optional[str]
+
class TCP:
TIMEOUT = 10
- def __init__(self, ipv6: bool, proxy: dict):
- self.socket = None
+ def __init__(self, ipv6: bool, proxy: Proxy) -> None:
+ self.ipv6 = ipv6
+ self.proxy = proxy
- self.reader = None
- self.writer = None
+ self.reader: Optional[asyncio.StreamReader] = None
+ self.writer: Optional[asyncio.StreamWriter] = None
self.lock = asyncio.Lock()
self.loop = asyncio.get_event_loop()
- self.proxy = proxy
+ async def _connect_via_proxy(
+ self,
+ destination: Tuple[str, int]
+ ) -> None:
+ scheme = self.proxy.get("scheme")
+ if scheme is None:
+ raise ValueError("No scheme specified")
- if proxy:
- hostname = proxy.get("hostname")
-
- try:
- ip_address = ipaddress.ip_address(hostname)
- except ValueError:
- self.socket = socks.socksocket(socket.AF_INET)
- else:
- if isinstance(ip_address, ipaddress.IPv6Address):
- self.socket = socks.socksocket(socket.AF_INET6)
- else:
- self.socket = socks.socksocket(socket.AF_INET)
+ proxy_type = proxy_type_by_scheme.get(scheme.upper())
+ if proxy_type is None:
+ raise ValueError(f"Unknown proxy type {scheme}")
- self.socket.set_proxy(
- proxy_type=getattr(socks, proxy.get("scheme").upper()),
- addr=hostname,
- port=proxy.get("port", None),
- username=proxy.get("username", None),
- password=proxy.get("password", None)
- )
+ hostname = self.proxy.get("hostname")
+ port = self.proxy.get("port")
+ username = self.proxy.get("username")
+ password = self.proxy.get("password")
- self.socket.settimeout(TCP.TIMEOUT)
-
- log.info("Using proxy %s", hostname)
+ try:
+ ip_address = ipaddress.ip_address(hostname)
+ except ValueError:
+ is_proxy_ipv6 = False
else:
- self.socket = socket.socket(
- socket.AF_INET6 if ipv6
- else socket.AF_INET
- )
-
- self.socket.setblocking(False)
-
- async def connect(self, address: tuple):
+ is_proxy_ipv6 = isinstance(ip_address, ipaddress.IPv6Address)
+
+ proxy_family = socket.AF_INET6 if is_proxy_ipv6 else socket.AF_INET
+ sock = socks.socksocket(proxy_family)
+
+ sock.set_proxy(
+ proxy_type=proxy_type,
+ addr=hostname,
+ port=port,
+ username=username,
+ password=password
+ )
+ sock.settimeout(TCP.TIMEOUT)
+
+ await self.loop.sock_connect(
+ sock=sock,
+ address=destination
+ )
+
+ sock.setblocking(False)
+
+ self.reader, self.writer = await asyncio.open_connection(
+ sock=sock
+ )
+
+ async def _connect_via_direct(
+ self,
+ destination: Tuple[str, int]
+ ) -> None:
+ host, port = destination
+ family = socket.AF_INET6 if self.ipv6 else socket.AF_INET
+ self.reader, self.writer = await asyncio.open_connection(
+ host=host,
+ port=port,
+ family=family
+ )
+
+ async def _connect(self, destination: Tuple[str, int]) -> None:
if self.proxy:
- with ThreadPoolExecutor(1) as executor:
- await self.loop.run_in_executor(executor, self.socket.connect, address)
+ await self._connect_via_proxy(destination)
else:
- try:
- await asyncio.wait_for(asyncio.get_event_loop().sock_connect(self.socket, address), TCP.TIMEOUT)
- except asyncio.TimeoutError: # Re-raise as TimeoutError. asyncio.TimeoutError is deprecated in 3.11
- raise TimeoutError("Connection timed out")
+ await self._connect_via_direct(destination)
+
+ async def connect(self, address: Tuple[str, int]) -> None:
+ try:
+ await asyncio.wait_for(self._connect(address), TCP.TIMEOUT)
+ except asyncio.TimeoutError: # Re-raise as TimeoutError. asyncio.TimeoutError is deprecated in 3.11
+ raise TimeoutError("Connection timed out")
- self.reader, self.writer = await asyncio.open_connection(sock=self.socket)
+ async def close(self) -> None:
+ if self.writer is None:
+ return None
- async def close(self):
try:
- if self.writer is not None:
- self.writer.close()
- await asyncio.wait_for(self.writer.wait_closed(), TCP.TIMEOUT)
+ self.writer.close()
+ await asyncio.wait_for(self.writer.wait_closed(), TCP.TIMEOUT)
except Exception as e:
log.info("Close exception: %s %s", type(e).__name__, e)
- async def send(self, data: bytes):
+ async def send(self, data: bytes) -> None:
+ if self.writer is None:
+ return None
+
async with self.lock:
try:
- if self.writer is not None:
- self.writer.write(data)
- await self.writer.drain()
+ self.writer.write(data)
+ await self.writer.drain()
except Exception as e:
# error coming somewhere here
log.exception("Send exception: %s %s", type(e).__name__, e)
raise OSError(e)
- async def recv(self, length: int = 0):
+ async def recv(self, length: int = 0) -> Optional[bytes]:
data = b""
while len(data) < length:
diff --git a/pyrogram/connection/transport/tcp/tcp_abridged.py b/pyrogram/connection/transport/tcp/tcp_abridged.py
index 77d44cf41c..4cb4c1b2a3 100644
--- a/pyrogram/connection/transport/tcp/tcp_abridged.py
+++ b/pyrogram/connection/transport/tcp/tcp_abridged.py
@@ -17,22 +17,22 @@
# along with Pyrogram. If not, see .
import logging
-from typing import Optional
+from typing import Optional, Tuple
-from .tcp import TCP
+from .tcp import TCP, Proxy
log = logging.getLogger(__name__)
class TCPAbridged(TCP):
- def __init__(self, ipv6: bool, proxy: dict):
+ def __init__(self, ipv6: bool, proxy: Proxy) -> None:
super().__init__(ipv6, proxy)
- async def connect(self, address: tuple):
+ async def connect(self, address: Tuple[str, int]) -> None:
await super().connect(address)
await super().send(b"\xef")
- async def send(self, data: bytes, *args):
+ async def send(self, data: bytes, *args) -> None:
length = len(data) // 4
await super().send(
diff --git a/pyrogram/connection/transport/tcp/tcp_abridged_o.py b/pyrogram/connection/transport/tcp/tcp_abridged_o.py
index 6f57ab1154..20efb5ec3f 100644
--- a/pyrogram/connection/transport/tcp/tcp_abridged_o.py
+++ b/pyrogram/connection/transport/tcp/tcp_abridged_o.py
@@ -18,11 +18,11 @@
import logging
import os
-from typing import Optional
+from typing import Optional, Tuple
import pyrogram
from pyrogram.crypto import aes
-from .tcp import TCP
+from .tcp import TCP, Proxy
log = logging.getLogger(__name__)
@@ -30,13 +30,13 @@
class TCPAbridgedO(TCP):
RESERVED = (b"HEAD", b"POST", b"GET ", b"OPTI", b"\xee" * 4)
- def __init__(self, ipv6: bool, proxy: dict):
+ def __init__(self, ipv6: bool, proxy: Proxy) -> None:
super().__init__(ipv6, proxy)
self.encrypt = None
self.decrypt = None
- async def connect(self, address: tuple):
+ async def connect(self, address: Tuple[str, int]) -> None:
await super().connect(address)
while True:
@@ -55,7 +55,7 @@ async def connect(self, address: tuple):
await super().send(nonce)
- async def send(self, data: bytes, *args):
+ async def send(self, data: bytes, *args) -> None:
length = len(data) // 4
data = (bytes([length]) if length <= 126 else b"\x7f" + length.to_bytes(3, "little")) + data
payload = await self.loop.run_in_executor(pyrogram.crypto_executor, aes.ctr256_encrypt, data, *self.encrypt)
diff --git a/pyrogram/connection/transport/tcp/tcp_full.py b/pyrogram/connection/transport/tcp/tcp_full.py
index 8bd89000c8..ad9d981718 100644
--- a/pyrogram/connection/transport/tcp/tcp_full.py
+++ b/pyrogram/connection/transport/tcp/tcp_full.py
@@ -19,24 +19,24 @@
import logging
from binascii import crc32
from struct import pack, unpack
-from typing import Optional
+from typing import Optional, Tuple
-from .tcp import TCP
+from .tcp import TCP, Proxy
log = logging.getLogger(__name__)
class TCPFull(TCP):
- def __init__(self, ipv6: bool, proxy: dict):
+ def __init__(self, ipv6: bool, proxy: Proxy) -> None:
super().__init__(ipv6, proxy)
- self.seq_no = None
+ self.seq_no: Optional[int] = None
- async def connect(self, address: tuple):
+ async def connect(self, address: Tuple[str, int]) -> None:
await super().connect(address)
self.seq_no = 0
- async def send(self, data: bytes, *args):
+ async def send(self, data: bytes, *args) -> None:
data = pack(" None:
super().__init__(ipv6, proxy)
- async def connect(self, address: tuple):
+ async def connect(self, address: Tuple[str, int]) -> None:
await super().connect(address)
await super().send(b"\xee" * 4)
- async def send(self, data: bytes, *args):
+ async def send(self, data: bytes, *args) -> None:
await super().send(pack(" Optional[bytes]:
diff --git a/pyrogram/connection/transport/tcp/tcp_intermediate_o.py b/pyrogram/connection/transport/tcp/tcp_intermediate_o.py
index 48b2d44520..3f47bdfe0a 100644
--- a/pyrogram/connection/transport/tcp/tcp_intermediate_o.py
+++ b/pyrogram/connection/transport/tcp/tcp_intermediate_o.py
@@ -19,10 +19,10 @@
import logging
import os
from struct import pack, unpack
-from typing import Optional
+from typing import Optional, Tuple
from pyrogram.crypto import aes
-from .tcp import TCP
+from .tcp import TCP, Proxy
log = logging.getLogger(__name__)
@@ -30,13 +30,13 @@
class TCPIntermediateO(TCP):
RESERVED = (b"HEAD", b"POST", b"GET ", b"OPTI", b"\xee" * 4)
- def __init__(self, ipv6: bool, proxy: dict):
+ def __init__(self, ipv6: bool, proxy: Proxy) -> None:
super().__init__(ipv6, proxy)
self.encrypt = None
self.decrypt = None
- async def connect(self, address: tuple):
+ async def connect(self, address: Tuple[str, int]) -> None:
await super().connect(address)
while True:
@@ -55,7 +55,7 @@ async def connect(self, address: tuple):
await super().send(nonce)
- async def send(self, data: bytes, *args):
+ async def send(self, data: bytes, *args) -> None:
await super().send(
aes.ctr256_encrypt(
pack(".
+from enum import auto
+
from pyrogram import raw
+
from .auto_name import AutoName
@@ -59,15 +62,18 @@ class MessageEntityType(AutoName):
SPOILER = raw.types.MessageEntitySpoiler
"Spoiler text"
+ BLOCKQUOTE = auto()
+ "Block quotation"
+
+ EXPANDABLE_BLOCKQUOTE = auto()
+ "collapsed-by-default block quotation"
+
CODE = raw.types.MessageEntityCode
"Monowidth string"
PRE = raw.types.MessageEntityPre
"Monowidth block (see ``language``)"
- BLOCKQUOTE = raw.types.MessageEntityBlockquote
- "Blockquote text"
-
TEXT_LINK = raw.types.MessageEntityTextUrl
"For clickable text URLs"
diff --git a/pyrogram/enums/message_media_type.py b/pyrogram/enums/message_media_type.py
index 3c8afacb95..e6073707c1 100644
--- a/pyrogram/enums/message_media_type.py
+++ b/pyrogram/enums/message_media_type.py
@@ -77,3 +77,9 @@ class MessageMediaType(AutoName):
GIVEAWAY_WINNERS = auto()
"Giveaway Winners"
+
+ INVOICE = auto()
+ "Invoice"
+
+ PAID_MEDIA = auto()
+ "Paid Media"
diff --git a/pyrogram/enums/message_service_type.py b/pyrogram/enums/message_service_type.py
index ead5951093..17e85678f3 100644
--- a/pyrogram/enums/message_service_type.py
+++ b/pyrogram/enums/message_service_type.py
@@ -72,6 +72,9 @@ class MessageServiceType(AutoName):
GIFTED_PREMIUM = auto()
"Gifted Premium"
+ GIFTED_STARS = auto()
+ "Gifted Stars"
+
VIDEO_CHAT_STARTED = auto()
"Video chat started"
@@ -93,8 +96,8 @@ class MessageServiceType(AutoName):
CHAT_SHARED = auto()
"Chat Shared"
- CHAT_TTL_CHANGED = auto()
- "Chat TTL changed"
+ MESSAGE_AUTO_DELETE_TIMER_CHANGED = auto()
+ "Message Auto Delete Timer changed"
CHAT_BOOST_ADDED = auto()
"Chat Boost Added"
@@ -119,3 +122,9 @@ class MessageServiceType(AutoName):
GENERAL_FORUM_TOPIC_UNHIDDEN = auto()
"a forum general topic unhidden in the chat"
+
+ SUCCESSFUL_PAYMENT = auto()
+ "Successful payment"
+
+ REFUNDED_PAYMENT = auto()
+ "Refunded payment"
diff --git a/pyrogram/errors/rpc_error.py b/pyrogram/errors/rpc_error.py
index 0268e5a2a4..1ca3631627 100644
--- a/pyrogram/errors/rpc_error.py
+++ b/pyrogram/errors/rpc_error.py
@@ -22,7 +22,7 @@
from importlib import import_module
from typing import Type, Union
-from pyrogram import raw
+from pyrogram import __version__, raw
from pyrogram.raw.core import TLObject
from .exceptions.all import exceptions
@@ -50,12 +50,13 @@ def __init__(
is_signed: bool = False,
_raw = None
):
- super().__init__("Telegram says: [{}{} {}] - {} {}".format(
+ super().__init__("Telegram says: [{}{} {}] {} Pyrogram {} thinks: {}".format(
"-" if is_signed else "",
self.CODE,
self.ID or self.NAME,
+ f'(caused by "{rpc_name}")' if rpc_name else "",
+ __version__,
self.MESSAGE.format(value=value),
- f'(caused by "{rpc_name}")' if rpc_name else ""
))
self._raw = _raw
diff --git a/pyrogram/filters.py b/pyrogram/filters.py
index 26d611f856..1889744405 100644
--- a/pyrogram/filters.py
+++ b/pyrogram/filters.py
@@ -18,7 +18,7 @@
import inspect
import re
-from typing import Any, Callable, Literal, Union, List, Pattern
+from typing import Any, Callable, List, Literal, Optional, Pattern, Union
import pyrogram
from pyrogram import enums
@@ -27,6 +27,7 @@
CallbackQuery,
InlineQuery,
InlineKeyboardMarkup,
+ PreCheckoutQuery,
ReplyKeyboardMarkup,
Update,
)
@@ -239,7 +240,7 @@ async def reply_filter(_, __, m: Message) -> bool:
# region forwarded_filter
async def forwarded_filter(_, __, m: Message) -> bool:
- return bool(m.forward_date)
+ return bool(m.forward_origin)
forwarded = create(forwarded_filter)
@@ -694,12 +695,36 @@ async def mentioned_filter(_, __, m: Message) -> bool:
# region via_bot_filter
-async def via_bot_filter(_, __, m: Message) -> bool:
- return bool(m.via_bot)
+def via_bot_filter(flt, *args):
+ # https://t.me/c/1220993104/1379819
+ if isinstance(args[0], pyrogram.Client):
+ _, m, *__ = args
+ return bool(m.via_bot) and (
+ len(flt) == 0
+ or (
+ m.via_bot.id in flt or (
+ m.via_bot.username and m.via_bot.username.lower() in flt
+ )
+ )
+ )
+ bots = args[0] if isinstance(args[0], list) else [args[0]]
+ flt = type(flt)(u.lower().lstrip("@") if isinstance(u, str) else u for u in bots)
+ return flt
+via_bot: Filter = type(
+ via_bot_filter.__name__,
+ (Filter, set),
+ dict(__call__=via_bot_filter),
+)()
+"""Filter messages sent via inline bots
-via_bot: Filter = create(via_bot_filter)
-"""Filter messages sent via inline bots"""
+ Parameters:
+ user_ids (``int`` | ``str`` | Iterable of ``int`` | Iterable of ``str``, *optional*):
+ Unique identifier (int) or username (str) of the target chat.
+ For your personal cloud (Saved Messages) you can simply use "me" or "self".
+ For a contact that exists in your Telegram address book you can use his phone number (str).
+ Defaults to None (all bots).
+"""
# endregion
@@ -757,6 +782,16 @@ async def video_chat_participants_invited_filter(_, __, m: Message) -> bool:
# endregion
+# region successful_payment_filter
+async def successful_payment_filter(_, __, m: Message):
+ return bool(m.successful_payment)
+
+
+successful_payment = create(successful_payment_filter)
+"""Filter messages for successful payments"""
+
+
+# endregion
# region service_filter
async def service_filter(_, __, m: Message) -> bool:
@@ -769,7 +804,7 @@ async def service_filter(_, __, m: Message) -> bool:
A service message contains any of the following fields set: *left_chat_member*,
*new_chat_title*, *new_chat_photo*, *delete_chat_photo*, *group_chat_created*, *supergroup_chat_created*,
*channel_chat_created*, *migrate_to_chat_id*, *migrate_from_chat_id*, *pinned_message*, *game_score*,
-*video_chat_started*, *video_chat_ended*, *video_chat_participants_invited*.
+*video_chat_started*, *video_chat_ended*, *video_chat_participants_invited*, *successful_payment*.
"""
@@ -817,8 +852,11 @@ async def from_scheduled_filter(_, __, m: Message) -> bool:
# region linked_channel_filter
async def linked_channel_filter(_, __, m: Message) -> bool:
- return bool(m.forward_from_chat and not m.from_user)
-
+ return bool(
+ m.forward_origin and
+ m.forward_origin.type == "channel" and
+ m.forward_origin.chat == m.sender_chat
+ )
linked_channel: Filter = create(linked_channel_filter)
"""Filter messages that are automatically forwarded from the linked channel to the group chat."""
@@ -955,6 +993,7 @@ def regex(pattern: Union[str, Pattern], flags: int = 0) -> Filter:
- :obj:`~pyrogram.types.Message`: The filter will match ``text`` or ``caption``.
- :obj:`~pyrogram.types.CallbackQuery`: The filter will match ``data``.
- :obj:`~pyrogram.types.InlineQuery`: The filter will match ``query``.
+ - :obj:`~pyrogram.types.PreCheckoutQuery`: The filter will match ``payload``.
When a pattern matches, all the `Match Objects `_ are
stored in the ``matches`` field of the update object itself.
@@ -974,6 +1013,8 @@ async def func(flt, _, update: Update) -> bool:
value: str | bytes = update.data
elif isinstance(update, InlineQuery):
value: str = update.query
+ elif isinstance(update, PreCheckoutQuery):
+ value: str = update.invoice_payload
else:
raise ValueError(f"Regex filter doesn't work with {type(update)}")
@@ -1006,7 +1047,7 @@ class user(Filter, set):
Defaults to None (no users).
"""
- def __init__(self, users: Union[int, str, List[Union[int, str]]] = None) -> None:
+ def __init__(self, users: Optional[Union[int, str, List[Union[int, str]]]] = None) -> None:
users = [] if users is None else users if isinstance(users, list) else [users]
super().__init__(
@@ -1043,7 +1084,7 @@ class chat(Filter, set):
Defaults to None (no chats).
"""
- def __init__(self, chats: Union[int, str, List[Union[int, str]]] = None) -> None:
+ def __init__(self, chats: Optional[Union[int, str, List[Union[int, str]]]] = None) -> None:
chats = [] if chats is None else chats if isinstance(chats, list) else [chats]
super().__init__(
@@ -1089,4 +1130,51 @@ async def __call__(self, _, message: Message) -> bool:
)
"""Filter service messages for chat shared."""
+# endregion
+
+# noinspection PyPep8Naming
+class thread(Filter, set):
+ """Filter messages coming from one or more threads.
+
+ You can use `set bound methods `_ to manipulate the
+ message_thread_ids container.
+
+ Parameters:
+ message_thread_ids (``int`` | ``list``):
+ Pass one or more message thread ids to filter messages in specific threads.
+ Defaults to None (no threads).
+ """
+
+ def __init__(self, message_thread_ids: Optional[Union[int, List[int]]] = None):
+ message_thread_ids = [] if message_thread_ids is None else message_thread_ids if isinstance(message_thread_ids, list) else [message_thread_ids]
+
+ super().__init__(
+ t for t in message_thread_ids
+ )
+
+ async def __call__(self, _, message: Message):
+ return message.message_thread_id and message.message_thread_id in self
+
+
+# region self_destruct_filter
+
+async def self_destruct_filter(_, __, m: Message):
+ return bool(
+ m.media and
+ getattr(
+ getattr(
+ m,
+ m.media.value,
+ None
+ ),
+ "ttl_seconds",
+ None
+ )
+ )
+
+
+self_destruct = create(self_destruct_filter)
+"""Filter self destruct media messages."""
+
+
# endregion
diff --git a/pyrogram/handlers/__init__.py b/pyrogram/handlers/__init__.py
index 0ce9deaa21..5ef7d3d6f8 100644
--- a/pyrogram/handlers/__init__.py
+++ b/pyrogram/handlers/__init__.py
@@ -30,3 +30,6 @@
from .user_status_handler import UserStatusHandler
from .message_reaction_updated_handler import MessageReactionUpdatedHandler
from .message_reaction_count_updated_handler import MessageReactionCountUpdatedHandler
+from .pre_checkout_query_handler import PreCheckoutQueryHandler
+from .shipping_query_handler import ShippingQueryHandler
+from .story_handler import StoryHandler
diff --git a/pyrogram/handlers/pre_checkout_query_handler.py b/pyrogram/handlers/pre_checkout_query_handler.py
new file mode 100644
index 0000000000..c68b9aa478
--- /dev/null
+++ b/pyrogram/handlers/pre_checkout_query_handler.py
@@ -0,0 +1,51 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+from typing import Callable
+
+from .handler import Handler
+
+
+class PreCheckoutQueryHandler(Handler):
+ """The PreCheckoutQueryHandler handler class. Used to handle pre-checkout queries coming from invoice buttons.
+
+ It is intended to be used with :meth:`~pyrogram.Client.add_handler`
+
+ For a nicer way to register this handler, have a look at the
+ :meth:`~pyrogram.Client.on_pre_checkout_query` decorator.
+
+ Parameters:
+ callback (``Callable``):
+ Pass a function that will be called when a new PreCheckoutQuery arrives. It takes *(client, pre_checkout_query)*
+ as positional arguments (look at the section below for a detailed description).
+
+ filters (:obj:`Filters`):
+ Pass one or more filters to allow only a subset of callback queries to be passed
+ in your callback function.
+
+ Other parameters:
+ client (:obj:`~pyrogram.Client`):
+ The Client itself, useful when you want to call other API methods inside the message handler.
+
+ pre_checkout_query (:obj:`~pyrogram.types.PreCheckoutQuery`):
+ New incoming pre-checkout query. Contains full information about checkout.
+
+ """
+
+ def __init__(self, callback: Callable, filters=None):
+ super().__init__(callback, filters)
diff --git a/pyrogram/handlers/shipping_query_handler.py b/pyrogram/handlers/shipping_query_handler.py
new file mode 100644
index 0000000000..478a3745fa
--- /dev/null
+++ b/pyrogram/handlers/shipping_query_handler.py
@@ -0,0 +1,51 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+from typing import Callable
+
+from .handler import Handler
+
+
+class ShippingQueryHandler(Handler):
+ """The ShippingQueryHandler handler class. Used to handle shipping queries coming only from invoice buttons with flexible price.
+
+ It is intended to be used with :meth:`~pyrogram.Client.add_handler`
+
+ For a nicer way to register this handler, have a look at the
+ :meth:`~pyrogram.Client.on_shipping_query` decorator.
+
+ Parameters:
+ callback (``Callable``):
+ Pass a function that will be called when a new PreCheckoutQuery arrives. It takes *(client, shipping_query)*
+ as positional arguments (look at the section below for a detailed description).
+
+ filters (:obj:`Filters`):
+ Pass one or more filters to allow only a subset of callback queries to be passed
+ in your callback function.
+
+ Other parameters:
+ client (:obj:`~pyrogram.Client`):
+ The Client itself, useful when you want to call other API methods inside the message handler.
+
+ shipping_query (:obj:`~pyrogram.types.ShippingQuery`):
+ New incoming shipping query. Only for invoices with flexible price.
+
+ """
+
+ def __init__(self, callback: Callable, filters=None):
+ super().__init__(callback, filters)
diff --git a/pyrogram/handlers/story_handler.py b/pyrogram/handlers/story_handler.py
new file mode 100644
index 0000000000..b6a07d1cd0
--- /dev/null
+++ b/pyrogram/handlers/story_handler.py
@@ -0,0 +1,49 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+from typing import Callable
+
+from .handler import Handler
+
+
+class StoryHandler(Handler):
+ """The Story handler class. Used to handle new stories.
+ It is intended to be used with :meth:`~pyrogram.Client.add_handler`
+
+ For a nicer way to register this handler, have a look at the
+ :meth:`~pyrogram.Client.on_story` decorator.
+
+ Parameters:
+ callback (``Callable``):
+ Pass a function that will be called when a new Stories arrives. It takes *(client, story)*
+ as positional arguments (look at the section below for a detailed description).
+
+ filters (:obj:`Filters`):
+ Pass one or more filters to allow only a subset of stories to be passed
+ in your callback function.
+
+ Other parameters:
+ client (:obj:`~pyrogram.Client`):
+ The Client itself, useful when you want to call other API methods inside the story handler.
+
+ story (:obj:`~pyrogram.types.Story`):
+ The received story.
+ """
+
+ def __init__(self, callback: Callable, filters=None):
+ super().__init__(callback, filters)
diff --git a/pyrogram/methods/__init__.py b/pyrogram/methods/__init__.py
index b1b8514344..9f0f11b1f0 100644
--- a/pyrogram/methods/__init__.py
+++ b/pyrogram/methods/__init__.py
@@ -26,7 +26,9 @@
from .invite_links import InviteLinks
from .messages import Messages
from .password import Password
+from .phone import Phone
from .stickers import Stickers
+from .stories import Stories
from .users import Users
from .utilities import Utilities
from .business import TelegramBusiness
@@ -43,7 +45,9 @@ class Methods(
InviteLinks,
Messages,
Password,
+ Phone,
Stickers,
+ Stories,
TelegramBusiness,
Users,
Utilities,
diff --git a/pyrogram/methods/auth/__init__.py b/pyrogram/methods/auth/__init__.py
index ce585648da..91a5009d1c 100644
--- a/pyrogram/methods/auth/__init__.py
+++ b/pyrogram/methods/auth/__init__.py
@@ -20,6 +20,7 @@
from .check_password import CheckPassword
from .connect import Connect
from .disconnect import Disconnect
+from .get_active_sessions import GetActiveSessions
from .get_password_hint import GetPasswordHint
from .initialize import Initialize
from .log_out import LogOut
@@ -31,6 +32,8 @@
from .sign_in_bot import SignInBot
from .sign_up import SignUp
from .terminate import Terminate
+from .terminate_all_other_sessions import TerminateAllOtherSessions
+from .terminate_session import TerminateSession
class Auth(
@@ -38,6 +41,7 @@ class Auth(
CheckPassword,
Connect,
Disconnect,
+ GetActiveSessions,
GetPasswordHint,
Initialize,
LogOut,
@@ -48,6 +52,8 @@ class Auth(
SignIn,
SignInBot,
SignUp,
- Terminate
+ Terminate,
+ TerminateAllOtherSessions,
+ TerminateSession,
):
pass
diff --git a/pyrogram/methods/auth/get_active_sessions.py b/pyrogram/methods/auth/get_active_sessions.py
new file mode 100644
index 0000000000..d8c906b860
--- /dev/null
+++ b/pyrogram/methods/auth/get_active_sessions.py
@@ -0,0 +1,40 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+from typing import List
+
+import pyrogram
+from pyrogram import raw, types
+
+
+class GetActiveSessions:
+ async def get_active_sessions(
+ self: "pyrogram.Client"
+ ) -> "types.ActiveSessions":
+ """Returns all active sessions of the current user.
+
+ .. include:: /_includes/usable-by/users.rst
+
+ Returns:
+ :obj:`~pyrogram.types.ActiveSessions`: On success, all the active sessions of the current user is returned.
+
+ """
+ r = await self.invoke(
+ raw.functions.account.GetAuthorizations()
+ )
+ return types.ActiveSessions._parse(r)
diff --git a/pyrogram/methods/auth/terminate_all_other_sessions.py b/pyrogram/methods/auth/terminate_all_other_sessions.py
new file mode 100644
index 0000000000..027ea3cc3b
--- /dev/null
+++ b/pyrogram/methods/auth/terminate_all_other_sessions.py
@@ -0,0 +1,40 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+import pyrogram
+from pyrogram import raw
+
+
+class TerminateAllOtherSessions:
+ async def terminate_all_other_sessions(
+ self: "pyrogram.Client"
+ ) -> bool:
+ """Terminates all other sessions of the current user.
+
+ .. include:: /_includes/usable-by/users.rst
+
+ Returns:
+ ``bool``: On success, in case the session is destroyed, True is returned. Otherwise, False is returned.
+
+ Raises:
+ RPCError: In case of a Telegram RPC error.
+
+ """
+ return await self.invoke(
+ raw.functions.auth.ResetAuthorizations()
+ )
diff --git a/pyrogram/methods/auth/terminate_session.py b/pyrogram/methods/auth/terminate_session.py
new file mode 100644
index 0000000000..90ddaf7a70
--- /dev/null
+++ b/pyrogram/methods/auth/terminate_session.py
@@ -0,0 +1,45 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+import pyrogram
+from pyrogram import raw
+
+
+class TerminateSession:
+ async def terminate_session(
+ self: "pyrogram.Client",
+ session_id: int
+ ) -> bool:
+ """Terminates a session of the current user.
+
+ .. include:: /_includes/usable-by/users.rst
+
+ Parameters:
+ session_id (``int``):
+ Session identifier.
+
+ Returns:
+ ``bool``: On success, in case the session is destroyed, True is returned. Otherwise, False is returned.
+
+ Raises:
+ RPCError: In case of a Telegram RPC error.
+
+ """
+ return await self.invoke(
+ raw.functions.account.ResetAuthorization(hash=session_id)
+ )
diff --git a/pyrogram/methods/bots/__init__.py b/pyrogram/methods/bots/__init__.py
index 8198ed8fe7..33a602914c 100644
--- a/pyrogram/methods/bots/__init__.py
+++ b/pyrogram/methods/bots/__init__.py
@@ -22,14 +22,21 @@
from .delete_bot_commands import DeleteBotCommands
from .get_bot_commands import GetBotCommands
from .get_bot_default_privileges import GetBotDefaultPrivileges
+from .get_bot_info_description import GetBotInfoDescription
+from .get_bot_info_short_description import GetBotInfoShortDescription
+from .get_bot_name import GetBotName
from .get_chat_menu_button import GetChatMenuButton
from .get_game_high_scores import GetGameHighScores
from .get_inline_bot_results import GetInlineBotResults
from .request_callback_answer import RequestCallbackAnswer
from .send_game import SendGame
from .send_inline_bot_result import SendInlineBotResult
+from .send_web_app_custom_request import SendWebAppCustomRequest
from .set_bot_commands import SetBotCommands
from .set_bot_default_privileges import SetBotDefaultPrivileges
+from .set_bot_info_description import SetBotInfoDescription
+from .set_bot_info_short_description import SetBotInfoShortDescription
+from .set_bot_name import SetBotName
from .set_chat_menu_button import SetChatMenuButton
from .set_game_score import SetGameScore
@@ -38,6 +45,7 @@ class Bots(
AnswerCallbackQuery,
AnswerInlineQuery,
AnswerWebAppQuery,
+ SendWebAppCustomRequest,
GetInlineBotResults,
RequestCallbackAnswer,
SendInlineBotResult,
@@ -51,5 +59,11 @@ class Bots(
GetBotDefaultPrivileges,
SetChatMenuButton,
GetChatMenuButton,
+ SetBotInfoDescription,
+ GetBotInfoDescription,
+ SetBotInfoShortDescription,
+ GetBotInfoShortDescription,
+ SetBotName,
+ GetBotName,
):
pass
diff --git a/pyrogram/methods/bots/get_bot_info_description.py b/pyrogram/methods/bots/get_bot_info_description.py
new file mode 100644
index 0000000000..739e6d60dc
--- /dev/null
+++ b/pyrogram/methods/bots/get_bot_info_description.py
@@ -0,0 +1,62 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+from typing import Union
+
+import pyrogram
+from pyrogram import raw
+
+
+class GetBotInfoDescription:
+ async def get_bot_info_description(
+ self: "pyrogram.Client",
+ language_code: str = "",
+ for_my_bot: Union[int, str] = None,
+ ) -> str:
+ """Use this method to get the current / owned bot description for the given user language.
+
+ .. note::
+
+ If the current account is an User, can be called only if the ``for_my_bot`` has ``can_be_edited`` property set to True.
+
+ .. include:: /_includes/usable-by/users-bots.rst
+
+ Parameters:
+ language_code (``str``, *optional*):
+ A two-letter ISO 639-1 language code or an empty string
+
+ for_my_bot (``int`` | ``str``, *optional*):
+ Unique identifier (int) or username (str) of the bot for which profile photo has to be updated instead of the current user.
+ The bot should have ``can_be_edited`` property set to True.
+
+ Returns:
+ ``str``: On success, returns the text shown in the chat with a bot if the chat is empty in the given language.
+
+ Example:
+ .. code-block:: python
+
+ bot_description = await app.get_bot_info_description()
+ """
+
+ bot_info = await self.invoke(
+ raw.functions.bots.GetBotInfo(
+ bot=await self.resolve_peer(for_my_bot) if for_my_bot else None,
+ lang_code=language_code
+ )
+ )
+ return bot_info.description
diff --git a/pyrogram/methods/bots/get_bot_info_short_description.py b/pyrogram/methods/bots/get_bot_info_short_description.py
new file mode 100644
index 0000000000..c78eb186c5
--- /dev/null
+++ b/pyrogram/methods/bots/get_bot_info_short_description.py
@@ -0,0 +1,62 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+from typing import Union
+
+import pyrogram
+from pyrogram import raw
+
+
+class GetBotInfoShortDescription:
+ async def get_bot_info_short_description(
+ self: "pyrogram.Client",
+ language_code: str = "",
+ for_my_bot: Union[int, str] = None,
+ ) -> str:
+ """Use this method to get the current / owned bot short description for the given user language.
+
+ .. note::
+
+ If the current account is an User, can be called only if the ``for_my_bot`` has ``can_be_edited`` property set to True.
+
+ .. include:: /_includes/usable-by/users-bots.rst
+
+ Parameters:
+ language_code (``str``, *optional*):
+ A two-letter ISO 639-1 language code or an empty string
+
+ for_my_bot (``int`` | ``str``, *optional*):
+ Unique identifier (int) or username (str) of the bot for which profile photo has to be updated instead of the current user.
+ The bot should have ``can_be_edited`` property set to True.
+
+ Returns:
+ ``str``: On success, returns the text shown on a bot's profile page and sent together with the link when users share the bot in the given language.
+
+ Example:
+ .. code-block:: python
+
+ bot_short_description = await app.get_bot_info_short_description()
+ """
+
+ bot_info = await self.invoke(
+ raw.functions.bots.GetBotInfo(
+ bot=await self.resolve_peer(for_my_bot) if for_my_bot else None,
+ lang_code=language_code
+ )
+ )
+ return bot_info.about
diff --git a/pyrogram/methods/bots/get_bot_name.py b/pyrogram/methods/bots/get_bot_name.py
new file mode 100644
index 0000000000..e2775b89fc
--- /dev/null
+++ b/pyrogram/methods/bots/get_bot_name.py
@@ -0,0 +1,62 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+from typing import Union
+
+import pyrogram
+from pyrogram import raw
+
+
+class GetBotName:
+ async def get_bot_name(
+ self: "pyrogram.Client",
+ language_code: str = "",
+ for_my_bot: Union[int, str] = None,
+ ) -> str:
+ """Use this method to get the current / owned bot name for the given user language.
+
+ .. note::
+
+ If the current account is an User, can be called only if the ``for_my_bot`` has ``can_be_edited`` property set to True.
+
+ .. include:: /_includes/usable-by/users-bots.rst
+
+ Parameters:
+ language_code (``str``, *optional*):
+ A two-letter ISO 639-1 language code or an empty string
+
+ for_my_bot (``int`` | ``str``, *optional*):
+ Unique identifier (int) or username (str) of the bot for which profile photo has to be updated instead of the current user.
+ The bot should have ``can_be_edited`` property set to True.
+
+ Returns:
+ ``str``: On success, returns the name of a bot in the given language.
+
+ Example:
+ .. code-block:: python
+
+ bot_name = await app.get_bot_name()
+ """
+
+ bot_info = await self.invoke(
+ raw.functions.bots.GetBotInfo(
+ bot=await self.resolve_peer(for_my_bot) if for_my_bot else None,
+ lang_code=language_code
+ )
+ )
+ return bot_info.name
diff --git a/pyrogram/methods/bots/send_game.py b/pyrogram/methods/bots/send_game.py
index 6551693c32..34f5d739d2 100644
--- a/pyrogram/methods/bots/send_game.py
+++ b/pyrogram/methods/bots/send_game.py
@@ -16,11 +16,14 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see .
+import logging
from typing import Union
import pyrogram
from pyrogram import raw, utils, types
+log = logging.getLogger(__name__)
+
class SendGame:
async def send_game(
@@ -31,6 +34,7 @@ async def send_game(
protect_content: bool = None,
message_thread_id: int = None,
business_connection_id: str = None,
+ message_effect_id: int = None,
reply_parameters: "types.ReplyParameters" = None,
reply_markup: Union[
"types.InlineKeyboardMarkup",
@@ -58,7 +62,7 @@ async def send_game(
Users will receive a notification with no sound.
protect_content (``bool``, *optional*):
- Protects the contents of the sent message from forwarding and saving.
+ Pass True if the content of the message must be protected from forwarding and saving; for bots only.
message_thread_id (``int``, *optional*):
If the message is in a thread, ID of the original message.
@@ -66,6 +70,9 @@ async def send_game(
business_connection_id (``str``, *optional*):
Unique identifier of the business connection on behalf of which the message will be sent.
+ message_effect_id (``int`` ``64-bit``, *optional*):
+ Unique identifier of the message effect to be added to the message; for private chats only.
+
reply_parameters (:obj:`~pyrogram.types.ReplyParameters`, *optional*):
Description of the message to reply to
@@ -113,6 +120,7 @@ async def send_game(
reply_to=reply_to,
random_id=self.rnd_id(),
noforwards=protect_content,
+ effect=message_effect_id,
reply_markup=await reply_markup.write(self) if reply_markup else None
)
if business_connection_id:
@@ -130,7 +138,8 @@ async def send_game(
return await types.Message._parse(
self, i.message,
{i.id: i for i in r.users},
- {i.id: i for i in r.chats}
+ {i.id: i for i in r.chats},
+ replies=self.fetch_replies
)
elif isinstance(
i,
@@ -144,5 +153,6 @@ async def send_game(
{i.id: i for i in r.users},
{i.id: i for i in r.chats},
business_connection_id=i.connection_id,
- raw_reply_to_message=i.reply_to_message
+ raw_reply_to_message=i.reply_to_message,
+ replies=0
)
diff --git a/pyrogram/methods/bots/send_web_app_custom_request.py b/pyrogram/methods/bots/send_web_app_custom_request.py
new file mode 100644
index 0000000000..8bf1a950b5
--- /dev/null
+++ b/pyrogram/methods/bots/send_web_app_custom_request.py
@@ -0,0 +1,61 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+from typing import Union
+
+import pyrogram
+from pyrogram import raw, types
+
+
+class SendWebAppCustomRequest:
+ async def send_web_app_custom_request(
+ self: "pyrogram.Client",
+ bot_user_id: Union[int, str],
+ method: str,
+ parameters: str
+ ) -> str:
+ """Sends a custom request from a Web App.
+
+ .. include:: /_includes/usable-by/users.rst
+
+ Parameters:
+ bot_user_id (``int`` | ``str``):
+ Unique identifier of the inline bot you want to get results from. You can specify
+ a @username (str) or a bot ID (int).
+
+ method (``str``):
+ The method name.
+
+ parameters (``str``):
+ JSON-serialized method parameters.
+
+ Returns:
+ ``str``: On success, a JSON-serialized result is returned.
+ """
+
+ r = await self.invoke(
+ raw.functions.bots.InvokeWebViewCustomMethod(
+ bot=await self.resolve_peer(bot_user_id),
+ custom_method=method,
+ params=raw.types.DataJSON(
+ data=parameters
+ )
+ )
+ )
+
+ return r.data
diff --git a/pyrogram/methods/bots/set_bot_info_description.py b/pyrogram/methods/bots/set_bot_info_description.py
new file mode 100644
index 0000000000..6be130aca4
--- /dev/null
+++ b/pyrogram/methods/bots/set_bot_info_description.py
@@ -0,0 +1,66 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+from typing import Union
+
+import pyrogram
+from pyrogram import raw
+
+
+class SetBotInfoDescription:
+ async def set_bot_info_description(
+ self: "pyrogram.Client",
+ description: str,
+ language_code: str = "",
+ for_my_bot: Union[int, str] = None,
+ ) -> bool:
+ """Use this method to change the bot's description, which is shown in the chat with the bot if the chat is empty.
+
+ .. note::
+
+ If the current account is an User, can be called only if the ``for_my_bot`` has ``can_be_edited`` property set to True.
+
+ .. include:: /_includes/usable-by/users-bots.rst
+
+ Parameters:
+ description (``str``):
+ New bot description; 0-512 characters. Pass an empty string to remove the dedicated description for the given language.
+
+ language_code (``str``, *optional*):
+ A two-letter ISO 639-1 language code or an empty string
+
+ for_my_bot (``int`` | ``str``, *optional*):
+ Unique identifier (int) or username (str) of the bot for which profile photo has to be updated instead of the current user.
+ The bot should have ``can_be_edited`` property set to True.
+
+ Returns:
+ ``bool``: True on success.
+
+ Example:
+ .. code-block:: python
+
+ await app.set_bot_info_description("")
+ """
+
+ return await self.invoke(
+ raw.functions.bots.SetBotInfo(
+ bot=await self.resolve_peer(for_my_bot) if for_my_bot else None,
+ lang_code=language_code,
+ description=description
+ )
+ )
diff --git a/pyrogram/methods/bots/set_bot_info_short_description.py b/pyrogram/methods/bots/set_bot_info_short_description.py
new file mode 100644
index 0000000000..5a3903d55a
--- /dev/null
+++ b/pyrogram/methods/bots/set_bot_info_short_description.py
@@ -0,0 +1,66 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+from typing import Union
+
+import pyrogram
+from pyrogram import raw
+
+
+class SetBotInfoShortDescription:
+ async def set_bot_info_short_description(
+ self: "pyrogram.Client",
+ short_description: str,
+ language_code: str = "",
+ for_my_bot: Union[int, str] = None,
+ ) -> bool:
+ """Use this method to change the bot's short description, which is shown on the bot's profile page and is sent together with the link when users share the bot.
+
+ .. note::
+
+ If the current account is an User, can be called only if the ``for_my_bot`` has ``can_be_edited`` property set to True.
+
+ .. include:: /_includes/usable-by/users-bots.rst
+
+ Parameters:
+ short_description (``str``):
+ New short description for the bot; 0-120 characters. Pass an empty string to remove the dedicated short description for the given language.
+
+ language_code (``str``, *optional*):
+ A two-letter ISO 639-1 language code or an empty string
+
+ for_my_bot (``int`` | ``str``, *optional*):
+ Unique identifier (int) or username (str) of the bot for which profile photo has to be updated instead of the current user.
+ The bot should have ``can_be_edited`` property set to True.
+
+ Returns:
+ ``bool``: True on success.
+
+ Example:
+ .. code-block:: python
+
+ await app.set_bot_info_short_description("")
+ """
+
+ return await self.invoke(
+ raw.functions.bots.SetBotInfo(
+ bot=await self.resolve_peer(for_my_bot) if for_my_bot else None,
+ lang_code=language_code,
+ about=short_description
+ )
+ )
diff --git a/pyrogram/methods/bots/set_bot_name.py b/pyrogram/methods/bots/set_bot_name.py
new file mode 100644
index 0000000000..34fd2fffeb
--- /dev/null
+++ b/pyrogram/methods/bots/set_bot_name.py
@@ -0,0 +1,66 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+from typing import Union
+
+import pyrogram
+from pyrogram import raw
+
+
+class SetBotName:
+ async def set_bot_name(
+ self: "pyrogram.Client",
+ name: str,
+ language_code: str = "",
+ for_my_bot: Union[int, str] = None,
+ ) -> str:
+ """Use this method to get the current / owned bot name for the given user language.
+
+ .. note::
+
+ If the current account is an User, can be called only if the ``for_my_bot`` has ``can_be_edited`` property set to True.
+
+ .. include:: /_includes/usable-by/users-bots.rst
+
+ Parameters:
+ name (``str``):
+ New bot name; 0-64 characters. Pass an empty string to remove the dedicated name for the given language.
+
+ language_code (``str``, *optional*):
+ A two-letter ISO 639-1 language code or an empty string
+
+ for_my_bot (``int`` | ``str``, *optional*):
+ Unique identifier (int) or username (str) of the bot for which profile photo has to be updated instead of the current user.
+ The bot should have ``can_be_edited`` property set to True.
+
+ Returns:
+ ``bool``: True on success.
+
+ Example:
+ .. code-block:: python
+
+ await app.set_bot_name("Pyrogram Assistant")
+ """
+
+ return await self.invoke(
+ raw.functions.bots.SetBotInfo(
+ bot=await self.resolve_peer(for_my_bot) if for_my_bot else None,
+ lang_code=language_code,
+ name=name
+ )
+ )
diff --git a/pyrogram/methods/bots/set_game_score.py b/pyrogram/methods/bots/set_game_score.py
index b3b58b2362..020e17293d 100644
--- a/pyrogram/methods/bots/set_game_score.py
+++ b/pyrogram/methods/bots/set_game_score.py
@@ -94,7 +94,8 @@ async def set_game_score(
return await types.Message._parse(
self, i.message,
{i.id: i for i in r.users},
- {i.id: i for i in r.chats}
+ {i.id: i for i in r.chats},
+ replies=self.fetch_replies
)
return True
diff --git a/pyrogram/methods/business/__init__.py b/pyrogram/methods/business/__init__.py
index eda3b7f03f..287b8588e4 100644
--- a/pyrogram/methods/business/__init__.py
+++ b/pyrogram/methods/business/__init__.py
@@ -16,10 +16,22 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see .
+from .answer_pre_checkout_query import AnswerPreCheckoutQuery
+from .answer_shipping_query import AnswerShippingQuery
+from .create_invoice_link import CreateInvoiceLink
from .get_business_connection import GetBusinessConnection
+from .get_collectible_item_info import GetCollectibleItemInfo
+from .refund_star_payment import RefundStarPayment
+from .send_invoice import SendInvoice
class TelegramBusiness(
+ AnswerPreCheckoutQuery,
+ AnswerShippingQuery,
+ CreateInvoiceLink,
GetBusinessConnection,
+ GetCollectibleItemInfo,
+ RefundStarPayment,
+ SendInvoice,
):
pass
diff --git a/pyrogram/methods/business/answer_pre_checkout_query.py b/pyrogram/methods/business/answer_pre_checkout_query.py
new file mode 100644
index 0000000000..88a524895e
--- /dev/null
+++ b/pyrogram/methods/business/answer_pre_checkout_query.py
@@ -0,0 +1,67 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+import pyrogram
+from pyrogram import raw
+
+
+class AnswerPreCheckoutQuery:
+ async def answer_pre_checkout_query(
+ self: "pyrogram.Client",
+ pre_checkout_query_id: str,
+ ok: bool,
+ error_message: str = None
+ ):
+ """Once the user has confirmed their payment and shipping details, the API sends the final confirmation in the form of an :obj:`~pyrogram.handlers.PreCheckoutQueryHandler`.
+
+ Use this method to respond to such pre-checkout queries.
+
+ **Note**: The API must receive an answer within 10 seconds after the pre-checkout query was sent.
+
+ .. include:: /_includes/usable-by/bots.rst
+
+ Parameters:
+ pre_checkout_query_id (``str``):
+ Unique identifier for the query to be answered.
+
+ ok (``bool``):
+ Specify True if everything is alright (goods are available, etc.) and the bot is ready to proceed with the order. Use False if there are any problems.
+
+ error_message (``str``, *optional*):
+ Required if ok is False. Error message in human readable form that explains the reason for failure to proceed with the checkout (e.g. "Sorry, somebody just bought the last of our amazing black T-shirts while you were busy filling out your payment details. Please choose a different color or garment!"). Telegram will display this message to the user.
+
+ Returns:
+ ``bool``: True, on success.
+
+ Example:
+ .. code-block:: python
+
+ # Proceed with the order
+ await app.answer_pre_checkout_query(query_id, ok=True)
+
+ # Answer with error message
+ await app.answer_pre_checkout_query(query_id, ok=False, error_message="Error Message displayed to the user")
+
+ """
+ return await self.invoke(
+ raw.functions.messages.SetBotPrecheckoutResults(
+ query_id=int(pre_checkout_query_id),
+ success=ok or None,
+ error=error_message or None
+ )
+ )
diff --git a/pyrogram/methods/business/answer_shipping_query.py b/pyrogram/methods/business/answer_shipping_query.py
new file mode 100644
index 0000000000..a5a5161587
--- /dev/null
+++ b/pyrogram/methods/business/answer_shipping_query.py
@@ -0,0 +1,83 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+from typing import List
+
+import pyrogram
+from pyrogram import raw, types
+
+
+class AnswerShippingQuery:
+ async def answer_shipping_query(
+ self: "pyrogram.Client",
+ shipping_query_id: str,
+ ok: bool,
+ shipping_options: List["types.ShippingOptions"] = None,
+ error_message: str = None
+ ):
+ """If you sent an invoice requesting a shipping address and the parameter ``is_flexible`` was specified, the API sends the confirmation in the form of an :obj:`~pyrogram.handlers.ShippingQueryHandler`.
+
+ Use this method to reply to shipping queries.
+
+ .. include:: /_includes/usable-by/bots.rst
+
+ Parameters:
+ shipping_query_id (``str``):
+ Unique identifier for the query to be answered.
+
+ ok (``bool``):
+ Specify True if everything is alright (goods are available, etc.) and the bot is ready to proceed with the order. Use False if there are any problems.
+
+ shipping_options (List of :obj:`~pyrogram.types.ShippingOptions`, *optional*):
+ Required if ok is True. A array of available shipping options.
+
+ error_message (``str``, *optional*):
+ Required if ok is False. Error message in human readable form that explains the reason for failure to proceed with the checkout (e.g. "Sorry, somebody just bought the last of our amazing black T-shirts while you were busy filling out your payment details. Please choose a different color or garment!"). Telegram will display this message to the user.
+
+ Returns:
+ ``bool``: True, on success.
+
+ Example:
+ .. code-block:: python
+
+ # Proceed with the order
+ await app.answer_shipping_query(query_id, ok=True, shipping_options=shipping_options)
+
+ # Answer with error message
+ await app.answer_shipping_query(query_id, ok=False, error_message="Error Message displayed to the user")
+
+ """
+ r = None
+ if ok:
+ r = await self.invoke(
+ raw.functions.messages.SetBotShippingResults(
+ query_id=int(pre_checkout_query_id),
+ shipping_options=[
+ so.write()
+ for so in shipping_options
+ ]
+ )
+ )
+ else:
+ r = await self.invoke(
+ raw.functions.messages.SetBotShippingResults(
+ query_id=int(pre_checkout_query_id),
+ error=error_message or None
+ )
+ )
+ return r
diff --git a/pyrogram/methods/business/create_invoice_link.py b/pyrogram/methods/business/create_invoice_link.py
new file mode 100644
index 0000000000..6298836445
--- /dev/null
+++ b/pyrogram/methods/business/create_invoice_link.py
@@ -0,0 +1,162 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+import logging
+from typing import List, Optional, Union
+
+import pyrogram
+from pyrogram import enums, raw, utils, types
+
+log = logging.getLogger(__name__)
+
+
+class CreateInvoiceLink:
+ async def create_invoice_link(
+ self: "pyrogram.Client",
+ title: str,
+ description: str,
+ payload: Union[str, bytes],
+ currency: str,
+ prices: List["types.LabeledPrice"],
+ provider_token: str = None,
+ max_tip_amount: int = None,
+ suggested_tip_amounts: List[int] = None,
+ start_parameter: str = None,
+ provider_data: str = None,
+ photo_url: str = None,
+ photo_size: int = None,
+ photo_width: int = None,
+ photo_height: int = None,
+ need_name: bool = None,
+ need_phone_number: bool = None,
+ need_email: bool = None,
+ need_shipping_address: bool = None,
+ send_phone_number_to_provider: bool = None,
+ send_email_to_provider: bool = None,
+ is_flexible: bool = None
+ ) -> str:
+ """Use this method to create a link for an invoice.
+
+ .. include:: /_includes/usable-by/bots.rst
+
+ Parameters:
+ title (``str``):
+ Product name, 1-32 characters.
+
+ description (``str``):
+ Product description, 1-255 characters
+
+ payload (``str`` | ``bytes``):
+ Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user, use for your internal processes.
+
+ currency (``str``):
+ Three-letter ISO 4217 currency code, see `more on currencies `_. Pass ``XTR`` for payments in `Telegram Stars `_.
+
+ prices (List of :obj:`~pyrogram.types.LabeledPrice`):
+ Price breakdown, a JSON-serialized list of components (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.). Must contain exactly one item for payments in `Telegram Stars `_.
+
+ provider_token (``str``, *optional*):
+ Payment provider token, obtained via `@BotFather `_. Pass an empty string for payments in `Telegram Stars `_.
+
+ max_tip_amount (``int``, *optional*):
+ The maximum accepted amount for tips in the smallest units of the currency (integer, **not** float/double). For example, for a maximum tip of ``US$ 1.45`` pass ``max_tip_amount = 145``. See the exp parameter in `currencies.json `_, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies). Defaults to 0. Not supported for payments in `Telegram Stars `_.
+
+ suggested_tip_amounts (List of ``int``, *optional*):
+ An array of suggested amounts of tips in the smallest units of the currency (integer, **not** float/double). At most 4 suggested tip amounts can be specified. The suggested tip amounts must be positive, passed in a strictly increased order and must not exceed ``max_tip_amount``.
+
+ start_parameter (``str``, *optional*):
+ Unique deep-linking parameter. If left empty, **forwarded copies** of the sent message will have a Pay button, allowing multiple users to pay directly from the forwarded message, using the same invoice. If non-empty, forwarded copies of the sent message will have a URL button with a deep link to the bot (instead of a Pay button), with the value used as the start parameter.
+
+ provider_data (``str``, *optional*):
+ JSON-serialized data about the invoice, which will be shared with the payment provider. A detailed description of required fields should be provided by the payment provider.
+
+ photo_url (``str``, *optional*):
+ URL of the product photo for the invoice. Can be a photo of the goods or a marketing image for a service. People like it better when they see what they are paying for.
+
+ photo_size (``int``, *optional*):
+ Photo size in bytes
+
+ photo_width (``int``, *optional*):
+ Photo width
+
+ photo_height (``int``, *optional*):
+ Photo height
+
+ need_name (``bool``, *optional*):
+ Pass True if you require the user's full name to complete the order. Ignored for payments in `Telegram Stars `_.
+
+ need_phone_number (``bool``, *optional*):
+ Pass True if you require the user's phone number to complete the order. Ignored for payments in `Telegram Stars `_.
+
+ need_email (``bool``, *optional*):
+ Pass True if you require the user's email address to complete the order. Ignored for payments in `Telegram Stars `_.
+
+ need_shipping_address (``bool``, *optional*):
+ Pass True if you require the user's shipping address to complete the order. Ignored for payments in `Telegram Stars `_.
+
+ send_phone_number_to_provider (``bool``, *optional*):
+ Pass True if the user's phone number should be sent to the provider. Ignored for payments in `Telegram Stars `_.
+
+ send_email_to_provider (``bool``, *optional*):
+ Pass True if the user's email address should be sent to the provider. Ignored for payments in `Telegram Stars `_.
+
+ is_flexible (``bool``, *optional*):
+ Pass True if the final price depends on the shipping method. Ignored for payments in `Telegram Stars `_.
+
+ Returns:
+ ``str``: On success, the created invoice link is returned.
+
+ """
+
+ rpc = raw.functions.payments.ExportInvoice(
+ invoice_media=raw.types.InputMediaInvoice(
+ title=title,
+ description=description,
+ photo=raw.types.InputWebDocument(
+ url=photo_url,
+ mime_type="image/jpg",
+ size=photo_size,
+ attributes=[
+ raw.types.DocumentAttributeImageSize(
+ w=photo_width,
+ h=photo_height
+ )
+ ]
+ ) if photo_url else None,
+ invoice=raw.types.Invoice(
+ currency=currency,
+ prices=[i.write() for i in prices],
+ test=self.test_mode,
+ name_requested=need_name,
+ phone_requested=need_phone_number,
+ email_requested=need_email,
+ shipping_address_requested=need_shipping_address,
+ flexible=is_flexible,
+ phone_to_provider=send_phone_number_to_provider,
+ email_to_provider=send_email_to_provider
+ ),
+ payload=payload.encode() if isinstance(payload, str) else payload,
+ provider=provider_token,
+ provider_data=raw.types.DataJSON(
+ data=provider_data if provider_data else "{}"
+ ),
+ start_param=start_parameter
+ )
+ )
+ r = await self.invoke(rpc)
+ return r.url
diff --git a/pyrogram/methods/business/get_collectible_item_info.py b/pyrogram/methods/business/get_collectible_item_info.py
new file mode 100644
index 0000000000..8fb93065a3
--- /dev/null
+++ b/pyrogram/methods/business/get_collectible_item_info.py
@@ -0,0 +1,69 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+import pyrogram
+from pyrogram import raw, types
+
+
+class GetCollectibleItemInfo:
+ async def get_collectible_item_info(
+ self: "pyrogram.Client",
+ username: str = None,
+ phone_number: str = None
+ ) -> "types.CollectibleInfo":
+ """Returns information about a given collectible item that was purchased at https://fragment.com
+
+ .. include:: /_includes/usable-by/users.rst
+
+ You must use exactly one of ``username`` OR ``phone_number``.
+
+ Parameters:
+ username (``str``, *optional*):
+ Describes a collectible username that can be purchased at https://fragment.com
+
+ phone_number (``str``, *optional*):
+ Describes a collectible phone number that can be purchased at https://fragment.com
+
+ Returns:
+ :obj:`~pyrogram.types.CollectibleInfo`: On success, a collectible info is returned.
+
+ Example:
+ .. code-block:: python
+
+ username = await app.get_collectible_item_info(username="nerd")
+ print(username)
+ """
+
+ input_collectible = None
+
+ if username:
+ input_collectible = raw.types.InputCollectibleUsername(username=username)
+ elif phone_number:
+ input_collectible = raw.types.InputCollectiblePhone(phone=phone_number)
+ else:
+ raise ValueError(
+ "No argument supplied. Either pass username OR phone_number"
+ )
+
+ r = await self.invoke(
+ raw.functions.fragment.GetCollectibleInfo(
+ collectible=input_collectible
+ )
+ )
+
+ return types.CollectibleItemInfo._parse(r)
diff --git a/pyrogram/methods/business/refund_star_payment.py b/pyrogram/methods/business/refund_star_payment.py
new file mode 100644
index 0000000000..12c4967731
--- /dev/null
+++ b/pyrogram/methods/business/refund_star_payment.py
@@ -0,0 +1,53 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+from typing import Union
+
+import pyrogram
+from pyrogram import raw
+
+
+class RefundStarPayment:
+ async def refund_star_payment(
+ self: "pyrogram.Client",
+ user_id: Union[int, str],
+ telegram_payment_charge_id: str
+ ) -> bool:
+ """Refunds a successful payment in `Telegram Stars `_.
+
+ .. include:: /_includes/usable-by/bots.rst
+
+ Parameters:
+ user_id (``int`` | ``str``):
+ Unique identifier (int) or username (str) of the target user, whose payment will be refunded.
+
+ telegram_payment_charge_id (``str``):
+ Telegram payment identifier.
+
+ Returns:
+ ``bool``: True on success
+
+ """
+
+ r = await self.invoke(
+ raw.functions.payments.RefundStarsCharge(
+ user_id=await self.resolve_peer(user_id),
+ charge_id=telegram_payment_charge_id
+ )
+ )
+ return bool(r)
diff --git a/pyrogram/methods/business/send_invoice.py b/pyrogram/methods/business/send_invoice.py
new file mode 100644
index 0000000000..f7b4b8ec49
--- /dev/null
+++ b/pyrogram/methods/business/send_invoice.py
@@ -0,0 +1,239 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+import logging
+from typing import List, Optional, Union
+
+import pyrogram
+from pyrogram import enums, raw, utils, types
+
+log = logging.getLogger(__name__)
+
+
+class SendInvoice:
+ async def send_invoice(
+ self: "pyrogram.Client",
+ chat_id: Union[int, str],
+ title: str,
+ description: str,
+ payload: Union[str, bytes],
+ currency: str,
+ prices: List["types.LabeledPrice"],
+ message_thread_id: int = None,
+ provider_token: str = None,
+ max_tip_amount: int = None,
+ suggested_tip_amounts: List[int] = None,
+ start_parameter: str = None,
+ provider_data: str = None,
+ photo_url: str = None,
+ photo_size: int = None,
+ photo_width: int = None,
+ photo_height: int = None,
+ need_name: bool = None,
+ need_phone_number: bool = None,
+ need_email: bool = None,
+ need_shipping_address: bool = None,
+ send_phone_number_to_provider: bool = None,
+ send_email_to_provider: bool = None,
+ is_flexible: bool = None,
+ disable_notification: bool = None,
+ protect_content: bool = None,
+ message_effect_id: int = None,
+ reply_parameters: "types.ReplyParameters" = None,
+ reply_markup: Union[
+ "types.InlineKeyboardMarkup",
+ "types.ReplyKeyboardMarkup",
+ "types.ReplyKeyboardRemove",
+ "types.ForceReply"
+ ] = None,
+ caption: str = "",
+ parse_mode: Optional["enums.ParseMode"] = None,
+ caption_entities: List["types.MessageEntity"] = None
+ ) -> "types.Message":
+ """Use this method to send invoices.
+
+ .. include:: /_includes/usable-by/bots.rst
+
+ Parameters:
+ chat_id (``int`` | ``str``):
+ Unique identifier (int) or username (str) of the target chat.
+
+ title (``str``):
+ Product name, 1-32 characters.
+
+ description (``str``):
+ Product description, 1-255 characters
+
+ payload (``str`` | ``bytes``):
+ Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user, use for your internal processes.
+
+ currency (``str``):
+ Three-letter ISO 4217 currency code, see `more on currencies `_. Pass ``XTR`` for payments in `Telegram Stars `_.
+
+ prices (List of :obj:`~pyrogram.types.LabeledPrice`):
+ Price breakdown, a JSON-serialized list of components (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.). Must contain exactly one item for payments in `Telegram Stars `_.
+
+ message_thread_id (``int``, *optional*):
+ If the message is in a thread, ID of the original message.
+
+ provider_token (``str``, *optional*):
+ Payment provider token, obtained via `@BotFather `_. Pass an empty string for payments in `Telegram Stars `_.
+
+ max_tip_amount (``int``, *optional*):
+ The maximum accepted amount for tips in the smallest units of the currency (integer, **not** float/double). For example, for a maximum tip of ``US$ 1.45`` pass ``max_tip_amount = 145``. See the exp parameter in `currencies.json `_, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies). Defaults to 0. Not supported for payments in `Telegram Stars `_.
+
+ suggested_tip_amounts (List of ``int``, *optional*):
+ An array of suggested amounts of tips in the smallest units of the currency (integer, **not** float/double). At most 4 suggested tip amounts can be specified. The suggested tip amounts must be positive, passed in a strictly increased order and must not exceed ``max_tip_amount``.
+
+ start_parameter (``str``, *optional*):
+ Unique deep-linking parameter. If left empty, **forwarded copies** of the sent message will have a Pay button, allowing multiple users to pay directly from the forwarded message, using the same invoice. If non-empty, forwarded copies of the sent message will have a URL button with a deep link to the bot (instead of a Pay button), with the value used as the start parameter.
+
+ provider_data (``str``, *optional*):
+ JSON-serialized data about the invoice, which will be shared with the payment provider. A detailed description of required fields should be provided by the payment provider.
+
+ photo_url (``str``, *optional*):
+ URL of the product photo for the invoice. Can be a photo of the goods or a marketing image for a service. People like it better when they see what they are paying for.
+
+ photo_size (``int``, *optional*):
+ Photo size in bytes
+
+ photo_width (``int``, *optional*):
+ Photo width
+
+ photo_height (``int``, *optional*):
+ Photo height
+
+ need_name (``bool``, *optional*):
+ Pass True if you require the user's full name to complete the order. Ignored for payments in `Telegram Stars `_.
+
+ need_phone_number (``bool``, *optional*):
+ Pass True if you require the user's phone number to complete the order. Ignored for payments in `Telegram Stars `_.
+
+ need_email (``bool``, *optional*):
+ Pass True if you require the user's email address to complete the order. Ignored for payments in `Telegram Stars `_.
+
+ need_shipping_address (``bool``, *optional*):
+ Pass True if you require the user's shipping address to complete the order. Ignored for payments in `Telegram Stars `_.
+
+ send_phone_number_to_provider (``bool``, *optional*):
+ Pass True if the user's phone number should be sent to the provider. Ignored for payments in `Telegram Stars `_.
+
+ send_email_to_provider (``bool``, *optional*):
+ Pass True if the user's email address should be sent to the provider. Ignored for payments in `Telegram Stars `_.
+
+ is_flexible (``bool``, *optional*):
+ Pass True if the final price depends on the shipping method. Ignored for payments in `Telegram Stars `_.
+
+ disable_notification (``bool``, *optional*):
+ Sends the message silently.
+ Users will receive a notification with no sound.
+
+ protect_content (``bool``, *optional*):
+ Pass True if the content of the message must be protected from forwarding and saving; for bots only.
+
+ message_effect_id (``int`` ``64-bit``, *optional*):
+ Unique identifier of the message effect to be added to the message; for private chats only.
+
+ reply_parameters (:obj:`~pyrogram.types.ReplyParameters`, *optional*):
+ Description of the message to reply to
+
+ reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*):
+ Additional interface options. An object for an inline keyboard, custom reply keyboard,
+ instructions to remove reply keyboard or to force a reply from the user.
+
+ caption (``str``, *optional*):
+ Document caption, 0-1024 characters.
+
+ parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*):
+ By default, texts are parsed using both Markdown and HTML styles.
+ You can combine both syntaxes together.
+
+ caption_entities (List of :obj:`~pyrogram.types.MessageEntity`):
+ List of special entities that appear in the caption, which can be specified instead of *parse_mode*.
+
+ Returns:
+ :obj:`~pyrogram.types.Message`: On success, the sent invoice message is returned.
+
+ """
+ reply_to = await utils._get_reply_message_parameters(
+ self,
+ message_thread_id,
+ reply_parameters
+ )
+
+ rpc = raw.functions.messages.SendMedia(
+ peer=await self.resolve_peer(chat_id),
+ media=raw.types.InputMediaInvoice(
+ title=title,
+ description=description,
+ photo=raw.types.InputWebDocument(
+ url=photo_url,
+ mime_type="image/jpg",
+ size=photo_size,
+ attributes=[
+ raw.types.DocumentAttributeImageSize(
+ w=photo_width,
+ h=photo_height
+ )
+ ]
+ ) if photo_url else None,
+ invoice=raw.types.Invoice(
+ currency=currency,
+ prices=[i.write() for i in prices],
+ test=self.test_mode,
+ name_requested=need_name,
+ phone_requested=need_phone_number,
+ email_requested=need_email,
+ shipping_address_requested=need_shipping_address,
+ flexible=is_flexible,
+ phone_to_provider=send_phone_number_to_provider,
+ email_to_provider=send_email_to_provider
+ ),
+ payload=payload.encode() if isinstance(payload, str) else payload,
+ provider=provider_token,
+ provider_data=raw.types.DataJSON(
+ data=provider_data if provider_data else "{}"
+ ),
+ start_param=start_parameter
+ ),
+ silent=disable_notification or None,
+ reply_to=reply_to,
+ random_id=self.rnd_id(),
+ noforwards=protect_content,
+ reply_markup=await reply_markup.write(self) if reply_markup else None,
+ effect=message_effect_id,
+ **await utils.parse_text_entities(self, caption, parse_mode, caption_entities)
+ )
+ r = await self.invoke(rpc)
+
+ for i in r.updates:
+ if isinstance(
+ i,
+ (
+ raw.types.UpdateNewMessage,
+ raw.types.UpdateNewChannelMessage,
+ raw.types.UpdateNewScheduledMessage
+ )
+ ):
+ return await types.Message._parse(
+ self, i.message,
+ {i.id: i for i in r.users},
+ {i.id: i for i in r.chats},
+ is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage),
+ replies=self.fetch_replies
+ )
diff --git a/pyrogram/methods/chat_topics/close_forum_topic.py b/pyrogram/methods/chat_topics/close_forum_topic.py
index 92b8479867..754faead6e 100644
--- a/pyrogram/methods/chat_topics/close_forum_topic.py
+++ b/pyrogram/methods/chat_topics/close_forum_topic.py
@@ -80,5 +80,5 @@ async def close_forum_topic(
{i.id: i for i in r.users},
{i.id: i for i in r.chats},
is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage),
- replies=2
+ replies=self.fetch_replies
)
diff --git a/pyrogram/methods/chat_topics/create_forum_topic.py b/pyrogram/methods/chat_topics/create_forum_topic.py
index e92fb9dcfa..4f8113558a 100644
--- a/pyrogram/methods/chat_topics/create_forum_topic.py
+++ b/pyrogram/methods/chat_topics/create_forum_topic.py
@@ -51,7 +51,7 @@ async def create_forum_topic(
Currently, must be one of 7322096 (0x6FB9F0), 16766590 (0xFFD67E), 13338331 (0xCB86DB), 9367192 (0x8EEE98), 16749490 (0xFF93B2), or 16478047 (0xFB6F5F)
icon_custom_emoji_id (``str``, *optional*):
- Unique identifier of the custom emoji shown as the topic icon. Use :meth:`~pyrogram.Client.getForumTopicIconStickers` to get all allowed custom emoji identifiers.
+ Unique identifier of the custom emoji shown as the topic icon. Use :meth:`~pyrogram.Client.get_forum_topic_icon_stickers` to get all allowed custom emoji identifiers.
send_as (``int`` | ``str``, *optional*):
Unique identifier (int) or username (str) of the as chat.
@@ -94,5 +94,5 @@ async def create_forum_topic(
{i.id: i for i in r.users},
{i.id: i for i in r.chats},
is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage),
- replies=2
+ replies=self.fetch_replies
)
diff --git a/pyrogram/methods/chat_topics/delete_forum_topic.py b/pyrogram/methods/chat_topics/delete_forum_topic.py
index df34c969b8..10e06dc24a 100644
--- a/pyrogram/methods/chat_topics/delete_forum_topic.py
+++ b/pyrogram/methods/chat_topics/delete_forum_topic.py
@@ -30,7 +30,8 @@ async def delete_forum_topic(
message_thread_id: int
) -> int:
"""Use this method to delete a forum topic along with all its messages in a forum supergroup chat.
- The bot must be an administrator in the chat for this to work and must have the can_delete_messages administrator rights.
+ The bot must be an administrator in the chat for this to work and must have the can_delete_messages administrator rights
+ unless the user is creator of the topic, the topic has no messages from other users and has at most 11 messages.
.. include:: /_includes/usable-by/users-bots.rst
diff --git a/pyrogram/methods/chat_topics/edit_forum_topic.py b/pyrogram/methods/chat_topics/edit_forum_topic.py
index daa1a57729..82e229d9b1 100644
--- a/pyrogram/methods/chat_topics/edit_forum_topic.py
+++ b/pyrogram/methods/chat_topics/edit_forum_topic.py
@@ -49,7 +49,7 @@ async def edit_forum_topic(
New topic name, 0-128 characters. If not specified or empty, the current name of the topic will be kept
icon_custom_emoji_id (``str``, *optional*):
- New unique identifier of the custom emoji shown as the topic icon. Use :meth:`~pyrogram.Client.getForumTopicIconStickers` to get all allowed custom emoji identifiers. Pass an empty string to remove the icon. If not specified, the current icon will be kept
+ New unique identifier of the custom emoji shown as the topic icon. Use :meth:`~pyrogram.Client.get_forum_topic_icon_stickers` to get all allowed custom emoji identifiers. Pass an empty string to remove the icon. If not specified, the current icon will be kept
Returns:
:obj:`~pyrogram.types.Message`: On success, the edited message is returned.
@@ -90,5 +90,5 @@ async def edit_forum_topic(
{i.id: i for i in r.users},
{i.id: i for i in r.chats},
is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage),
- replies=2
+ replies=self.fetch_replies
)
diff --git a/pyrogram/methods/chat_topics/get_forum_topic.py b/pyrogram/methods/chat_topics/get_forum_topic.py
index 35066e4fb3..a965491023 100644
--- a/pyrogram/methods/chat_topics/get_forum_topic.py
+++ b/pyrogram/methods/chat_topics/get_forum_topic.py
@@ -76,7 +76,11 @@ async def get_forum_topic(
messages = {}
for message in getattr(r, "messages", []):
messages[message.id] = await types.Message._parse(
- self, message, users, chats, replies=0
+ self,
+ message,
+ users,
+ chats,
+ replies=self.fetch_replies
)
topics = types.List()
diff --git a/pyrogram/methods/chat_topics/get_forum_topics.py b/pyrogram/methods/chat_topics/get_forum_topics.py
index b7931bfc6b..f64d0d2e85 100644
--- a/pyrogram/methods/chat_topics/get_forum_topics.py
+++ b/pyrogram/methods/chat_topics/get_forum_topics.py
@@ -93,7 +93,11 @@ async def get_forum_topics(
for message in getattr(r, "messages", []):
messages[message.id] = await types.Message._parse(
- self, message, users, chats, replies=0
+ self,
+ message,
+ users,
+ chats,
+ replies=self.fetch_replies
)
topics = []
diff --git a/pyrogram/methods/chat_topics/hide_forum_topic.py b/pyrogram/methods/chat_topics/hide_forum_topic.py
index ca81848117..2ff59fe676 100644
--- a/pyrogram/methods/chat_topics/hide_forum_topic.py
+++ b/pyrogram/methods/chat_topics/hide_forum_topic.py
@@ -78,5 +78,5 @@ async def hide_forum_topic(
{i.id: i for i in r.users},
{i.id: i for i in r.chats},
is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage),
- replies=2
+ replies=self.fetch_replies
)
diff --git a/pyrogram/methods/chat_topics/reopen_forum_topic.py b/pyrogram/methods/chat_topics/reopen_forum_topic.py
index 65b72d1e7d..93f1b0307e 100644
--- a/pyrogram/methods/chat_topics/reopen_forum_topic.py
+++ b/pyrogram/methods/chat_topics/reopen_forum_topic.py
@@ -82,5 +82,5 @@ async def reopen_forum_topic(
{i.id: i for i in r.users},
{i.id: i for i in r.chats},
is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage),
- replies=2
+ replies=self.fetch_replies
)
diff --git a/pyrogram/methods/chat_topics/unhide_forum_topic.py b/pyrogram/methods/chat_topics/unhide_forum_topic.py
index 713f5a216d..40324669b2 100644
--- a/pyrogram/methods/chat_topics/unhide_forum_topic.py
+++ b/pyrogram/methods/chat_topics/unhide_forum_topic.py
@@ -78,5 +78,5 @@ async def unhide_forum_topic(
{i.id: i for i in r.users},
{i.id: i for i in r.chats},
is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage),
- replies=2
+ replies=self.fetch_replies
)
diff --git a/pyrogram/methods/chats/__init__.py b/pyrogram/methods/chats/__init__.py
index 4029f5eeeb..39750d2a26 100644
--- a/pyrogram/methods/chats/__init__.py
+++ b/pyrogram/methods/chats/__init__.py
@@ -42,13 +42,14 @@
from .pin_chat_message import PinChatMessage
from .promote_chat_member import PromoteChatMember
from .restrict_chat_member import RestrictChatMember
+from .search_chats import SearchChats
from .set_administrator_title import SetAdministratorTitle
from .set_chat_description import SetChatDescription
from .set_chat_permissions import SetChatPermissions
from .set_chat_photo import SetChatPhoto
from .set_chat_protected_content import SetChatProtectedContent
from .set_chat_title import SetChatTitle
-from .set_chat_ttl import SetChatTTL
+from .set_chat_message_auto_delete_time import SetChatMessageAutoDeleteTime
from .set_chat_username import SetChatUsername
from .set_send_as_chat import SetSendAsChat
from .set_slow_mode import SetSlowMode
@@ -57,6 +58,7 @@
from .unpin_all_chat_messages import UnpinAllChatMessages
from .unpin_chat_message import UnpinChatMessage
from .get_created_chats import GetCreatedChats
+from .transfer_chat_ownership import TransferChatOwnership
class Chats(
@@ -66,6 +68,7 @@ class Chats(
BanChatMember,
UnbanChatMember,
RestrictChatMember,
+ SearchChats,
PromoteChatMember,
GetChatMembers,
GetChatMember,
@@ -73,7 +76,7 @@ class Chats(
DeleteChatPhoto,
SetChatTitle,
SetChatDescription,
- SetChatTTL,
+ SetChatMessageAutoDeleteTime,
PinChatMessage,
UnpinChatMessage,
GetDialogs,
@@ -101,5 +104,6 @@ class Chats(
SetSendAsChat,
SetChatProtectedContent,
GetCreatedChats,
+ TransferChatOwnership,
):
pass
diff --git a/pyrogram/methods/chats/add_chat_members.py b/pyrogram/methods/chats/add_chat_members.py
index 573220c39f..85ae8fe8b7 100644
--- a/pyrogram/methods/chats/add_chat_members.py
+++ b/pyrogram/methods/chats/add_chat_members.py
@@ -106,9 +106,11 @@ async def add_chat_members(
):
_rc.append(
await types.Message._parse(
- self, i.message,
+ self,
+ i.message,
{i.id: i for i in rr.updates.users},
{i.id: i for i in rr.updates.chats},
+ replies=self.fetch_replies
)
)
break
diff --git a/pyrogram/methods/chats/ban_chat_member.py b/pyrogram/methods/chats/ban_chat_member.py
index 635c48b5da..79769aa640 100644
--- a/pyrogram/methods/chats/ban_chat_member.py
+++ b/pyrogram/methods/chats/ban_chat_member.py
@@ -29,7 +29,8 @@ async def ban_chat_member(
self: "pyrogram.Client",
chat_id: Union[int, str],
user_id: Union[int, str],
- until_date: datetime = utils.zero_datetime()
+ until_date: datetime = utils.zero_datetime(),
+ revoke_messages: bool = None
) -> Union["types.Message", bool]:
"""Ban a user from a group, a supergroup or a channel.
In the case of supergroups and channels, the user will not be able to return to the group on their own using
@@ -56,6 +57,10 @@ async def ban_chat_member(
If user is banned for more than 366 days or less than 30 seconds from the current time they are
considered to be banned forever. Defaults to epoch (ban forever).
+ revoke_messages (``bool``, *optional*):
+ Pass True to delete all messages from the chat for the user that is being removed. If False, the user will be able to see messages in the group that were sent before the user was removed.
+ Always True for supergroups and channels.
+
Returns:
:obj:`~pyrogram.types.Message` | ``bool``: On success, a service message will be returned (when applicable),
otherwise, in case a message object couldn't be returned, True is returned.
@@ -96,16 +101,19 @@ async def ban_chat_member(
r = await self.invoke(
raw.functions.messages.DeleteChatUser(
chat_id=abs(chat_id),
- user_id=user_peer
+ user_id=user_peer,
+ revoke_history=revoke_messages
)
)
for i in r.updates:
if isinstance(i, (raw.types.UpdateNewMessage, raw.types.UpdateNewChannelMessage)):
return await types.Message._parse(
- self, i.message,
+ self,
+ i.message,
{i.id: i for i in r.users},
- {i.id: i for i in r.chats}
+ {i.id: i for i in r.chats},
+ replies=self.fetch_replies
)
else:
return True
diff --git a/pyrogram/methods/chats/get_dialogs.py b/pyrogram/methods/chats/get_dialogs.py
index 3509ccbcca..c991a4e362 100644
--- a/pyrogram/methods/chats/get_dialogs.py
+++ b/pyrogram/methods/chats/get_dialogs.py
@@ -25,7 +25,8 @@
class GetDialogs:
async def get_dialogs(
self: "pyrogram.Client",
- limit: int = 0
+ limit: int = 0,
+ chat_list: int = 0
) -> Optional[AsyncGenerator["types.Dialog", None]]:
"""Get a user's dialogs sequentially.
@@ -35,6 +36,9 @@ async def get_dialogs(
limit (``int``, *optional*):
Limits the number of dialogs to be retrieved.
By default, no limit is applied and all dialogs are returned.
+
+ chat_list (``int``, *optional*):
+ Chat list in which to search messages; Only Main (0) and Archive (1) chat lists are supported. Defaults to (0) Main chat list.
Returns:
``Generator``: A generator yielding :obj:`~pyrogram.types.Dialog` objects.
@@ -61,7 +65,8 @@ async def get_dialogs(
offset_id=offset_id,
offset_peer=offset_peer,
limit=limit,
- hash=0
+ hash=0,
+ folder_id=chat_list
),
sleep_threshold=60
)
@@ -76,7 +81,13 @@ async def get_dialogs(
continue
chat_id = utils.get_peer_id(message.peer_id)
- messages[chat_id] = await types.Message._parse(self, message, users, chats)
+ messages[chat_id] = await types.Message._parse(
+ self,
+ message,
+ users,
+ chats,
+ replies=self.fetch_replies
+ )
dialogs = []
diff --git a/pyrogram/methods/chats/get_dialogs_count.py b/pyrogram/methods/chats/get_dialogs_count.py
index ae22eb5cff..8a93527f3c 100644
--- a/pyrogram/methods/chats/get_dialogs_count.py
+++ b/pyrogram/methods/chats/get_dialogs_count.py
@@ -23,7 +23,8 @@
class GetDialogsCount:
async def get_dialogs_count(
self: "pyrogram.Client",
- pinned_only: bool = False
+ pinned_only: bool = False,
+ chat_list: int = 0
) -> int:
"""Get the total count of your dialogs.
@@ -33,6 +34,9 @@ async def get_dialogs_count(
pinned_only (``bool``, *optional*):
Pass True if you want to count only pinned dialogs.
Defaults to False.
+
+ chat_list (``int``, *optional*):
+ Chat list in which to search messages; Only Main (0) and Archive (1) chat lists are supported. Defaults to (0) Main chat list.
Returns:
``int``: On success, the dialogs count is returned.
@@ -45,7 +49,7 @@ async def get_dialogs_count(
"""
if pinned_only:
- return len((await self.invoke(raw.functions.messages.GetPinnedDialogs(folder_id=0))).dialogs)
+ return len((await self.invoke(raw.functions.messages.GetPinnedDialogs(folder_id=chat_list))).dialogs)
else:
r = await self.invoke(
raw.functions.messages.GetDialogs(
@@ -53,7 +57,8 @@ async def get_dialogs_count(
offset_id=0,
offset_peer=raw.types.InputPeerEmpty(),
limit=1,
- hash=0
+ hash=0,
+ folder_id=chat_list
)
)
diff --git a/pyrogram/methods/chats/pin_chat_message.py b/pyrogram/methods/chats/pin_chat_message.py
index 8ec06e7b13..5e190c5c5e 100644
--- a/pyrogram/methods/chats/pin_chat_message.py
+++ b/pyrogram/methods/chats/pin_chat_message.py
@@ -21,6 +21,8 @@
import pyrogram
from pyrogram import raw, types
+from ..messages.inline_session import get_session
+
class PinChatMessage:
async def pin_chat_message(
@@ -29,6 +31,7 @@ async def pin_chat_message(
message_id: int,
disable_notification: bool = False,
both_sides: bool = False,
+ business_connection_id: str = None,
) -> "types.Message":
"""Pin a message in a group, channel or your own chat.
You must be an administrator in the chat for this to work and must have the "can_pin_messages" admin right in
@@ -51,6 +54,9 @@ async def pin_chat_message(
Pass True to pin the message for both sides (you and recipient).
Applicable to private chats only. Defaults to False.
+ business_connection_id (``str``, *optional*):
+ Unique identifier of the business connection on behalf of which the message will be pinned.
+
Returns:
:obj:`~pyrogram.types.Message`: On success, the service message is returned.
@@ -63,19 +69,65 @@ async def pin_chat_message(
# Pin without notification
await app.pin_chat_message(chat_id, message_id, disable_notification=True)
"""
- r = await self.invoke(
- raw.functions.messages.UpdatePinnedMessage(
- peer=await self.resolve_peer(chat_id),
- id=message_id,
- silent=disable_notification or None,
- pm_oneside=not both_sides or None
- )
+ rpc = raw.functions.messages.UpdatePinnedMessage(
+ peer=await self.resolve_peer(chat_id),
+ id=message_id,
+ silent=disable_notification or None,
+ pm_oneside=not both_sides or None
)
+ session = None
+ business_connection = None
+ if business_connection_id:
+ business_connection = self.business_user_connection_cache[business_connection_id]
+ if not business_connection:
+ business_connection = await self.get_business_connection(business_connection_id)
+ session = await get_session(
+ self,
+ business_connection._raw.connection.dc_id
+ )
+
+ if business_connection_id:
+ r = await session.invoke(
+ raw.functions.InvokeWithBusinessConnection(
+ query=rpc,
+ connection_id=business_connection_id
+ )
+ )
+ # await session.stop()
+ else:
+ r = await self.invoke(rpc)
+
users = {u.id: u for u in r.users}
chats = {c.id: c for c in r.chats}
for i in r.updates:
- if isinstance(i, (raw.types.UpdateNewMessage,
- raw.types.UpdateNewChannelMessage)):
- return await types.Message._parse(self, i.message, users, chats)
+ if isinstance(
+ i,
+ (
+ raw.types.UpdateNewMessage,
+ raw.types.UpdateNewChannelMessage
+ )
+ ):
+ return await types.Message._parse(
+ self,
+ i.message,
+ users,
+ chats,
+ replies=self.fetch_replies
+ )
+ elif isinstance(
+ i,
+ (
+ raw.types.UpdateBotNewBusinessMessage
+ )
+ ):
+ return await types.Message._parse(
+ self,
+ i.message,
+ {i.id: i for i in r.users},
+ {i.id: i for i in r.chats},
+ business_connection_id=getattr(i, "connection_id", business_connection_id),
+ raw_reply_to_message=i.reply_to_message,
+ replies=0
+ )
diff --git a/pyrogram/methods/chats/promote_chat_member.py b/pyrogram/methods/chats/promote_chat_member.py
index 1100559332..0e82452cbb 100644
--- a/pyrogram/methods/chats/promote_chat_member.py
+++ b/pyrogram/methods/chats/promote_chat_member.py
@@ -98,7 +98,8 @@ async def promote_chat_member(
return await types.Message._parse(
self, i.message,
{i.id: i for i in r.users},
- {i.id: i for i in r.chats}
+ {i.id: i for i in r.chats},
+ replies=self.fetch_replies
)
else:
return True
diff --git a/pyrogram/methods/chats/search_chats.py b/pyrogram/methods/chats/search_chats.py
new file mode 100644
index 0000000000..d5d3e61a9d
--- /dev/null
+++ b/pyrogram/methods/chats/search_chats.py
@@ -0,0 +1,81 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+from typing import Union
+
+import pyrogram
+from pyrogram import raw, types, utils
+
+
+class SearchChats:
+ async def search_chats(
+ self: "pyrogram.Client",
+ query: str,
+ limit: int = 10,
+ personalize_result: bool = False
+ ) -> bool:
+ """Searches for the specified query in the title and username of already known chats via request to the server.
+
+ .. include:: /_includes/usable-by/users.rst
+
+ Parameters:
+ query (``str``):
+ Query to search for.
+
+ limit (``int``, *optional*):
+ The maximum number of chats to be returned. Defaults to 10.
+
+ personalize_result (``bool``, *optional*):
+ True, if should return personalized results, else would return all found user identifiers. Defaults to False.
+
+ Returns:
+ List[:obj:`~pyrogram.types.Chat`]: Returns chats in the order seen in the main chat list
+
+ Example:
+ .. code-block:: python
+
+ chats = await app.search_chats("Pyrogram")
+ """
+ r = await self.invoke(
+ raw.functions.contacts.Search(
+ q=query,
+ limit=limit
+ )
+ )
+ users = {i.id: i for i in r.users}
+ chats = {i.id: i for i in r.chats}
+ c = []
+ attr = "my_results" if personalize_result else "results"
+ m = getattr(r, attr, [])
+ for o in m:
+ id = utils.get_raw_peer_id(o)
+ if isinstance(o, raw.types.PeerUser):
+ c.append(
+ types.Chat._parse_chat(
+ self,
+ users[id]
+ )
+ )
+ else:
+ c.append(
+ types.Chat._parse_chat(
+ self,
+ chats[id]
+ )
+ )
+ return types.List(c)
diff --git a/pyrogram/methods/chats/set_chat_ttl.py b/pyrogram/methods/chats/set_chat_message_auto_delete_time.py
similarity index 62%
rename from pyrogram/methods/chats/set_chat_ttl.py
rename to pyrogram/methods/chats/set_chat_message_auto_delete_time.py
index 2baeeb900e..50d434a123 100644
--- a/pyrogram/methods/chats/set_chat_ttl.py
+++ b/pyrogram/methods/chats/set_chat_message_auto_delete_time.py
@@ -19,25 +19,25 @@
from typing import Union
import pyrogram
-from pyrogram import raw
-from pyrogram import types
+from pyrogram import raw, types
-class SetChatTTL:
- async def set_chat_ttl(
+class SetChatMessageAutoDeleteTime:
+ async def set_chat_message_auto_delete_time(
self: "pyrogram.Client",
chat_id: Union[int, str],
- ttl_seconds: int
+ message_auto_delete_time: int
) -> "types.Message":
- """Set the time-to-live for the chat.
+ """Changes the message auto-delete or self-destruct (for secret chats) time in a chat.
+
+ Requires change_info administrator right in basic groups, supergroups and channels.
Parameters:
chat_id (``int`` | ``str``):
Unique identifier (int) or username (str) of the target chat.
- ttl_seconds (``int``):
- The time-to-live for the chat.
- Either 86000 for 1 day, 604800 for 1 week or 0 (zero) to disable it.
+ message_auto_delete_time (``int``):
+ New time value, in seconds; unless the chat is secret, it must be from 0 up to 365 * 86400 and be divisible by 86400. If 0, then messages aren't deleted automatically.
Returns:
``bool``: True on success.
@@ -45,19 +45,19 @@ async def set_chat_ttl(
Example:
.. code-block:: python
- # Set TTL for a chat to 1 day
- app.set_chat_ttl(chat_id, 86400)
+ # Set message auto delete for a chat to 1 day
+ app.set_chat_message_auto_delete_time(chat_id, 86400)
- # Set TTL for a chat to 1 week
- app.set_chat_ttl(chat_id, 604800)
+ # Set message auto delete for a chat to 1 week
+ app.set_chat_message_auto_delete_time(chat_id, 604800)
- # Disable TTL for this chat
- app.set_chat_ttl(chat_id, 0)
+ # Disable message auto delete for this chat
+ app.set_chat_message_auto_delete_time(chat_id, 0)
"""
r = await self.invoke(
raw.functions.messages.SetHistoryTTL(
peer=await self.resolve_peer(chat_id),
- period=ttl_seconds,
+ period=message_auto_delete_time,
)
)
@@ -69,4 +69,5 @@ async def set_chat_ttl(
i.message,
{i.id: i for i in r.users},
{i.id: i for i in r.chats},
+ replies=self.fetch_replies
)
diff --git a/pyrogram/methods/chats/transfer_chat_ownership.py b/pyrogram/methods/chats/transfer_chat_ownership.py
new file mode 100644
index 0000000000..e61406c59e
--- /dev/null
+++ b/pyrogram/methods/chats/transfer_chat_ownership.py
@@ -0,0 +1,82 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+from typing import Union
+
+import pyrogram
+from pyrogram import raw
+from pyrogram.utils import compute_password_check
+
+
+class TransferChatOwnership:
+ async def transfer_chat_ownership(
+ self: "pyrogram.Client",
+ chat_id: Union[int, str],
+ user_id: Union[int, str],
+ password: str,
+ ) -> bool:
+ """Changes the owner of a chat.
+
+ Requires owner privileges in the chat. Available only for supergroups and channel chats.
+
+ .. include:: /_includes/usable-by/users.rst
+
+ Parameters:
+ chat_id (``int`` | ``str``):
+ Unique identifier (int) or username (str) of the target chat.
+
+ user_id (``int`` | ``str``):
+ Unique identifier (int) or username (str) of the new owner.
+ The ownership can't be transferred to a bot or to a deleted user.
+
+ password (``str``):
+ The 2-step verification password of the current user.
+
+ Returns:
+ ``bool``: True on success.
+
+ Raises:
+ ValueError: In case of invalid parameters.
+ RPCError: In case of a Telegram RPC error.
+
+ Example:
+ .. code-block:: python
+
+ await app.transfer_chat_ownership(chat_id, user_id, "password")
+ """
+
+ peer_channel = await self.resolve_peer(chat_id)
+ peer_user = await self.resolve_peer(user_id)
+
+ if not isinstance(peer_channel, raw.types.InputPeerChannel):
+ raise ValueError("The chat_id must belong to a channel/supergroup.")
+
+ if not isinstance(peer_user, raw.types.InputPeerUser):
+ raise ValueError("The user_id must belong to a user.")
+
+ r = await self.invoke(
+ raw.functions.channels.EditCreator(
+ channel=peer_channel,
+ user_id=peer_user,
+ password=compute_password_check(
+ await self.invoke(raw.functions.account.GetPassword()), password
+ ),
+ )
+ )
+
+ return bool(r)
diff --git a/pyrogram/methods/chats/unpin_chat_message.py b/pyrogram/methods/chats/unpin_chat_message.py
index 6c8e036e0d..2cf2b47fa4 100644
--- a/pyrogram/methods/chats/unpin_chat_message.py
+++ b/pyrogram/methods/chats/unpin_chat_message.py
@@ -21,12 +21,15 @@
import pyrogram
from pyrogram import raw
+from ..messages.inline_session import get_session
+
class UnpinChatMessage:
async def unpin_chat_message(
self: "pyrogram.Client",
chat_id: Union[int, str],
- message_id: int = 0
+ message_id: int = 0,
+ business_connection_id: str = None,
) -> bool:
"""Unpin a message in a group, channel or your own chat.
You must be an administrator in the chat for this to work and must have the "can_pin_messages" admin
@@ -40,8 +43,12 @@ async def unpin_chat_message(
message_id (``int``, *optional*):
Identifier of a message to unpin.
+ Required if ``business_connection_id`` is specified.
If not specified, the most recent pinned message (by sending date) will be unpinned.
+ business_connection_id (``str``, *optional*):
+ Unique identifier of the business connection on behalf of which the message will be unpinned.
+
Returns:
``bool``: True on success.
@@ -50,12 +57,32 @@ async def unpin_chat_message(
await app.unpin_chat_message(chat_id, message_id)
"""
- await self.invoke(
- raw.functions.messages.UpdatePinnedMessage(
- peer=await self.resolve_peer(chat_id),
- id=message_id,
- unpin=True
- )
+ rpc = raw.functions.messages.UpdatePinnedMessage(
+ peer=await self.resolve_peer(chat_id),
+ id=message_id,
+ unpin=True
)
+
+ session = None
+ business_connection = None
+ if business_connection_id:
+ business_connection = self.business_user_connection_cache[business_connection_id]
+ if not business_connection:
+ business_connection = await self.get_business_connection(business_connection_id)
+ session = await get_session(
+ self,
+ business_connection._raw.connection.dc_id
+ )
+
+ if business_connection_id:
+ r = await session.invoke(
+ raw.functions.InvokeWithBusinessConnection(
+ query=rpc,
+ connection_id=business_connection_id
+ )
+ )
+ # await session.stop()
+ else:
+ r = await self.invoke(rpc)
return True
diff --git a/pyrogram/methods/decorators/__init__.py b/pyrogram/methods/decorators/__init__.py
index d7ab7bcb41..fc90783ed9 100644
--- a/pyrogram/methods/decorators/__init__.py
+++ b/pyrogram/methods/decorators/__init__.py
@@ -30,6 +30,9 @@
from .on_user_status import OnUserStatus
from .on_message_reaction_updated import OnMessageReactionUpdated
from .on_message_reaction_count_updated import OnMessageReactionCountUpdated
+from .on_pre_checkout_query import OnPreCheckoutQuery
+from .on_shipping_query import OnShippingQuery
+from .on_story import OnStory
class Decorators(
@@ -41,8 +44,8 @@ class Decorators(
OnInlineQuery,
OnChosenInlineResult,
OnCallbackQuery,
-
-
+ OnShippingQuery,
+ OnPreCheckoutQuery,
OnPoll,
OnChatMemberUpdated,
@@ -51,6 +54,7 @@ class Decorators(
OnDisconnect,
OnUserStatus,
+ OnStory,
OnRawUpdate,
):
pass
diff --git a/pyrogram/methods/decorators/on_pre_checkout_query.py b/pyrogram/methods/decorators/on_pre_checkout_query.py
new file mode 100644
index 0000000000..0a28edb718
--- /dev/null
+++ b/pyrogram/methods/decorators/on_pre_checkout_query.py
@@ -0,0 +1,64 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+from typing import Callable, Optional, Union
+
+import pyrogram
+from pyrogram.filters import Filter
+
+
+class OnPreCheckoutQuery:
+ def on_pre_checkout_query(
+ self=None,
+ filters=None,
+ group: int = 0,
+ ) -> Callable:
+ """Decorator for handling pre-checkout queries.
+
+ This does the same thing as :meth:`~pyrogram.Client.add_handler` using the
+ :obj:`~pyrogram.handlers.PreCheckoutQueryHandler`.
+
+ .. include:: /_includes/usable-by/bots.rst
+
+ Parameters:
+ filters (:obj:`~pyrogram.filters`, *optional*):
+ Pass one or more filters to allow only a subset of callback queries to be passed
+ in your function.
+
+ group (``int``, *optional*):
+ The group identifier, defaults to 0.
+
+ """
+
+ def decorator(func: Callable) -> Callable:
+ if isinstance(self, pyrogram.Client):
+ self.add_handler(pyrogram.handlers.PreCheckoutQueryHandler(func, filters), group)
+ elif isinstance(self, Filter) or self is None:
+ if not hasattr(func, "handlers"):
+ func.handlers = []
+
+ func.handlers.append(
+ (
+ pyrogram.handlers.PreCheckoutQueryHandler(func, self),
+ group if filters is None else filters
+ )
+ )
+
+ return func
+
+ return decorator
diff --git a/pyrogram/methods/decorators/on_raw_update.py b/pyrogram/methods/decorators/on_raw_update.py
index 1c50378ea4..644bc8a5b9 100644
--- a/pyrogram/methods/decorators/on_raw_update.py
+++ b/pyrogram/methods/decorators/on_raw_update.py
@@ -31,8 +31,6 @@ def on_raw_update(
This does the same thing as :meth:`~pyrogram.Client.add_handler` using the
:obj:`~pyrogram.handlers.RawUpdateHandler`.
- .. include:: /_includes/usable-by/users-bots.rst
-
Parameters:
group (``int``, *optional*):
The group identifier, defaults to 0.
diff --git a/pyrogram/methods/decorators/on_shipping_query.py b/pyrogram/methods/decorators/on_shipping_query.py
new file mode 100644
index 0000000000..e8962ef4ac
--- /dev/null
+++ b/pyrogram/methods/decorators/on_shipping_query.py
@@ -0,0 +1,64 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+from typing import Callable, Optional, Union
+
+import pyrogram
+from pyrogram.filters import Filter
+
+
+class OnShippingQuery:
+ def on_shipping_query(
+ self=None,
+ filters=None,
+ group: int = 0,
+ ) -> Callable:
+ """Decorator for handling shipping queries.
+
+ This does the same thing as :meth:`~pyrogram.Client.add_handler` using the
+ :obj:`~pyrogram.handlers.ShippingQueryHandler`.
+
+ .. include:: /_includes/usable-by/bots.rst
+
+ Parameters:
+ filters (:obj:`~pyrogram.filters`, *optional*):
+ Pass one or more filters to allow only a subset of callback queries to be passed
+ in your function.
+
+ group (``int``, *optional*):
+ The group identifier, defaults to 0.
+
+ """
+
+ def decorator(func: Callable) -> Callable:
+ if isinstance(self, pyrogram.Client):
+ self.add_handler(pyrogram.handlers.ShippingQueryHandler(func, filters), group)
+ elif isinstance(self, Filter) or self is None:
+ if not hasattr(func, "handlers"):
+ func.handlers = []
+
+ func.handlers.append(
+ (
+ pyrogram.handlers.ShippingQueryHandler(func, self),
+ group if filters is None else filters
+ )
+ )
+
+ return func
+
+ return decorator
diff --git a/pyrogram/methods/decorators/on_story.py b/pyrogram/methods/decorators/on_story.py
new file mode 100644
index 0000000000..ea0407b9a0
--- /dev/null
+++ b/pyrogram/methods/decorators/on_story.py
@@ -0,0 +1,64 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+from typing import Callable, Optional, Union
+
+import pyrogram
+from pyrogram.filters import Filter
+
+
+class OnStory:
+ def on_story(
+ self: Union["OnStory", Filter, None] = None,
+ filters: Optional[Filter] = None,
+ group: int = 0,
+ ) -> Callable:
+ """Decorator for handling new stories.
+
+ This does the same thing as :meth:`~pyrogram.Client.add_handler` using the
+ :obj:`~pyrogram.handlers.StoryHandler`.
+
+ .. include:: /_includes/usable-by/users.rst
+
+ Parameters:
+ filters (:obj:`~pyrogram.filters`, *optional*):
+ Pass one or more filters to allow only a subset of stories to be passed
+ in your function.
+
+ group (``int``, *optional*):
+ The group identifier, defaults to 0.
+
+ """
+
+ def decorator(func: Callable) -> Callable:
+ if isinstance(self, pyrogram.Client):
+ self.add_handler(pyrogram.handlers.StoryHandler(func, filters), group)
+ elif isinstance(self, Filter) or self is None:
+ if not hasattr(func, "handlers"):
+ func.handlers = []
+
+ func.handlers.append(
+ (
+ pyrogram.handlers.StoryHandler(func, self),
+ group if filters is None else filters
+ )
+ )
+
+ return func
+
+ return decorator
diff --git a/pyrogram/methods/messages/__init__.py b/pyrogram/methods/messages/__init__.py
index 8fc44b7634..2328d02a99 100644
--- a/pyrogram/methods/messages/__init__.py
+++ b/pyrogram/methods/messages/__init__.py
@@ -20,6 +20,7 @@
from .copy_message import CopyMessage
from .delete_messages import DeleteMessages
from .download_media import DownloadMedia
+from .edit_cached_media import EditCachedMedia
from .edit_inline_caption import EditInlineCaption
from .edit_inline_media import EditInlineMedia
from .edit_inline_reply_markup import EditInlineReplyMarkup
@@ -53,11 +54,10 @@
from .send_location import SendLocation
from .send_media_group import SendMediaGroup
from .send_message import SendMessage
+from .send_paid_media import SendPaidMedia
from .send_photo import SendPhoto
from .send_poll import SendPoll
-from .set_reaction import (
- SetReaction
-)
+from .set_reaction import SetReaction
from .send_sticker import SendSticker
from .send_venue import SendVenue
from .send_video import SendVideo
@@ -65,14 +65,19 @@
from .send_voice import SendVoice
from .stop_poll import StopPoll
from .stream_media import StreamMedia
+from .view_messages import ViewMessages
from .vote_poll import VotePoll
-
+from .get_chat_sponsored_messages import GetChatSponsoredMessages
+from .search_public_messages_by_tag import SearchPublicMessagesByTag
+from .count_public_messages_by_tag import CountPublicMessagesByTag
+from .translate_text import TranslateText
class Messages(
CopyMediaGroup,
CopyMessage,
DeleteMessages,
DownloadMedia,
+ EditCachedMedia,
EditInlineCaption,
EditInlineMedia,
EditInlineReplyMarkup,
@@ -96,6 +101,8 @@ class Messages(
SearchGlobalCount,
SearchMessages,
SearchMessagesCount,
+ SearchPublicMessagesByTag,
+ CountPublicMessagesByTag,
SendAnimation,
SendAudio,
SendCachedMedia,
@@ -106,6 +113,7 @@ class Messages(
SendLocation,
SendMediaGroup,
SendMessage,
+ SendPaidMedia,
SendPhoto,
SendPoll,
SendSticker,
@@ -116,6 +124,9 @@ class Messages(
SetReaction,
StopPoll,
StreamMedia,
+ ViewMessages,
VotePoll,
+ GetChatSponsoredMessages,
+ TranslateText,
):
pass
diff --git a/pyrogram/methods/messages/copy_media_group.py b/pyrogram/methods/messages/copy_media_group.py
index dbc6206443..e145ee49c1 100644
--- a/pyrogram/methods/messages/copy_media_group.py
+++ b/pyrogram/methods/messages/copy_media_group.py
@@ -21,7 +21,7 @@
from typing import Union, List
import pyrogram
-from pyrogram import types, utils, raw
+from pyrogram import enums, raw, types, utils
log = logging.getLogger(__name__)
@@ -109,6 +109,7 @@ async def copy_media_group(
media_group = await self.get_media_group(from_chat_id, message_id)
multi_media = []
+ show_caption_above_media = []
for i, message in enumerate(media_group):
if message.photo:
@@ -123,17 +124,26 @@ async def copy_media_group(
raise ValueError("Message with this type can't be copied.")
media = utils.get_input_media_from_file_id(file_id=file_id)
+
+ sent_message, sent_entities = None, None
+ if isinstance(captions, list) and i < len(captions) and isinstance(captions[i], str):
+ sent_message, sent_entities = (await utils.parse_text_entities(self, captions[i], self.parse_mode, None)).values()
+ elif isinstance(captions, str) and i == 0:
+ sent_message, sent_entities = (await utils.parse_text_entities(self, captions, self.parse_mode, None)).values()
+ elif message.caption and message.caption != "None" and not type(captions) is str: # TODO
+ sent_message, sent_entities = (await utils.parse_text_entities(self, message.caption, None, message.caption_entities)).values()
+ else:
+ sent_message, sent_entities = "", None
+
multi_media.append(
raw.types.InputSingleMedia(
media=media,
random_id=self.rnd_id(),
- **await self.parser.parse(
- captions[i] if isinstance(captions, list) and i < len(captions) and captions[i] else
- captions if isinstance(captions, str) and i == 0 else
- message.caption if message.caption and message.caption != "None" and not type(
- captions) is str else "")
+ message=sent_message,
+ entities=sent_entities
)
)
+ show_caption_above_media.append(message.show_caption_above_media)
reply_to = await utils._get_reply_message_parameters(
self,
@@ -147,7 +157,8 @@ async def copy_media_group(
multi_media=multi_media,
silent=disable_notification or None,
reply_to=reply_to,
- schedule_date=utils.datetime_to_timestamp(schedule_date)
+ schedule_date=utils.datetime_to_timestamp(schedule_date),
+ invert_media=any(show_caption_above_media)
),
sleep_threshold=60
)
diff --git a/pyrogram/methods/messages/copy_message.py b/pyrogram/methods/messages/copy_message.py
index d726acbc86..bba4a6f97f 100644
--- a/pyrogram/methods/messages/copy_message.py
+++ b/pyrogram/methods/messages/copy_message.py
@@ -35,6 +35,7 @@ async def copy_message(
caption: str = None,
parse_mode: Optional["enums.ParseMode"] = None,
caption_entities: List["types.MessageEntity"] = None,
+ show_caption_above_media: bool = None,
disable_notification: bool = None,
reply_parameters: "types.ReplyParameters" = None,
reply_markup: Union[
@@ -44,10 +45,15 @@ async def copy_message(
"types.ForceReply"
] = None,
schedule_date: datetime = None,
+ business_connection_id: str = None,
+ protect_content: bool = None,
+ message_thread_id: int = None,
reply_to_message_id: int = None
) -> "types.Message":
"""Copy messages of any kind.
+ Service messages, paid media messages, giveaway messages, giveaway winners messages, and invoice messages can't be copied. A quiz poll can be copied only if the value of the field ``correct_option_id`` is known to the bot.
+
The method is analogous to the method :meth:`~Client.forward_messages`, but the copied message doesn't have a
link to the original message.
@@ -79,6 +85,9 @@ async def copy_message(
caption_entities (List of :obj:`~pyrogram.types.MessageEntity`):
List of special entities that appear in the new caption, which can be specified instead of *parse_mode*.
+ show_caption_above_media (``bool``, *optional*):
+ Pass True, if the caption must be shown above the message media. Ignored if a new caption isn't specified.
+
disable_notification (``bool``, *optional*):
Sends the message silently.
Users will receive a notification with no sound.
@@ -93,6 +102,15 @@ async def copy_message(
schedule_date (:py:obj:`~datetime.datetime`, *optional*):
Date when the message will be automatically sent.
+ business_connection_id (``str``, *optional*):
+ Unique identifier of the business connection on behalf of which the message will be sent
+
+ protect_content (``bool``, *optional*):
+ Pass True if the content of the message must be protected from forwarding and saving; for bots only.
+
+ message_thread_id (``int``, *optional*):
+ Unique identifier for the target message thread (topic) of the forum; for forum supergroups only
+
Returns:
:obj:`~pyrogram.types.Message`: On success, the copied message is returned.
@@ -127,8 +145,12 @@ async def copy_message(
caption=caption,
parse_mode=parse_mode,
caption_entities=caption_entities,
+ show_caption_above_media=show_caption_above_media,
disable_notification=disable_notification,
reply_parameters=reply_parameters,
reply_markup=reply_markup,
- schedule_date=schedule_date
+ schedule_date=schedule_date,
+ business_connection_id=business_connection_id,
+ protect_content=protect_content,
+ message_thread_id=message_thread_id
)
diff --git a/pyrogram/methods/messages/count_public_messages_by_tag.py b/pyrogram/methods/messages/count_public_messages_by_tag.py
new file mode 100644
index 0000000000..b1cb2cd744
--- /dev/null
+++ b/pyrogram/methods/messages/count_public_messages_by_tag.py
@@ -0,0 +1,55 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+import pyrogram
+from pyrogram import raw
+
+
+class CountPublicMessagesByTag:
+ async def count_public_messages_by_tag(
+ self: "pyrogram.Client",
+ tag: str = "",
+ ) -> int:
+ """Get the count of messages with the provided hashtag or cashtag.
+
+ If you want to get the actual messages, see :meth:`~pyrogram.Client.search_public_messages_by_tag`.
+
+ .. include:: /_includes/usable-by/users.rst
+
+ Parameters:
+ hashtag (``str``, *optional*):
+ Hashtag or cashtag to search for.
+
+ Returns:
+ ``int``: On success, the messages count is returned.
+
+ """
+ r = await self.invoke(
+ raw.functions.channels.SearchPosts(
+ hashtag=tag,
+ offset_rate=0,
+ offset_peer=raw.types.InputPeerEmpty(),
+ offset_id=0,
+ limit=1
+ )
+ )
+
+ if hasattr(r, "count"):
+ return r.count
+ else:
+ return len(r.messages)
diff --git a/pyrogram/methods/messages/download_media.py b/pyrogram/methods/messages/download_media.py
index 225beccb46..b1bdea7e9e 100644
--- a/pyrogram/methods/messages/download_media.py
+++ b/pyrogram/methods/messages/download_media.py
@@ -155,7 +155,9 @@ async def progress(current, total):
media = None
if not media:
- raise ValueError("This message doesn't contain any downloadable media")
+ raise ValueError(
+ f"The message {message if isinstance(message, str) else message.id} doesn't contain any downloadable media"
+ )
if isinstance(media, str):
file_id_str = media
diff --git a/pyrogram/methods/messages/edit_cached_media.py b/pyrogram/methods/messages/edit_cached_media.py
new file mode 100644
index 0000000000..533c47ecc5
--- /dev/null
+++ b/pyrogram/methods/messages/edit_cached_media.py
@@ -0,0 +1,161 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+import logging
+from datetime import datetime
+from typing import Union, List, Optional
+
+import pyrogram
+from pyrogram import raw, enums, types, utils
+
+from .inline_session import get_session
+
+log = logging.getLogger(__name__)
+
+
+class EditCachedMedia:
+ async def edit_cached_media(
+ self: "pyrogram.Client",
+ chat_id: Union[int, str],
+ message_id: int,
+ file_id: str,
+ caption: str = "",
+ parse_mode: Optional["enums.ParseMode"] = None,
+ caption_entities: List["types.MessageEntity"] = None,
+ show_caption_above_media: bool = None,
+ schedule_date: datetime = None,
+ has_spoiler: bool = None,
+ reply_markup: "types.InlineKeyboardMarkup" = None,
+ business_connection_id: str = None
+ ) -> Optional["types.Message"]:
+ """Edit a media stored on the Telegram servers using a file_id.
+
+ This convenience method works with any valid file_id only.
+ It does the same as calling the relevant method for editing media using a file_id, thus saving you from the
+ hassle of using the correct :obj:`~pyrogram.types.InputMedia` for the media the file_id is pointing to.
+
+ .. include:: /_includes/usable-by/users-bots.rst
+
+ Parameters:
+ chat_id (``int`` | ``str``):
+ Unique identifier (int) or username (str) of the target chat.
+ For your personal cloud (Saved Messages) you can simply use "me" or "self".
+ For a contact that exists in your Telegram address book you can use his phone number (str).
+
+ message_id (``int``):
+ Message identifier in the chat specified in chat_id.
+
+ file_id (``str``):
+ Media to send.
+ Pass a file_id as string to send a media that exists on the Telegram servers.
+
+ caption (``str``, *optional*):
+ Media caption, 0-1024 characters.
+
+ parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*):
+ By default, texts are parsed using both Markdown and HTML styles.
+ You can combine both syntaxes together.
+
+ caption_entities (List of :obj:`~pyrogram.types.MessageEntity`):
+ List of special entities that appear in the caption, which can be specified instead of *parse_mode*.
+
+ show_caption_above_media (``bool``, *optional*):
+ Pass True, if the caption must be shown above the message media. Supported only for animation, photo and video messages.
+
+ schedule_date (:py:obj:`~datetime.datetime`, *optional*):
+ Date when the message will be automatically sent.
+
+ has_spoiler (``bool``, *optional*):
+ True, if the message media is covered by a spoiler animation.
+
+ reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup`, *optional*):
+ An InlineKeyboardMarkup object.
+
+ business_connection_id (``str``, *optional*):
+ Unique identifier of the business connection on behalf of which the message to be edited was sent
+
+ Returns:
+ :obj:`~pyrogram.types.Message`: On success, the edited media message is returned.
+
+ Example:
+ .. code-block:: python
+
+ await app.edit_cached_media(chat_id, message_id, file_id)
+ """
+
+ rpc = raw.functions.messages.EditMessage(
+ peer=await self.resolve_peer(chat_id),
+ id=message_id,
+ media=utils.get_input_media_from_file_id(file_id, has_spoiler=has_spoiler),
+ reply_markup=await reply_markup.write(self) if reply_markup else None,
+ schedule_date=utils.datetime_to_timestamp(schedule_date),
+ invert_media=show_caption_above_media,
+ **await utils.parse_text_entities(self, caption, parse_mode, caption_entities)
+ )
+ session = None
+ business_connection = None
+ if business_connection_id:
+ business_connection = self.business_user_connection_cache[business_connection_id]
+ if not business_connection:
+ business_connection = await self.get_business_connection(business_connection_id)
+ session = await get_session(
+ self,
+ business_connection._raw.connection.dc_id
+ )
+ if business_connection_id:
+ r = await session.invoke(
+ raw.functions.InvokeWithBusinessConnection(
+ query=rpc,
+ connection_id=business_connection_id
+ )
+ )
+ # await session.stop()
+ else:
+ r = await self.invoke(rpc)
+
+ for i in r.updates:
+ if isinstance(
+ i,
+ (
+ raw.types.UpdateEditMessage,
+ raw.types.UpdateEditChannelMessage,
+ raw.types.UpdateNewScheduledMessage
+ )
+ ):
+ return await types.Message._parse(
+ self, i.message,
+ {i.id: i for i in r.users},
+ {i.id: i for i in r.chats},
+ is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage),
+ replies=self.fetch_replies
+ )
+ elif isinstance(
+ i,
+ (
+ raw.types.UpdateBotEditBusinessMessage
+ )
+ ):
+ return await types.Message._parse(
+ self,
+ i.message,
+ {i.id: i for i in r.users},
+ {i.id: i for i in r.chats},
+ business_connection_id=getattr(i, "connection_id", business_connection_id),
+ raw_reply_to_message=i.reply_to_message,
+ replies=0
+ )
diff --git a/pyrogram/methods/messages/edit_inline_media.py b/pyrogram/methods/messages/edit_inline_media.py
index 5d4a3701c0..d549ccfbaf 100644
--- a/pyrogram/methods/messages/edit_inline_media.py
+++ b/pyrogram/methods/messages/edit_inline_media.py
@@ -216,7 +216,7 @@ async def edit_inline_media(
file_reference=uploaded_media.photo.file_reference
),
spoiler=getattr(media, "has_spoiler", None)
- ) if isinstance(media, types.InputMediaPhoto) else raw.types.InputMediaDocument(
+ ) if isinstance(uploaded_media, raw.types.MessageMediaPhoto) else raw.types.InputMediaDocument(
id=raw.types.InputDocument(
id=uploaded_media.document.id,
access_hash=uploaded_media.document.access_hash,
diff --git a/pyrogram/methods/messages/edit_inline_text.py b/pyrogram/methods/messages/edit_inline_text.py
index 86e8ac4578..2f0dd30ef1 100644
--- a/pyrogram/methods/messages/edit_inline_text.py
+++ b/pyrogram/methods/messages/edit_inline_text.py
@@ -20,9 +20,7 @@
from typing import Optional, List
import pyrogram
-from pyrogram import raw, enums
-from pyrogram import types
-from pyrogram import utils
+from pyrogram import raw, enums, types, utils
from .inline_session import get_session
log = logging.getLogger(__name__)
@@ -95,6 +93,8 @@ async def edit_inline_text(
)
link_preview_options = types.LinkPreviewOptions(is_disabled=disable_web_page_preview)
+ link_preview_options = link_preview_options or self.link_preview_options
+
unpacked = utils.unpack_inline_message_id(inline_message_id)
dc_id = unpacked.dc_id
diff --git a/pyrogram/methods/messages/edit_message_caption.py b/pyrogram/methods/messages/edit_message_caption.py
index 4ef53ed5d4..6752d00258 100644
--- a/pyrogram/methods/messages/edit_message_caption.py
+++ b/pyrogram/methods/messages/edit_message_caption.py
@@ -31,8 +31,10 @@ async def edit_message_caption(
caption: str,
parse_mode: Optional["enums.ParseMode"] = None,
caption_entities: List["types.MessageEntity"] = None,
+ show_caption_above_media: bool = None,
reply_markup: "types.InlineKeyboardMarkup" = None,
- schedule_date: datetime = None
+ schedule_date: datetime = None,
+ business_connection_id: str = None
) -> "types.Message":
"""Edit the caption of media messages.
@@ -57,12 +59,18 @@ async def edit_message_caption(
caption_entities (List of :obj:`~pyrogram.types.MessageEntity`):
List of special entities that appear in the caption, which can be specified instead of *parse_mode*.
+ show_caption_above_media (``bool``, *optional*):
+ Pass True, if the caption must be shown above the message media. Supported only for animation, photo and video messages.
+
reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup`, *optional*):
An InlineKeyboardMarkup object.
schedule_date (:py:obj:`~datetime.datetime`, *optional*):
Date when the message will be automatically sent.
+ business_connection_id (``str``, *optional*):
+ Unique identifier of the business connection on behalf of which the message to be edited was sent
+
Returns:
:obj:`~pyrogram.types.Message`: On success, the edited message is returned.
@@ -71,6 +79,12 @@ async def edit_message_caption(
await app.edit_message_caption(chat_id, message_id, "new media caption")
"""
+ link_preview_options = None
+ if show_caption_above_media:
+ link_preview_options = types.LinkPreviewOptions(
+ show_above_text=show_caption_above_media
+ )
+
return await self.edit_message_text(
chat_id=chat_id,
message_id=message_id,
@@ -78,5 +92,7 @@ async def edit_message_caption(
parse_mode=parse_mode,
entities=caption_entities,
reply_markup=reply_markup,
- schedule_date=schedule_date
+ link_preview_options=link_preview_options,
+ schedule_date=schedule_date,
+ business_connection_id=business_connection_id
)
diff --git a/pyrogram/methods/messages/edit_message_media.py b/pyrogram/methods/messages/edit_message_media.py
index bad30ffabb..3a9086fcb4 100644
--- a/pyrogram/methods/messages/edit_message_media.py
+++ b/pyrogram/methods/messages/edit_message_media.py
@@ -26,6 +26,8 @@
from pyrogram import raw, types, utils
from pyrogram.file_id import FileType
+from .inline_session import get_session
+
class EditMessageMedia:
async def edit_message_media(
@@ -35,7 +37,8 @@ async def edit_message_media(
media: "types.InputMedia",
reply_markup: "types.InlineKeyboardMarkup" = None,
file_name: str = None,
- schedule_date: datetime = None
+ schedule_date: datetime = None,
+ business_connection_id: str = None
) -> "types.Message":
"""Edit animation, audio, document, photo or video messages.
@@ -66,6 +69,9 @@ async def edit_message_media(
schedule_date (:py:obj:`~datetime.datetime`, *optional*):
Date when the message will be automatically sent.
+ business_connection_id (``str``, *optional*):
+ Unique identifier of the business connection on behalf of which the message to be edited was sent
+
Returns:
:obj:`~pyrogram.types.Message`: On success, the edited message is returned.
@@ -279,17 +285,36 @@ async def edit_message_media(
else:
media = utils.get_input_media_from_file_id(media.media, FileType.DOCUMENT)
- r = await self.invoke(
- raw.functions.messages.EditMessage(
- peer=await self.resolve_peer(chat_id),
- id=message_id,
- media=media,
- reply_markup=await reply_markup.write(self) if reply_markup else None,
- message=message,
- entities=entities,
- schedule_date=utils.datetime_to_timestamp(schedule_date)
- )
+ rpc = raw.functions.messages.EditMessage(
+ peer=await self.resolve_peer(chat_id),
+ id=message_id,
+ media=media,
+ reply_markup=await reply_markup.write(self) if reply_markup else None,
+ message=message,
+ entities=entities,
+ # TODO
+ schedule_date=utils.datetime_to_timestamp(schedule_date)
)
+ session = None
+ business_connection = None
+ if business_connection_id:
+ business_connection = self.business_user_connection_cache[business_connection_id]
+ if not business_connection:
+ business_connection = await self.get_business_connection(business_connection_id)
+ session = await get_session(
+ self,
+ business_connection._raw.connection.dc_id
+ )
+ if business_connection_id:
+ r = await session.invoke(
+ raw.functions.InvokeWithBusinessConnection(
+ query=rpc,
+ connection_id=business_connection_id
+ )
+ )
+ # await session.stop()
+ else:
+ r = await self.invoke(rpc)
for i in r.updates:
if isinstance(
@@ -305,5 +330,20 @@ async def edit_message_media(
{i.id: i for i in r.users},
{i.id: i for i in r.chats},
is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage),
+ replies=self.fetch_replies
+ )
+ elif isinstance(
+ i,
+ (
+ raw.types.UpdateBotEditBusinessMessage
+ )
+ ):
+ return await types.Message._parse(
+ self,
+ i.message,
+ {i.id: i for i in r.users},
+ {i.id: i for i in r.chats},
+ business_connection_id=getattr(i, "connection_id", business_connection_id),
+ raw_reply_to_message=i.reply_to_message,
replies=0
)
diff --git a/pyrogram/methods/messages/edit_message_reply_markup.py b/pyrogram/methods/messages/edit_message_reply_markup.py
index 1cd75c1a8b..cb4a5e53b9 100644
--- a/pyrogram/methods/messages/edit_message_reply_markup.py
+++ b/pyrogram/methods/messages/edit_message_reply_markup.py
@@ -19,8 +19,9 @@
from typing import Union
import pyrogram
-from pyrogram import raw
-from pyrogram import types
+from pyrogram import raw, types
+
+from .inline_session import get_session
class EditMessageReplyMarkup:
@@ -29,6 +30,7 @@ async def edit_message_reply_markup(
chat_id: Union[int, str],
message_id: int,
reply_markup: "types.InlineKeyboardMarkup" = None,
+ business_connection_id: str = None
) -> "types.Message":
"""Edit only the reply markup of messages sent by the bot.
@@ -46,6 +48,9 @@ async def edit_message_reply_markup(
reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup`, *optional*):
An InlineKeyboardMarkup object.
+ business_connection_id (``str``, *optional*):
+ Unique identifier of the business connection on behalf of which the message to be edited was sent
+
Returns:
:obj:`~pyrogram.types.Message`: On success, the edited message is returned.
@@ -60,18 +65,58 @@ async def edit_message_reply_markup(
InlineKeyboardMarkup([[
InlineKeyboardButton("New button", callback_data="new_data")]]))
"""
- r = await self.invoke(
- raw.functions.messages.EditMessage(
- peer=await self.resolve_peer(chat_id),
- id=message_id,
- reply_markup=await reply_markup.write(self) if reply_markup else None,
- )
+ rpc = raw.functions.messages.EditMessage(
+ peer=await self.resolve_peer(chat_id),
+ id=message_id,
+ reply_markup=await reply_markup.write(self) if reply_markup else None,
)
+ session = None
+ business_connection = None
+ if business_connection_id:
+ business_connection = self.business_user_connection_cache[business_connection_id]
+ if not business_connection:
+ business_connection = await self.get_business_connection(business_connection_id)
+ session = await get_session(
+ self,
+ business_connection._raw.connection.dc_id
+ )
+ if business_connection_id:
+ r = await session.invoke(
+ raw.functions.InvokeWithBusinessConnection(
+ query=rpc,
+ connection_id=business_connection_id
+ )
+ )
+ # await session.stop()
+ else:
+ r = await self.invoke(rpc)
for i in r.updates:
- if isinstance(i, (raw.types.UpdateEditMessage, raw.types.UpdateEditChannelMessage)):
+ if isinstance(
+ i,
+ (
+ raw.types.UpdateEditMessage,
+ raw.types.UpdateEditChannelMessage
+ )
+ ):
return await types.Message._parse(
self, i.message,
{i.id: i for i in r.users},
- {i.id: i for i in r.chats}
+ {i.id: i for i in r.chats},
+ replies=self.fetch_replies
+ )
+ elif isinstance(
+ i,
+ (
+ raw.types.UpdateBotEditBusinessMessage
+ )
+ ):
+ return await types.Message._parse(
+ self,
+ i.message,
+ {i.id: i for i in r.users},
+ {i.id: i for i in r.chats},
+ business_connection_id=getattr(i, "connection_id", business_connection_id),
+ raw_reply_to_message=i.reply_to_message,
+ replies=0
)
diff --git a/pyrogram/methods/messages/edit_message_text.py b/pyrogram/methods/messages/edit_message_text.py
index 375e846aac..62ad47f507 100644
--- a/pyrogram/methods/messages/edit_message_text.py
+++ b/pyrogram/methods/messages/edit_message_text.py
@@ -23,6 +23,8 @@
import pyrogram
from pyrogram import raw, enums, types, utils
+from .inline_session import get_session
+
log = logging.getLogger(__name__)
@@ -37,6 +39,7 @@ async def edit_message_text(
link_preview_options: "types.LinkPreviewOptions" = None,
reply_markup: "types.InlineKeyboardMarkup" = None,
schedule_date: datetime = None,
+ business_connection_id: str = None,
disable_web_page_preview: bool = None
) -> "types.Message":
"""Edit the text of messages.
@@ -63,7 +66,7 @@ async def edit_message_text(
List of special entities that appear in message text, which can be specified instead of *parse_mode*.
link_preview_options (:obj:`~pyrogram.types.LinkPreviewOptions`, *optional*):
- Link preview generation options for the message
+ Link preview generation options for the message. Ignored if the specified URL does not have a valid preview.
reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup`, *optional*):
An InlineKeyboardMarkup object.
@@ -71,6 +74,9 @@ async def edit_message_text(
schedule_date (:py:obj:`~datetime.datetime`, *optional*):
Date when the message will be automatically sent.
+ business_connection_id (``str``, *optional*):
+ Unique identifier of the business connection on behalf of which the message to be edited was sent
+
Returns:
:obj:`~pyrogram.types.Message`: On success, the edited message is returned.
@@ -101,17 +107,50 @@ async def edit_message_text(
)
link_preview_options = types.LinkPreviewOptions(is_disabled=disable_web_page_preview)
- r = await self.invoke(
- raw.functions.messages.EditMessage(
- peer=await self.resolve_peer(chat_id),
- id=message_id,
- no_webpage=link_preview_options.is_disabled if link_preview_options else None,
- invert_media=link_preview_options.show_above_text if link_preview_options else None,
- reply_markup=await reply_markup.write(self) if reply_markup else None,
- schedule_date=utils.datetime_to_timestamp(schedule_date),
- **await utils.parse_text_entities(self, text, parse_mode, entities)
+ link_preview_options = link_preview_options or self.link_preview_options
+
+ media = None
+ if (
+ link_preview_options and
+ link_preview_options.url
+ ):
+ media = raw.types.InputMediaWebPage(
+ url=link_preview_options.url,
+ force_large_media=link_preview_options.prefer_large_media,
+ force_small_media=link_preview_options.prefer_small_media,
+ optional=True
)
+
+ rpc = raw.functions.messages.EditMessage(
+ peer=await self.resolve_peer(chat_id),
+ id=message_id,
+ no_webpage=link_preview_options.is_disabled if link_preview_options else None,
+ invert_media=link_preview_options.show_above_text if link_preview_options else None,
+ media=media,
+ reply_markup=await reply_markup.write(self) if reply_markup else None,
+ schedule_date=utils.datetime_to_timestamp(schedule_date),
+ **await utils.parse_text_entities(self, text, parse_mode, entities)
)
+ session = None
+ business_connection = None
+ if business_connection_id:
+ business_connection = self.business_user_connection_cache[business_connection_id]
+ if not business_connection:
+ business_connection = await self.get_business_connection(business_connection_id)
+ session = await get_session(
+ self,
+ business_connection._raw.connection.dc_id
+ )
+ if business_connection_id:
+ r = await session.invoke(
+ raw.functions.InvokeWithBusinessConnection(
+ query=rpc,
+ connection_id=business_connection_id
+ )
+ )
+ # await session.stop()
+ else:
+ r = await self.invoke(rpc)
for i in r.updates:
if isinstance(
@@ -127,5 +166,20 @@ async def edit_message_text(
{i.id: i for i in r.users},
{i.id: i for i in r.chats},
is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage),
+ replies=self.fetch_replies
+ )
+ elif isinstance(
+ i,
+ (
+ raw.types.UpdateBotEditBusinessMessage
+ )
+ ):
+ return await types.Message._parse(
+ self,
+ i.message,
+ {i.id: i for i in r.users},
+ {i.id: i for i in r.chats},
+ business_connection_id=getattr(i, "connection_id", business_connection_id),
+ raw_reply_to_message=i.reply_to_message,
replies=0
)
diff --git a/pyrogram/methods/messages/forward_messages.py b/pyrogram/methods/messages/forward_messages.py
index 3771bf43ec..024d61e5ed 100644
--- a/pyrogram/methods/messages/forward_messages.py
+++ b/pyrogram/methods/messages/forward_messages.py
@@ -63,7 +63,7 @@ async def forward_messages(
Users will receive a notification with no sound.
protect_content (``bool``, *optional*):
- Protects the contents of the sent message from forwarding and saving.
+ Pass True if the content of the message must be protected from forwarding and saving; for bots only.
drop_author (``bool``, *optional*):
Whether to forward messages without quoting the original author.
@@ -128,7 +128,8 @@ async def forward_messages(
i.message,
users,
chats,
- is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage)
+ is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage),
+ replies=self.fetch_replies
)
)
diff --git a/pyrogram/methods/messages/get_chat_history.py b/pyrogram/methods/messages/get_chat_history.py
index 25a759b993..6fb8ca0e51 100644
--- a/pyrogram/methods/messages/get_chat_history.py
+++ b/pyrogram/methods/messages/get_chat_history.py
@@ -33,6 +33,7 @@ async def get_chunk(
max_id: int = 0,
from_message_id: int = 0,
from_date: datetime = utils.zero_datetime(),
+ reverse: bool = False,
is_scheduled: bool = False
):
if is_scheduled:
@@ -43,19 +44,23 @@ async def get_chunk(
),
sleep_threshold=60
)
- return await utils.parse_messages(
+ messages = await utils.parse_messages(
client,
r,
is_scheduled=True,
replies=0
)
+ if reverse:
+ messages.reverse()
+ return messages
else:
+ from_message_id = from_message_id or (1 if reverse else 0)
messages = await client.invoke(
raw.functions.messages.GetHistory(
peer=await client.resolve_peer(chat_id),
offset_id=from_message_id,
offset_date=utils.datetime_to_timestamp(from_date),
- add_offset=offset,
+ add_offset=offset * (-1 if reverse else 1) - (limit if reverse else 0),
limit=limit,
max_id=max_id,
min_id=min_id,
@@ -63,13 +68,15 @@ async def get_chunk(
),
sleep_threshold=60
)
-
- return await utils.parse_messages(
+ messages =await utils.parse_messages(
client,
messages,
is_scheduled=False,
replies=0
)
+ if reverse:
+ messages.reverse()
+ return messages
class GetChatHistory:
@@ -82,6 +89,7 @@ async def get_chat_history(
min_id: int = 0,
max_id: int = 0,
offset_date: datetime = utils.zero_datetime(),
+ reverse: bool = False,
is_scheduled: bool = False
) -> Optional[AsyncGenerator["types.Message", None]]:
"""Get messages from a chat history.
@@ -118,8 +126,12 @@ async def get_chat_history(
offset_date (:py:obj:`~datetime.datetime`, *optional*):
Pass a date as offset to retrieve only older messages starting from that date.
+ reverse (``bool``, *optional*):
+ Pass True to retrieve the messages in reversed order (from older to most recent). Defaults to False.
+
is_scheduled (``bool``, *optional*):
Whether to get scheduled messages. Defaults to False.
+
Returns:
``Generator``: A generator yielding :obj:`~pyrogram.types.Message` objects.
@@ -143,13 +155,14 @@ async def get_chat_history(
min_id=min_id,
max_id=max_id,
from_date=offset_date,
+ reverse=reverse,
is_scheduled=is_scheduled
)
if not messages:
return
- offset_id = messages[-1].id
+ offset_id = messages[-1].id + (1 if reverse else 0)
for message in messages:
yield message
diff --git a/pyrogram/methods/messages/get_chat_sponsored_messages.py b/pyrogram/methods/messages/get_chat_sponsored_messages.py
new file mode 100644
index 0000000000..08eea342d7
--- /dev/null
+++ b/pyrogram/methods/messages/get_chat_sponsored_messages.py
@@ -0,0 +1,61 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+from typing import Optional, Union, List
+
+import pyrogram
+from pyrogram import raw, types
+
+
+class GetChatSponsoredMessages:
+ async def get_chat_sponsored_messages(
+ self: "pyrogram.Client",
+ chat_id: Union[int, str],
+ ) -> Optional[List["types.SponsoredMessage"]]:
+ """Returns sponsored messages to be shown in a chat; for channel chats only.
+
+ .. include:: /_includes/usable-by/users.rst
+
+ Parameters:
+ chat_id (``int`` | ``str``):
+ Unique identifier (int) or username (str) of the target chat.
+
+ Returns:
+ List of :obj:`~pyrogram.types.SponsoredMessage`: a list of sponsored messages is returned.
+
+ Example:
+ .. code-block:: python
+
+ # Get a sponsored messages
+ sm = await app.get_chat_sponsored_messages(chat_id)
+ print(sm)
+
+ """
+ r = await self.invoke(
+ raw.functions.channels.GetSponsoredMessages(
+ channel=await self.resolve_peer(chat_id)
+ )
+ )
+
+ if isinstance(r, raw.types.messages.SponsoredMessagesEmpty):
+ return None
+
+ return types.List([
+ types.SponsoredMessage._parse(self, sm)
+ for sm in r.messages
+ ])
diff --git a/pyrogram/methods/messages/get_custom_emoji_stickers.py b/pyrogram/methods/messages/get_custom_emoji_stickers.py
index 7a71f0587c..c8d7f4354b 100644
--- a/pyrogram/methods/messages/get_custom_emoji_stickers.py
+++ b/pyrogram/methods/messages/get_custom_emoji_stickers.py
@@ -19,8 +19,7 @@
from typing import List
import pyrogram
-from pyrogram import raw
-from pyrogram import types
+from pyrogram import raw, types
class GetCustomEmojiStickers:
@@ -52,4 +51,4 @@ async def get_custom_emoji_stickers(
sticker = await types.Sticker._parse(self, item, attributes)
stickers.append(sticker)
- return pyrogram.types.List(stickers)
+ return types.List(stickers)
diff --git a/pyrogram/methods/messages/get_discussion_message.py b/pyrogram/methods/messages/get_discussion_message.py
index 58a6b7044d..7d641a8d30 100644
--- a/pyrogram/methods/messages/get_discussion_message.py
+++ b/pyrogram/methods/messages/get_discussion_message.py
@@ -62,4 +62,10 @@ async def get_discussion_message(
users = {u.id: u for u in r.users}
chats = {c.id: c for c in r.chats}
- return await types.Message._parse(self, r.messages[0], users, chats)
+ return await types.Message._parse(
+ self,
+ r.messages[0],
+ users,
+ chats,
+ replies=self.fetch_replies
+ )
diff --git a/pyrogram/methods/messages/get_discussion_replies.py b/pyrogram/methods/messages/get_discussion_replies.py
index dd23751bb1..7ea8479775 100644
--- a/pyrogram/methods/messages/get_discussion_replies.py
+++ b/pyrogram/methods/messages/get_discussion_replies.py
@@ -78,7 +78,13 @@ async def get_discussion_replies(
return
for message in messages:
- yield await types.Message._parse(self, message, users, chats, replies=0)
+ yield await types.Message._parse(
+ self,
+ message,
+ users,
+ chats,
+ replies=self.fetch_replies
+ )
current += 1
diff --git a/pyrogram/methods/messages/search_global.py b/pyrogram/methods/messages/search_global.py
index e57c3c741d..c171c7ced0 100644
--- a/pyrogram/methods/messages/search_global.py
+++ b/pyrogram/methods/messages/search_global.py
@@ -31,7 +31,7 @@ async def search_global(
filter: "enums.MessagesFilter" = enums.MessagesFilter.EMPTY,
limit: int = 0,
chat_list: int = 0,
- is_channel: bool = False,
+ only_in_channels: bool = False,
) -> Optional[AsyncGenerator["types.Message", None]]:
"""Search messages globally from all of your chats.
@@ -60,7 +60,7 @@ async def search_global(
chat_list (``int``, *optional*):
Chat list in which to search messages; Only Main (0) and Archive (1) chat lists are supported. Defaults to (0) Main chat list.
- is_channel (``bool``, *optional*):
+ only_in_channels (``bool``, *optional*):
True, if should search only in joined channels.
Defaults to False. All available chats are searched.
@@ -103,7 +103,7 @@ async def search_global(
offset_id=offset_id,
limit=limit,
folder_id=chat_list,
- broadcasts_only=is_channel
+ broadcasts_only=only_in_channels
),
sleep_threshold=60
),
diff --git a/pyrogram/methods/messages/search_global_count.py b/pyrogram/methods/messages/search_global_count.py
index ee4a0fa561..c0683c8648 100644
--- a/pyrogram/methods/messages/search_global_count.py
+++ b/pyrogram/methods/messages/search_global_count.py
@@ -26,7 +26,7 @@ async def search_global_count(
query: str = "",
filter: "enums.MessagesFilter" = enums.MessagesFilter.EMPTY,
chat_list: int = 0,
- is_channel: bool = False,
+ only_in_channels: bool = False,
) -> int:
"""Get the count of messages resulting from a global search.
@@ -45,7 +45,7 @@ async def search_global_count(
chat_list (``int``, *optional*):
Chat list in which to search messages; Only Main (0) and Archive (1) chat lists are supported. Defaults to (0) Main chat list.
- is_channel (``bool``, *optional*):
+ only_in_channels (``bool``, *optional*):
True, if should search only in joined channels.
Defaults to False. All available chats are searched.
@@ -63,7 +63,7 @@ async def search_global_count(
offset_id=0,
limit=1,
folder_id=chat_list,
- broadcasts_only=is_channel
+ broadcasts_only=only_in_channels
)
)
diff --git a/pyrogram/methods/messages/search_messages.py b/pyrogram/methods/messages/search_messages.py
index 99da7927d7..c4da6cf790 100644
--- a/pyrogram/methods/messages/search_messages.py
+++ b/pyrogram/methods/messages/search_messages.py
@@ -16,6 +16,7 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see .
+from datetime import datetime
from typing import Union, List, AsyncGenerator, Optional
import pyrogram
@@ -27,31 +28,39 @@ async def get_chunk(
client,
chat_id: Union[int, str],
query: str = "",
- filter: "enums.MessagesFilter" = enums.MessagesFilter.EMPTY,
offset: int = 0,
+ filter: "enums.MessagesFilter" = enums.MessagesFilter.EMPTY,
limit: int = 100,
from_user: Union[int, str] = None,
- message_thread_id: int = None
+ message_thread_id: int = None,
+ offset_id: int = 0,
+ min_date: datetime = utils.zero_datetime(),
+ max_date: datetime = utils.zero_datetime(),
+ min_id: int = 0,
+ max_id: int = 0,
+ saved_messages_topic_id: Optional[Union[int, str]] = None
) -> List["types.Message"]:
r = await client.invoke(
raw.functions.messages.Search(
peer=await client.resolve_peer(chat_id),
q=query,
filter=filter.value(),
- min_date=0,
- max_date=0,
- offset_id=0,
+ min_date=utils.datetime_to_timestamp(min_date),
+ max_date= utils.datetime_to_timestamp(max_date),
+ offset_id=offset_id,
add_offset=offset,
limit=limit,
- min_id=0,
- max_id=0,
+ min_id=min_id,
+ max_id=max_id,
from_id=(
await client.resolve_peer(from_user)
if from_user
else None
),
hash=0,
- top_msg_id=message_thread_id
+ top_msg_id=message_thread_id,
+ saved_peer_id=await client.resolve_peer(saved_messages_topic_id) if saved_messages_topic_id else None
+ # saved_reaction:flags.3?Vector
),
sleep_threshold=60
)
@@ -65,11 +74,17 @@ async def search_messages(
self: "pyrogram.Client",
chat_id: Union[int, str],
query: str = "",
- offset: int = 0,
filter: "enums.MessagesFilter" = enums.MessagesFilter.EMPTY,
- limit: int = 0,
from_user: Union[int, str] = None,
- message_thread_id: int = None
+ message_thread_id: int = None,
+ offset: int = 0,
+ limit: int = 0,
+ offset_id: int = 0,
+ min_date: datetime = utils.zero_datetime(),
+ max_date: datetime = utils.zero_datetime(),
+ min_id: int = 0,
+ max_id: int = 0,
+ saved_messages_topic_id: Optional[Union[int, str]] = None
) -> Optional[AsyncGenerator["types.Message", None]]:
"""Search for text and media messages inside a specific chat.
@@ -89,24 +104,42 @@ async def search_messages(
When passed while searching for media messages, the query will be applied to captions.
Defaults to "" (empty string).
- offset (``int``, *optional*):
- Sequential number of the first message to be returned.
- Defaults to 0.
-
filter (:obj:`~pyrogram.enums.MessagesFilter`, *optional*):
Pass a filter in order to search for specific kind of messages only.
Defaults to any message (no filter).
- limit (``int``, *optional*):
- Limits the number of messages to be retrieved.
- By default, no limit is applied and all messages are returned.
-
from_user (``int`` | ``str``, *optional*):
Unique identifier (int) or username (str) of the target user you want to search for messages from.
message_thread_id (``int``, *optional*):
Unique identifier for the target message thread (topic) of the forum; for forum supergroups only
+ offset (``int``, *optional*):
+ Sequential number of the first message to be returned.
+ Defaults to 0.
+
+ limit (``int``, *optional*):
+ Limits the number of messages to be retrieved.
+ By default, no limit is applied and all messages are returned.
+
+ offset_id (``int``, *optional*):
+ Identifier of the first message to be returned.
+
+ min_date (:py:obj:`~datetime.datetime`, *optional*):
+ Pass a date as offset to retrieve only older messages starting from that date.
+
+ max_date (:py:obj:`~datetime.datetime`, *optional*):
+ Pass a date as offset to retrieve only newer messages starting from that date.
+
+ min_id (``int``, *optional*):
+ If a positive value was provided, the method will return only messages with IDs more than min_id.
+
+ max_id (``int``, *optional*):
+ If a positive value was provided, the method will return only messages with IDs less than max_id.
+
+ saved_messages_topic_id (``int`` | ``str``, *optional*):
+ If not None, only messages in the specified Saved Messages topic will be returned; pass None to return all messages, or for chats other than Saved Messages.
+
Returns:
``Generator``: A generator yielding :obj:`~pyrogram.types.Message` objects.
@@ -137,11 +170,17 @@ async def search_messages(
client=self,
chat_id=chat_id,
query=query,
- filter=filter,
offset=offset,
+ filter=filter,
limit=limit,
from_user=from_user,
- message_thread_id=message_thread_id
+ message_thread_id=message_thread_id,
+ offset_id=offset_id,
+ min_date=min_date,
+ max_date=max_date,
+ min_id=min_id,
+ max_id=max_id,
+ saved_messages_topic_id=saved_messages_topic_id
)
if not messages:
diff --git a/pyrogram/methods/messages/search_messages_count.py b/pyrogram/methods/messages/search_messages_count.py
index 9cea46cb2a..e4a19113eb 100644
--- a/pyrogram/methods/messages/search_messages_count.py
+++ b/pyrogram/methods/messages/search_messages_count.py
@@ -16,10 +16,11 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see .
-from typing import Union
+from datetime import datetime
+from typing import Optional, Union
import pyrogram
-from pyrogram import raw, enums
+from pyrogram import enums, raw, utils
class SearchMessagesCount:
@@ -29,7 +30,12 @@ async def search_messages_count(
query: str = "",
filter: "enums.MessagesFilter" = enums.MessagesFilter.EMPTY,
from_user: Union[int, str] = None,
- message_thread_id: int = None
+ message_thread_id: int = None,
+ min_date: datetime = utils.zero_datetime(),
+ max_date: datetime = utils.zero_datetime(),
+ min_id: int = 0,
+ max_id: int = 0,
+ saved_messages_topic_id: Optional[Union[int, str]] = None
) -> int:
"""Get the count of messages resulting from a search inside a chat.
@@ -58,28 +64,46 @@ async def search_messages_count(
message_thread_id (``int``, *optional*):
Unique identifier for the target message thread (topic) of the forum; for forum supergroups only
+ min_date (:py:obj:`~datetime.datetime`, *optional*):
+ Pass a date as offset to retrieve only older messages starting from that date.
+
+ max_date (:py:obj:`~datetime.datetime`, *optional*):
+ Pass a date as offset to retrieve only newer messages starting from that date.
+
+ min_id (``int``, *optional*):
+ If a positive value was provided, the method will return only messages with IDs more than min_id.
+
+ max_id (``int``, *optional*):
+ If a positive value was provided, the method will return only messages with IDs less than max_id.
+
+ saved_messages_topic_id (``int`` | ``str``, *optional*):
+ If not None, only messages in the specified Saved Messages topic will be returned; pass None to return all messages, or for chats other than Saved Messages.
+
Returns:
``int``: On success, the messages count is returned.
+
"""
r = await self.invoke(
raw.functions.messages.Search(
peer=await self.resolve_peer(chat_id),
q=query,
filter=filter.value(),
- min_date=0,
- max_date=0,
+ min_date=utils.datetime_to_timestamp(min_date),
+ max_date= utils.datetime_to_timestamp(max_date),
offset_id=0,
add_offset=0,
limit=1,
- min_id=0,
- max_id=0,
+ min_id=min_id,
+ max_id=max_id,
from_id=(
await self.resolve_peer(from_user)
if from_user
else None
),
hash=0,
- top_msg_id=message_thread_id
+ top_msg_id=message_thread_id,
+ saved_peer_id=await self.resolve_peer(saved_messages_topic_id) if saved_messages_topic_id else None
+ # saved_reaction:flags.3?Vector
)
)
diff --git a/pyrogram/methods/messages/search_public_messages_by_tag.py b/pyrogram/methods/messages/search_public_messages_by_tag.py
new file mode 100644
index 0000000000..b42e8fdf23
--- /dev/null
+++ b/pyrogram/methods/messages/search_public_messages_by_tag.py
@@ -0,0 +1,102 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+from datetime import datetime
+from typing import AsyncGenerator
+
+import pyrogram
+from pyrogram import raw, types, utils
+
+
+class SearchPublicMessagesByTag:
+ async def search_public_messages_by_tag(
+ self: "pyrogram.Client",
+ tag: str = "",
+ offset_id: int = 0,
+ offset_date: datetime = utils.zero_datetime(),
+ limit: int = 0,
+ ) -> AsyncGenerator["types.Message", None]:
+ """Searches for public channel posts containing the given hashtag or cashtag. For optimal performance, the number of returned messages is chosen by Telegram Server and can be smaller than the specified limit.
+
+ If you want to get the posts count only, see :meth:`~pyrogram.Client.count_public_messages_by_tag`.
+
+ .. include:: /_includes/usable-by/users.rst
+
+ Parameters:
+ tag (``str``, *optional*):
+ Hashtag or cashtag to search for.
+
+ offset_id (``int``, *optional*):
+ Offset of the first entry to return as received from the previous request; use empty string to get the first chunk of results.
+
+ offset_date (:py:obj:`~datetime.datetime`, *optional*):
+ Pass a date as offset to retrieve only older messages starting from that date.
+
+ limit (``int``, *optional*):
+ The maximum number of messages to be returned.
+ By default, no limit is applied and all posts are returned.
+
+ Returns:
+ ``Generator``: A generator yielding :obj:`~pyrogram.types.Message` objects.
+
+ Example:
+ .. code-block:: python
+
+ # Search for "#pyrogram". Get the first 50 results
+ async for message in app.search_public_messages_by_tag("pyrogram", limit=50):
+ print(message.text)
+
+ """
+ current = 0
+ total = abs(limit) or (1 << 31)
+ limit = min(100, total)
+
+ offset_peer = raw.types.InputPeerEmpty()
+
+ while True:
+ messages = await utils.parse_messages(
+ self,
+ await self.invoke(
+ raw.functions.channels.SearchPosts(
+ hashtag=tag,
+ offset_rate=utils.datetime_to_timestamp(offset_date),
+ offset_peer=offset_peer,
+ offset_id=offset_id,
+ limit=limit
+ ),
+ sleep_threshold=60
+ ),
+ replies=0
+ )
+
+ if not messages:
+ return
+
+ last = messages[-1]
+
+ offset_date = utils.datetime_to_timestamp(last.date)
+ offset_peer = await self.resolve_peer(last.chat.id)
+ offset_id = last.id
+
+ for message in messages:
+ yield message
+
+ current += 1
+
+ if current >= total:
+ return
diff --git a/pyrogram/methods/messages/send_animation.py b/pyrogram/methods/messages/send_animation.py
index d6580d93e3..a020d79293 100644
--- a/pyrogram/methods/messages/send_animation.py
+++ b/pyrogram/methods/messages/send_animation.py
@@ -39,6 +39,7 @@ async def send_animation(
caption: str = "",
parse_mode: Optional["enums.ParseMode"] = None,
caption_entities: List["types.MessageEntity"] = None,
+ show_caption_above_media: bool = None,
unsave: bool = False,
has_spoiler: bool = None,
duration: int = 0,
@@ -50,6 +51,7 @@ async def send_animation(
reply_parameters: "types.ReplyParameters" = None,
message_thread_id: int = None,
business_connection_id: str = None,
+ message_effect_id: int = None,
schedule_date: datetime = None,
protect_content: bool = None,
ttl_seconds: int = None,
@@ -90,6 +92,9 @@ async def send_animation(
caption_entities (List of :obj:`~pyrogram.types.MessageEntity`):
List of special entities that appear in the caption, which can be specified instead of *parse_mode*.
+ show_caption_above_media (``bool``, *optional*):
+ Pass True, if the caption must be shown above the message media.
+
unsave (``bool``, *optional*):
By default, the server will save into your own collection any new animation you send.
Pass True to automatically unsave the sent animation. Defaults to False.
@@ -129,11 +134,14 @@ async def send_animation(
business_connection_id (``str``, *optional*):
Unique identifier of the business connection on behalf of which the message will be sent.
+ message_effect_id (``int`` ``64-bit``, *optional*):
+ Unique identifier of the message effect to be added to the message; for private chats only.
+
schedule_date (:py:obj:`~datetime.datetime`, *optional*):
Date when the message will be automatically sent.
protect_content (``bool``, *optional*):
- Protects the contents of the sent message from forwarding and saving.
+ Pass True if the content of the message must be protected from forwarding and saving; for bots only.
ttl_seconds (``int``, *optional*):
Self-Destruct Timer.
@@ -278,6 +286,8 @@ async def progress(current, total):
schedule_date=utils.datetime_to_timestamp(schedule_date),
noforwards=protect_content,
reply_markup=await reply_markup.write(self) if reply_markup else None,
+ effect=message_effect_id,
+ invert_media=show_caption_above_media,
**await utils.parse_text_entities(self, caption, parse_mode, caption_entities)
)
session = None
@@ -320,7 +330,8 @@ async def progress(current, total):
i.message,
{i.id: i for i in r.users},
{i.id: i for i in r.chats},
- is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage)
+ is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage),
+ replies=self.fetch_replies
)
if unsave:
@@ -349,7 +360,8 @@ async def progress(current, total):
{i.id: i for i in r.users},
{i.id: i for i in r.chats},
business_connection_id=getattr(i, "connection_id", business_connection_id),
- raw_reply_to_message=i.reply_to_message
+ raw_reply_to_message=i.reply_to_message,
+ replies=0
)
diff --git a/pyrogram/methods/messages/send_audio.py b/pyrogram/methods/messages/send_audio.py
index 6fc369e5df..f427f01f2d 100644
--- a/pyrogram/methods/messages/send_audio.py
+++ b/pyrogram/methods/messages/send_audio.py
@@ -16,20 +16,20 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see .
+import logging
import os
import re
from datetime import datetime
from typing import Union, BinaryIO, List, Optional, Callable
import pyrogram
-from pyrogram import StopTransmission, enums
-from pyrogram import raw
-from pyrogram import types
-from pyrogram import utils
+from pyrogram import StopTransmission, enums, raw, types, utils
from pyrogram.errors import FilePartMissing
from pyrogram.file_id import FileType
from .inline_session import get_session
+log = logging.getLogger(__name__)
+
class SendAudio:
async def send_audio(
@@ -48,6 +48,7 @@ async def send_audio(
reply_parameters: "types.ReplyParameters" = None,
message_thread_id: int = None,
business_connection_id: str = None,
+ message_effect_id: int = None,
schedule_date: datetime = None,
protect_content: bool = None,
reply_markup: Union[
@@ -121,11 +122,14 @@ async def send_audio(
business_connection_id (``str``, *optional*):
Unique identifier of the business connection on behalf of which the message will be sent.
+ message_effect_id (``int`` ``64-bit``, *optional*):
+ Unique identifier of the message effect to be added to the message; for private chats only.
+
schedule_date (:py:obj:`~datetime.datetime`, *optional*):
Date when the message will be automatically sent.
protect_content (``bool``, *optional*):
- Protects the contents of the sent message from forwarding and saving.
+ Pass True if the content of the message must be protected from forwarding and saving; for bots only.
reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*):
Additional interface options. An object for an inline keyboard, custom reply keyboard,
@@ -250,6 +254,7 @@ async def progress(current, total):
schedule_date=utils.datetime_to_timestamp(schedule_date),
noforwards=protect_content,
reply_markup=await reply_markup.write(self) if reply_markup else None,
+ effect=message_effect_id,
**await utils.parse_text_entities(self, caption, parse_mode, caption_entities)
)
session = None
@@ -291,7 +296,8 @@ async def progress(current, total):
self, i.message,
{i.id: i for i in r.users},
{i.id: i for i in r.chats},
- is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage)
+ is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage),
+ replies=self.fetch_replies
)
elif isinstance(
i,
@@ -305,7 +311,8 @@ async def progress(current, total):
{i.id: i for i in r.users},
{i.id: i for i in r.chats},
business_connection_id=getattr(i, "connection_id", business_connection_id),
- raw_reply_to_message=i.reply_to_message
+ raw_reply_to_message=i.reply_to_message,
+ replies=0
)
except StopTransmission:
return None
diff --git a/pyrogram/methods/messages/send_cached_media.py b/pyrogram/methods/messages/send_cached_media.py
index 56abf672d4..25ab90ed4e 100644
--- a/pyrogram/methods/messages/send_cached_media.py
+++ b/pyrogram/methods/messages/send_cached_media.py
@@ -34,7 +34,9 @@ async def send_cached_media(
caption: str = "",
parse_mode: Optional["enums.ParseMode"] = None,
caption_entities: List["types.MessageEntity"] = None,
+ show_caption_above_media: bool = None,
disable_notification: bool = None,
+ message_effect_id: int = None,
reply_parameters: "types.ReplyParameters" = None,
message_thread_id: int = None,
business_connection_id: str = None,
@@ -77,10 +79,16 @@ async def send_cached_media(
caption_entities (List of :obj:`~pyrogram.types.MessageEntity`):
List of special entities that appear in the caption, which can be specified instead of *parse_mode*.
+ show_caption_above_media (``bool``, *optional*):
+ Pass True, if the caption must be shown above the message media. Supported only for animation, photo and video messages.
+
disable_notification (``bool``, *optional*):
Sends the message silently.
Users will receive a notification with no sound.
+ message_effect_id (``int`` ``64-bit``, *optional*):
+ Unique identifier of the message effect to be added to the message; for private chats only.
+
reply_parameters (:obj:`~pyrogram.types.ReplyParameters`, *optional*):
Description of the message to reply to
@@ -94,7 +102,7 @@ async def send_cached_media(
Date when the message will be automatically sent.
protect_content (``bool``, *optional*):
- Protects the contents of the sent message from forwarding and saving.
+ Pass True if the content of the message must be protected from forwarding and saving; for bots only.
has_spoiler (``bool``, *optional*):
True, if the message media is covered by a spoiler animation.
@@ -139,6 +147,8 @@ async def send_cached_media(
schedule_date=utils.datetime_to_timestamp(schedule_date),
noforwards=protect_content,
reply_markup=await reply_markup.write(self) if reply_markup else None,
+ effect=message_effect_id,
+ invert_media=show_caption_above_media,
**await utils.parse_text_entities(self, caption, parse_mode, caption_entities)
)
@@ -165,7 +175,8 @@ async def send_cached_media(
self, i.message,
{i.id: i for i in r.users},
{i.id: i for i in r.chats},
- is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage)
+ is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage),
+ replies=self.fetch_replies
)
elif isinstance(
i,
@@ -181,5 +192,6 @@ async def send_cached_media(
{i.id: i for i in r.users},
{i.id: i for i in r.chats},
business_connection_id=getattr(i, "connection_id", business_connection_id),
- raw_reply_to_message=i.reply_to_message
+ raw_reply_to_message=i.reply_to_message,
+ replies=0
)
diff --git a/pyrogram/methods/messages/send_contact.py b/pyrogram/methods/messages/send_contact.py
index 511a05a235..44d78889a1 100644
--- a/pyrogram/methods/messages/send_contact.py
+++ b/pyrogram/methods/messages/send_contact.py
@@ -40,6 +40,7 @@ async def send_contact(
business_connection_id: str = None,
schedule_date: datetime = None,
protect_content: bool = None,
+ message_effect_id: int = None,
reply_markup: Union[
"types.InlineKeyboardMarkup",
"types.ReplyKeyboardMarkup",
@@ -87,7 +88,10 @@ async def send_contact(
Date when the message will be automatically sent.
protect_content (``bool``, *optional*):
- Protects the contents of the sent message from forwarding and saving.
+ Pass True if the content of the message must be protected from forwarding and saving; for bots only.
+
+ message_effect_id (``int`` ``64-bit``, *optional*):
+ Unique identifier of the message effect to be added to the message; for private chats only.
reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*):
Additional interface options. An object for an inline keyboard, custom reply keyboard,
@@ -134,7 +138,8 @@ async def send_contact(
random_id=self.rnd_id(),
schedule_date=utils.datetime_to_timestamp(schedule_date),
noforwards=protect_content,
- reply_markup=await reply_markup.write(self) if reply_markup else None
+ reply_markup=await reply_markup.write(self) if reply_markup else None,
+ effect=message_effect_id
)
if business_connection_id:
r = await self.invoke(
@@ -159,7 +164,8 @@ async def send_contact(
self, i.message,
{i.id: i for i in r.users},
{i.id: i for i in r.chats},
- is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage)
+ is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage),
+ replies=self.fetch_replies
)
elif isinstance(
i,
@@ -173,5 +179,6 @@ async def send_contact(
{i.id: i for i in r.users},
{i.id: i for i in r.chats},
business_connection_id=getattr(i, "connection_id", business_connection_id),
- raw_reply_to_message=i.reply_to_message
+ raw_reply_to_message=i.reply_to_message,
+ replies=0
)
diff --git a/pyrogram/methods/messages/send_dice.py b/pyrogram/methods/messages/send_dice.py
index dbb9076cb9..963c1ffeca 100644
--- a/pyrogram/methods/messages/send_dice.py
+++ b/pyrogram/methods/messages/send_dice.py
@@ -37,6 +37,7 @@ async def send_dice(
business_connection_id: str = None,
schedule_date: datetime = None,
protect_content: bool = None,
+ message_effect_id: int = None,
reply_markup: Union[
"types.InlineKeyboardMarkup",
"types.ReplyKeyboardMarkup",
@@ -79,7 +80,10 @@ async def send_dice(
Date when the message will be automatically sent.
protect_content (``bool``, *optional*):
- Protects the contents of the sent message from forwarding and saving.
+ Pass True if the content of the message must be protected from forwarding and saving; for bots only.
+
+ message_effect_id (``int`` ``64-bit``, *optional*):
+ Unique identifier of the message effect to be added to the message; for private chats only.
reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*):
Additional interface options. An object for an inline keyboard, custom reply keyboard,
@@ -128,6 +132,7 @@ async def send_dice(
schedule_date=utils.datetime_to_timestamp(schedule_date),
noforwards=protect_content,
reply_markup=await reply_markup.write(self) if reply_markup else None,
+ effect=message_effect_id,
message=""
)
if business_connection_id:
@@ -153,7 +158,8 @@ async def send_dice(
self, i.message,
{i.id: i for i in r.users},
{i.id: i for i in r.chats},
- is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage)
+ is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage),
+ replies=self.fetch_replies
)
elif isinstance(
i,
@@ -167,5 +173,6 @@ async def send_dice(
{i.id: i for i in r.users},
{i.id: i for i in r.chats},
business_connection_id=getattr(i, "connection_id", business_connection_id),
- raw_reply_to_message=i.reply_to_message
+ raw_reply_to_message=i.reply_to_message,
+ replies=0
)
diff --git a/pyrogram/methods/messages/send_document.py b/pyrogram/methods/messages/send_document.py
index 70a6d375cf..ae768ebf98 100644
--- a/pyrogram/methods/messages/send_document.py
+++ b/pyrogram/methods/messages/send_document.py
@@ -41,11 +41,12 @@ async def send_document(
parse_mode: Optional["enums.ParseMode"] = None,
caption_entities: List["types.MessageEntity"] = None,
file_name: str = None,
- disable_content_type_detection: bool = None,
+ disable_content_type_detection: bool = True,
disable_notification: bool = None,
reply_parameters: "types.ReplyParameters" = None,
message_thread_id: int = None,
business_connection_id: str = None,
+ message_effect_id: int = None,
schedule_date: datetime = None,
protect_content: bool = None,
reply_markup: Union[
@@ -55,6 +56,7 @@ async def send_document(
"types.ForceReply"
] = None,
reply_to_message_id: int = None,
+ force_document: bool = None,
progress: Callable = None,
progress_args: tuple = ()
) -> Optional["types.Message"]:
@@ -114,11 +116,14 @@ async def send_document(
business_connection_id (``str``, *optional*):
Unique identifier of the business connection on behalf of which the message will be sent.
+ message_effect_id (``int`` ``64-bit``, *optional*):
+ Unique identifier of the message effect to be added to the message; for private chats only.
+
schedule_date (:py:obj:`~datetime.datetime`, *optional*):
Date when the message will be automatically sent.
protect_content (``bool``, *optional*):
- Protects the contents of the sent message from forwarding and saving.
+ Pass True if the content of the message must be protected from forwarding and saving; for bots only.
reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*):
Additional interface options. An object for an inline keyboard, custom reply keyboard,
@@ -179,6 +184,19 @@ async def progress(current, total):
)
reply_parameters = types.ReplyParameters(message_id=reply_to_message_id)
+ if force_document and disable_content_type_detection:
+ raise ValueError(
+ "Parameters `force_document` and `disable_content_type_detection` "
+ "are mutually exclusive."
+ )
+
+ if force_document is not None:
+ log.warning(
+ "This property is deprecated. "
+ "Please use disable_content_type_detection instead"
+ )
+ disable_content_type_detection = force_document
+
file = None
try:
@@ -228,6 +246,7 @@ async def progress(current, total):
schedule_date=utils.datetime_to_timestamp(schedule_date),
noforwards=protect_content,
reply_markup=await reply_markup.write(self) if reply_markup else None,
+ effect=message_effect_id,
**await utils.parse_text_entities(self, caption, parse_mode, caption_entities)
)
session = None
@@ -269,7 +288,8 @@ async def progress(current, total):
self, i.message,
{i.id: i for i in r.users},
{i.id: i for i in r.chats},
- is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage)
+ is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage),
+ replies=self.fetch_replies
)
elif isinstance(
i,
@@ -283,7 +303,8 @@ async def progress(current, total):
{i.id: i for i in r.users},
{i.id: i for i in r.chats},
business_connection_id=getattr(i, "connection_id", business_connection_id),
- raw_reply_to_message=i.reply_to_message
+ raw_reply_to_message=i.reply_to_message,
+ replies=0
)
except StopTransmission:
diff --git a/pyrogram/methods/messages/send_location.py b/pyrogram/methods/messages/send_location.py
index 707d5e818f..f03cf62d40 100644
--- a/pyrogram/methods/messages/send_location.py
+++ b/pyrogram/methods/messages/send_location.py
@@ -33,12 +33,15 @@ async def send_location(
chat_id: Union[int, str],
latitude: float,
longitude: float,
+ horizontal_accuracy: float = None,
+ # TODO
disable_notification: bool = None,
reply_parameters: "types.ReplyParameters" = None,
message_thread_id: int = None,
business_connection_id: str = None,
schedule_date: datetime = None,
protect_content: bool = None,
+ message_effect_id: int = None,
reply_markup: Union[
"types.InlineKeyboardMarkup",
"types.ReplyKeyboardMarkup",
@@ -63,6 +66,9 @@ async def send_location(
longitude (``float``):
Longitude of the location.
+ horizontal_accuracy (``float``, *optional*):
+ The radius of uncertainty for the location, measured in meters; 0-1500.
+
disable_notification (``bool``, *optional*):
Sends the message silently.
Users will receive a notification with no sound.
@@ -80,7 +86,10 @@ async def send_location(
Date when the message will be automatically sent.
protect_content (``bool``, *optional*):
- Protects the contents of the sent message from forwarding and saving.
+ Pass True if the content of the message must be protected from forwarding and saving; for bots only.
+
+ message_effect_id (``int`` ``64-bit``, *optional*):
+ Unique identifier of the message effect to be added to the message; for private chats only.
reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*):
Additional interface options. An object for an inline keyboard, custom reply keyboard,
@@ -118,7 +127,8 @@ async def send_location(
media=raw.types.InputMediaGeoPoint(
geo_point=raw.types.InputGeoPoint(
lat=latitude,
- long=longitude
+ long=longitude,
+ accuracy_radius=horizontal_accuracy
)
),
message="",
@@ -127,7 +137,8 @@ async def send_location(
random_id=self.rnd_id(),
schedule_date=utils.datetime_to_timestamp(schedule_date),
noforwards=protect_content,
- reply_markup=await reply_markup.write(self) if reply_markup else None
+ reply_markup=await reply_markup.write(self) if reply_markup else None,
+ effect=message_effect_id
)
if business_connection_id:
r = await self.invoke(
@@ -152,7 +163,8 @@ async def send_location(
self, i.message,
{i.id: i for i in r.users},
{i.id: i for i in r.chats},
- is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage)
+ is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage),
+ replies=self.fetch_replies
)
elif isinstance(
i,
@@ -166,5 +178,6 @@ async def send_location(
{i.id: i for i in r.users},
{i.id: i for i in r.chats},
business_connection_id=getattr(i, "connection_id", business_connection_id),
- raw_reply_to_message=i.reply_to_message
+ raw_reply_to_message=i.reply_to_message,
+ replies=0
)
diff --git a/pyrogram/methods/messages/send_media_group.py b/pyrogram/methods/messages/send_media_group.py
index 01308672a3..fb5f5e9a1e 100644
--- a/pyrogram/methods/messages/send_media_group.py
+++ b/pyrogram/methods/messages/send_media_group.py
@@ -47,6 +47,7 @@ async def send_media_group(
business_connection_id: str = None,
schedule_date: datetime = None,
protect_content: bool = None,
+ message_effect_id: int = None,
reply_to_message_id: int = None
) -> List["types.Message"]:
"""Send a group of photos or videos as an album.
@@ -79,7 +80,10 @@ async def send_media_group(
Date when the message will be automatically sent.
protect_content (``bool``, *optional*):
- Protects the contents of the sent message from forwarding and saving.
+ Pass True if the content of the message must be protected from forwarding and saving; for bots only.
+
+ message_effect_id (``int`` ``64-bit``, *optional*):
+ Unique identifier of the message effect to be added to the message; for private chats only.
Returns:
List of :obj:`~pyrogram.types.Message`: On success, a list of the sent messages is returned.
@@ -112,6 +116,7 @@ async def send_media_group(
)
reply_parameters = types.ReplyParameters(message_id=reply_to_message_id)
+ show_caption_above_media = []
multi_media = []
for i in media:
@@ -183,6 +188,7 @@ async def send_media_group(
),
spoiler=i.has_spoiler
)
+ show_caption_above_media.append(i.show_caption_above_media)
elif isinstance(i, types.InputMediaVideo):
if isinstance(i.media, str):
if os.path.isfile(i.media):
@@ -247,8 +253,7 @@ async def send_media_group(
media=raw.types.InputMediaUploadedDocument(
file=await self.save_file(i.media),
thumb=await self.save_file(i.thumb),
- nosound_video=i.disable_content_type_detection,
- force_file=not i.disable_content_type_detection or None,
+ nosound_video=i.disable_content_type_detection or True,
spoiler=i.has_spoiler,
mime_type=self.guess_mime_type(getattr(i.media, "name", "video.mp4")) or "video/mp4",
attributes=[
@@ -272,6 +277,7 @@ async def send_media_group(
),
spoiler=i.has_spoiler
)
+ show_caption_above_media.append(i.show_caption_above_media)
elif isinstance(i, types.InputMediaAudio):
if isinstance(i.media, str):
if os.path.isfile(i.media):
@@ -443,7 +449,9 @@ async def send_media_group(
silent=disable_notification or None,
reply_to=reply_to,
schedule_date=utils.datetime_to_timestamp(schedule_date),
- noforwards=protect_content
+ noforwards=protect_content,
+ effect=message_effect_id,
+ invert_media=any(show_caption_above_media)
)
session = None
business_connection = None
diff --git a/pyrogram/methods/messages/send_message.py b/pyrogram/methods/messages/send_message.py
index daff719aa9..afeb86c132 100644
--- a/pyrogram/methods/messages/send_message.py
+++ b/pyrogram/methods/messages/send_message.py
@@ -39,6 +39,7 @@ async def send_message(
protect_content: bool = None,
message_thread_id: int = None,
business_connection_id: str = None,
+ message_effect_id: int = None,
reply_parameters: "types.ReplyParameters" = None,
reply_markup: Union[
"types.InlineKeyboardMarkup",
@@ -78,7 +79,7 @@ async def send_message(
Users will receive a notification with no sound.
protect_content (``bool``, *optional*):
- Protects the contents of the sent message from forwarding and saving.
+ Pass True if the content of the message must be protected from forwarding and saving; for bots only.
message_thread_id (``int``, *optional*):
If the message is in a thread, ID of the original message.
@@ -86,6 +87,9 @@ async def send_message(
business_connection_id (``str``, *optional*):
Unique identifier of the business connection on behalf of which the message will be sent.
+ message_effect_id (``int`` ``64-bit``, *optional*):
+ Unique identifier of the message effect to be added to the message; for private chats only.
+
reply_parameters (:obj:`~pyrogram.types.ReplyParameters`, *optional*):
Description of the message to reply to
@@ -150,6 +154,8 @@ async def send_message(
)
link_preview_options = types.LinkPreviewOptions(is_disabled=disable_web_page_preview)
+ link_preview_options = link_preview_options or self.link_preview_options
+
if reply_to_message_id and reply_parameters:
raise ValueError(
"Parameters `reply_to_message_id` and `reply_parameters` are mutually "
@@ -197,11 +203,13 @@ async def send_message(
media=raw.types.InputMediaWebPage(
url=link_preview_options.url,
force_large_media=link_preview_options.prefer_large_media,
- force_small_media=link_preview_options.prefer_small_media
+ force_small_media=link_preview_options.prefer_small_media,
+ optional=True
),
invert_media=link_preview_options.show_above_text,
entities=entities,
- noforwards=protect_content
+ noforwards=protect_content,
+ effect=message_effect_id
)
if business_connection_id:
r = await session.invoke(
@@ -213,7 +221,7 @@ async def send_message(
# await session.stop()
else:
r = await self.invoke(rpc)
- except errors.WebpageNotFound:
+ except errors.MessageEmpty:
if not message:
raise ValueError(
"Bad Request: text is empty"
@@ -247,6 +255,7 @@ async def send_message(
message=message,
entities=entities,
# TODO
+ effect=message_effect_id
)
if business_connection_id:
r = await session.invoke(
@@ -277,6 +286,7 @@ async def send_message(
message=message,
entities=entities,
# TODO
+ effect=message_effect_id
)
if business_connection_id:
r = await session.invoke(
@@ -303,19 +313,23 @@ async def send_message(
return types.Message(
id=r.id,
+ outgoing=r.out,
+ date=utils.timestamp_to_datetime(r.date),
+ entities=[
+ types.MessageEntity._parse(None, entity, {})
+ for entity in r.entities
+ ] if r.entities else None,
+ message_auto_delete_timer_changed=types.MessageAutoDeleteTimerChanged(
+ message_auto_delete_time=getattr(r, "ttl_period", None)
+ ),
+ # TODO: #52 fix inconsistency
chat=types.Chat(
id=peer_id,
type=enums.ChatType.PRIVATE,
client=self
),
text=message,
- date=utils.timestamp_to_datetime(r.date),
- outgoing=r.out,
reply_markup=reply_markup,
- entities=[
- types.MessageEntity._parse(None, entity, {})
- for entity in entities
- ] if entities else None,
client=self
)
@@ -333,7 +347,7 @@ async def send_message(
{i.id: i for i in r.users},
{i.id: i for i in r.chats},
is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage),
- replies=0
+ replies=self.fetch_replies
)
elif isinstance(
i,
@@ -347,5 +361,6 @@ async def send_message(
{i.id: i for i in r.users},
{i.id: i for i in r.chats},
business_connection_id=getattr(i, "connection_id", business_connection_id),
- raw_reply_to_message=i.reply_to_message
+ raw_reply_to_message=i.reply_to_message,
+ replies=0
)
diff --git a/pyrogram/methods/messages/send_paid_media.py b/pyrogram/methods/messages/send_paid_media.py
new file mode 100644
index 0000000000..1d92953b68
--- /dev/null
+++ b/pyrogram/methods/messages/send_paid_media.py
@@ -0,0 +1,274 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+import os
+import re
+
+from datetime import datetime
+from typing import Union, List, Optional
+
+import pyrogram
+from pyrogram import enums, raw, types, utils
+from pyrogram.file_id import FileType
+
+
+class SendPaidMedia:
+ async def send_paid_media(
+ self: "pyrogram.Client",
+ chat_id: Union[int, str],
+ star_count: int,
+ media: List[Union[
+ "types.InputPaidMediaPhoto",
+ "types.InputPaidMediaVideo"
+ ]],
+ caption: str = "",
+ parse_mode: Optional["enums.ParseMode"] = None,
+ caption_entities: List["types.MessageEntity"] = None,
+ show_caption_above_media: bool = None,
+ disable_notification: bool = None,
+ protect_content: bool = None,
+ reply_parameters: "types.ReplyParameters" = None,
+ reply_markup: Union[
+ "types.InlineKeyboardMarkup",
+ "types.ReplyKeyboardMarkup",
+ "types.ReplyKeyboardRemove",
+ "types.ForceReply"
+ ] = None,
+ schedule_date: datetime = None
+ ) -> "types.Message":
+ """Use this method to send paid media to channel chats.
+
+ .. include:: /_includes/usable-by/users-bots.rst
+
+ Parameters:
+ chat_id (``int`` | ``str``):
+ Unique identifier for the target chat or username of the target channel (in the format @channelusername).
+
+ star_count (``int``):
+ The number of Telegram Stars that must be paid to buy access to the media.
+
+ media (List of :obj:`~pyrogram.types.InputPaidMedia`):
+ A list describing the media to be sent; up to 10 items.
+
+ caption (``str``, *optional*):
+ Media caption, 0-1024 characters after entities parsing.
+
+ parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*):
+ By default, texts are parsed using both Markdown and HTML styles.
+ You can combine both syntaxes together.
+
+ caption_entities (List of :obj:`~pyrogram.types.MessageEntity`):
+ List of special entities that appear in the caption, which can be specified instead of *parse_mode*.
+
+ show_caption_above_media (``bool``, *optional*):
+ Pass True, if the caption must be shown above the message media.
+
+ disable_notification (``bool``, *optional*):
+ Sends the message silently. Users will receive a notification with no sound.
+
+ protect_content (``bool``, *optional*):
+ Pass True if the content of the message must be protected from forwarding and saving; for bots only.
+
+ reply_parameters (:obj:`~pyrogram.types.ReplyParameters`, *optional*):
+ Description of the message to reply to
+
+ reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*):
+ Additional interface options. An object for an inline keyboard, custom reply keyboard,
+ instructions to remove reply keyboard or to force a reply from the user.
+
+ schedule_date (:obj:`~datetime.datetime`, *optional*):
+ Date when the message will be automatically sent. Pass a :obj:`~datetime.datetime` object.
+
+ Returns:
+ :obj:`~pyrogram.types.Message`: On success, the sent message is returned.
+
+ """
+ multi_media = []
+
+ peer = await self.resolve_peer(chat_id)
+ for i in media:
+ if isinstance(i, types.InputPaidMediaPhoto):
+ if isinstance(i.media, str):
+ if os.path.isfile(i.media):
+ media = await self.invoke(
+ raw.functions.messages.UploadMedia(
+ peer=await self.resolve_peer(chat_id),
+ media=raw.types.InputMediaUploadedPhoto(
+ file=await self.save_file(i.media)
+ )
+ )
+ )
+
+ media = raw.types.InputMediaPhoto(
+ id=raw.types.InputPhoto(
+ id=media.photo.id,
+ access_hash=media.photo.access_hash,
+ file_reference=media.photo.file_reference
+ )
+ )
+ elif re.match("^https?://", i.media):
+ media = await self.invoke(
+ raw.functions.messages.UploadMedia(
+ peer=await self.resolve_peer(chat_id),
+ media=raw.types.InputMediaPhotoExternal(
+ url=i.media
+ )
+ )
+ )
+
+ media = raw.types.InputMediaPhoto(
+ id=raw.types.InputPhoto(
+ id=media.photo.id,
+ access_hash=media.photo.access_hash,
+ file_reference=media.photo.file_reference
+ )
+ )
+ else:
+ media = utils.get_input_media_from_file_id(i.media, FileType.PHOTO)
+ else:
+ media = await self.invoke(
+ raw.functions.messages.UploadMedia(
+ peer=await self.resolve_peer(chat_id),
+ media=raw.types.InputMediaUploadedPhoto(
+ file=await self.save_file(i.media)
+ )
+ )
+ )
+
+ media = raw.types.InputMediaPhoto(
+ id=raw.types.InputPhoto(
+ id=media.photo.id,
+ access_hash=media.photo.access_hash,
+ file_reference=media.photo.file_reference
+ )
+ )
+ elif (
+ isinstance(i, types.InputPaidMediaVideo)
+ ):
+ if isinstance(i.media, str):
+ if os.path.isfile(i.media):
+ attributes = [
+ raw.types.DocumentAttributeVideo(
+ supports_streaming=i.supports_streaming or None,
+ duration=i.duration,
+ w=i.width,
+ h=i.height
+ ),
+ raw.types.DocumentAttributeFilename(file_name=os.path.basename(i.media))
+ ]
+ media = await self.invoke(
+ raw.functions.messages.UploadMedia(
+ peer=await self.resolve_peer(chat_id),
+ media=raw.types.InputMediaUploadedDocument(
+ file=await self.save_file(i.media),
+ thumb=await self.save_file(i.thumbnail),
+ mime_type=self.guess_mime_type(i.media) or "video/mp4",
+ nosound_video=True,
+ attributes=attributes
+ )
+ )
+ )
+
+ media = raw.types.InputMediaDocument(
+ id=raw.types.InputDocument(
+ id=media.document.id,
+ access_hash=media.document.access_hash,
+ file_reference=media.document.file_reference
+ )
+ )
+ elif re.match("^https?://", i.media):
+ media = await self.invoke(
+ raw.functions.messages.UploadMedia(
+ peer=await self.resolve_peer(chat_id),
+ media=raw.types.InputMediaDocumentExternal(
+ url=i.media
+ )
+ )
+ )
+
+ media = raw.types.InputMediaDocument(
+ id=raw.types.InputDocument(
+ id=media.document.id,
+ access_hash=media.document.access_hash,
+ file_reference=media.document.file_reference
+ )
+ )
+ else:
+ media = utils.get_input_media_from_file_id(i.media, FileType.VIDEO)
+ else:
+ media = await self.invoke(
+ raw.functions.messages.UploadMedia(
+ peer=await self.resolve_peer(chat_id),
+ media=raw.types.InputMediaUploadedDocument(
+ file=await self.save_file(i.media),
+ thumb=await self.save_file(i.thumbnail),
+ mime_type=self.guess_mime_type(getattr(i.media, "name", "video.mp4")) or "video/mp4",
+ attributes=[
+ raw.types.DocumentAttributeVideo(
+ supports_streaming=i.supports_streaming or None,
+ duration=i.duration,
+ w=i.width,
+ h=i.height
+ ),
+ raw.types.DocumentAttributeFilename(file_name=getattr(i.media, "name", "video.mp4"))
+ ]
+ )
+ )
+ )
+
+ media = raw.types.InputMediaDocument(
+ id=raw.types.InputDocument(
+ id=media.document.id,
+ access_hash=media.document.access_hash,
+ file_reference=media.document.file_reference
+ )
+ )
+ else:
+ raise ValueError(f"{i.__class__.__name__} is not a supported type for send_paid_media")
+ multi_media.append(media)
+
+ rpc = raw.functions.messages.SendMedia(
+ peer=await self.resolve_peer(chat_id),
+ media=raw.types.InputMediaPaidMedia(
+ stars_amount=star_count,
+ extended_media=multi_media
+ ),
+ silent=disable_notification or None,
+ random_id=self.rnd_id(),
+ schedule_date=utils.datetime_to_timestamp(schedule_date),
+ noforwards=protect_content,
+ invert_media=show_caption_above_media,
+ **await utils.parse_text_entities(self, caption, parse_mode, caption_entities)
+ )
+ r = await self.invoke(rpc, sleep_threshold=60)
+ for i in r.updates:
+ if isinstance(
+ i,
+ (
+ raw.types.UpdateNewMessage,
+ raw.types.UpdateNewChannelMessage,
+ raw.types.UpdateNewScheduledMessage
+ )
+ ):
+ return await types.Message._parse(
+ self, i.message,
+ {i.id: i for i in r.users},
+ {i.id: i for i in r.chats},
+ is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage),
+ replies=self.fetch_replies
+ )
diff --git a/pyrogram/methods/messages/send_photo.py b/pyrogram/methods/messages/send_photo.py
index 796b3a9030..dbfcfaebc0 100644
--- a/pyrogram/methods/messages/send_photo.py
+++ b/pyrogram/methods/messages/send_photo.py
@@ -39,6 +39,7 @@ async def send_photo(
caption: str = "",
parse_mode: Optional["enums.ParseMode"] = None,
caption_entities: List["types.MessageEntity"] = None,
+ show_caption_above_media: bool = None,
has_spoiler: bool = None,
ttl_seconds: int = None,
disable_notification: bool = None,
@@ -48,6 +49,7 @@ async def send_photo(
schedule_date: datetime = None,
protect_content: bool = None,
view_once: bool = None,
+ message_effect_id: int = None,
reply_markup: Union[
"types.InlineKeyboardMarkup",
"types.ReplyKeyboardMarkup",
@@ -85,6 +87,9 @@ async def send_photo(
caption_entities (List of :obj:`~pyrogram.types.MessageEntity`):
List of special entities that appear in the caption, which can be specified instead of *parse_mode*.
+ show_caption_above_media (``bool``, *optional*):
+ Pass True, if the caption must be shown above the message media.
+
has_spoiler (``bool``, *optional*):
Pass True if the photo needs to be covered with a spoiler animation.
@@ -110,11 +115,14 @@ async def send_photo(
Date when the message will be automatically sent.
protect_content (``bool``, *optional*):
- Protects the contents of the sent message from forwarding and saving.
+ Pass True if the content of the message must be protected from forwarding and saving; for bots only.
view_once (``bool``, *optional*):
Pass True if the photo should be viewable only once.
+ message_effect_id (``int`` ``64-bit``, *optional*):
+ Unique identifier of the message effect to be added to the message; for private chats only.
+
reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*):
Additional interface options. An object for an inline keyboard, custom reply keyboard,
instructions to remove reply keyboard or to force a reply from the user.
@@ -221,6 +229,8 @@ async def send_photo(
schedule_date=utils.datetime_to_timestamp(schedule_date),
noforwards=protect_content,
reply_markup=await reply_markup.write(self) if reply_markup else None,
+ effect=message_effect_id,
+ invert_media=show_caption_above_media,
**await utils.parse_text_entities(self, caption, parse_mode, caption_entities)
)
session = None
@@ -262,7 +272,8 @@ async def send_photo(
self, i.message,
{i.id: i for i in r.users},
{i.id: i for i in r.chats},
- is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage)
+ is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage),
+ replies=self.fetch_replies
)
elif isinstance(
i,
@@ -276,7 +287,8 @@ async def send_photo(
{i.id: i for i in r.users},
{i.id: i for i in r.chats},
business_connection_id=getattr(i, "connection_id", business_connection_id),
- raw_reply_to_message=i.reply_to_message
+ raw_reply_to_message=i.reply_to_message,
+ replies=0
)
except pyrogram.StopTransmission:
return None
diff --git a/pyrogram/methods/messages/send_poll.py b/pyrogram/methods/messages/send_poll.py
index f4aa5d4680..6ae28cc3fd 100644
--- a/pyrogram/methods/messages/send_poll.py
+++ b/pyrogram/methods/messages/send_poll.py
@@ -31,7 +31,9 @@ async def send_poll(
self: "pyrogram.Client",
chat_id: Union[int, str],
question: str,
- options: List[str],
+ options: List["types.InputPollOption"],
+ question_parse_mode: "enums.ParseMode" = None,
+ question_entities: List["types.MessageEntity"] = None,
is_anonymous: bool = True,
type: "enums.PollType" = enums.PollType.REGULAR,
allows_multiple_answers: bool = None,
@@ -48,6 +50,7 @@ async def send_poll(
message_thread_id: int = None,
business_connection_id: str = None,
schedule_date: datetime = None,
+ message_effect_id: int = None,
reply_markup: Union[
"types.InlineKeyboardMarkup",
"types.ReplyKeyboardMarkup",
@@ -69,14 +72,21 @@ async def send_poll(
question (``str``):
Poll question, 1-255 characters.
- options (List of ``str``):
- List of answer options, 2-10 strings 1-100 characters each.
+ options (List of :obj:`~pyrogram.types.InputPollOption`):
+ List of answer options, 2-10 answer options.
+
+ question_parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*):
+ By default, texts are parsed using both Markdown and HTML styles.
+ You can combine both syntaxes together.
+
+ question_entities (List of :obj:`~pyrogram.types.MessageEntity`):
+ List of special entities that appear in the poll question, which can be specified instead of *question_parse_mode*.
is_anonymous (``bool``, *optional*):
True, if the poll needs to be anonymous.
Defaults to True.
- type (:obj`~pyrogram.enums.PollType`, *optional*):
+ type (:obj:`~pyrogram.enums.PollType`, *optional*):
Poll type, :obj:`~pyrogram.enums.PollType.QUIZ` or :obj:`~pyrogram.enums.PollType.REGULAR`.
Defaults to :obj:`~pyrogram.enums.PollType.REGULAR`.
@@ -97,7 +107,7 @@ async def send_poll(
explanation_entities (List of :obj:`~pyrogram.types.MessageEntity`):
List of special entities that appear in the poll explanation, which can be specified instead of
- *parse_mode*.
+ *explanation_parse_mode*.
open_period (``int``, *optional*):
Amount of time in seconds the poll will be active after creation, 5-600.
@@ -117,7 +127,7 @@ async def send_poll(
Users will receive a notification with no sound.
protect_content (``bool``, *optional*):
- Protects the contents of the sent message from forwarding and saving.
+ Pass True if the content of the message must be protected from forwarding and saving; for bots only.
reply_parameters (:obj:`~pyrogram.types.ReplyParameters`, *optional*):
Description of the message to reply to
@@ -131,6 +141,9 @@ async def send_poll(
schedule_date (:py:obj:`~datetime.datetime`, *optional*):
Date when the message will be automatically sent.
+ message_effect_id (``int`` ``64-bit``, *optional*):
+ Unique identifier of the message effect to be added to the message; for private chats only.
+
reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*):
Additional interface options. An object for an inline keyboard, custom reply keyboard,
instructions to remove reply keyboard or to force a reply from the user.
@@ -166,16 +179,36 @@ async def send_poll(
message_thread_id,
reply_parameters
)
+
+ question, question_entities = (await utils.parse_text_entities(self, question, question_parse_mode, question_entities)).values()
+ if not question_entities:
+ question_entities = []
+
+ answers = []
+ for i, answer_ in enumerate(options):
+ answer, answer_entities = (await utils.parse_text_entities(self, answer_.text, answer_.text_parse_mode, answer_.text_entities)).values()
+ if not answer_entities:
+ answer_entities = []
+ answers.append(
+ raw.types.PollAnswer(
+ text=raw.types.TextWithEntities(
+ text=answer,
+ entities=answer_entities
+ ),
+ option=bytes([i])
+ )
+ )
+
rpc = raw.functions.messages.SendMedia(
peer=await self.resolve_peer(chat_id),
media=raw.types.InputMediaPoll(
poll=raw.types.Poll(
id=self.rnd_id(),
- question=question,
- answers=[
- raw.types.PollAnswer(text=text, option=bytes([i]))
- for i, text in enumerate(options)
- ],
+ question=raw.types.TextWithEntities(
+ text=question,
+ entities=question_entities
+ ),
+ answers=answers,
closed=is_closed,
public_voters=not is_anonymous,
multiple_choice=allows_multiple_answers,
@@ -193,7 +226,8 @@ async def send_poll(
random_id=self.rnd_id(),
schedule_date=utils.datetime_to_timestamp(schedule_date),
noforwards=protect_content,
- reply_markup=await reply_markup.write(self) if reply_markup else None
+ reply_markup=await reply_markup.write(self) if reply_markup else None,
+ effect=message_effect_id
)
if business_connection_id:
r = await self.invoke(
@@ -218,7 +252,8 @@ async def send_poll(
self, i.message,
{i.id: i for i in r.users},
{i.id: i for i in r.chats},
- is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage)
+ is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage),
+ replies=self.fetch_replies
)
elif isinstance(
i,
@@ -232,5 +267,6 @@ async def send_poll(
{i.id: i for i in r.users},
{i.id: i for i in r.chats},
business_connection_id=getattr(i, "connection_id", business_connection_id),
- raw_reply_to_message=i.reply_to_message
+ raw_reply_to_message=i.reply_to_message,
+ replies=0
)
diff --git a/pyrogram/methods/messages/send_sticker.py b/pyrogram/methods/messages/send_sticker.py
index 820f77b73c..b26aba79bb 100644
--- a/pyrogram/methods/messages/send_sticker.py
+++ b/pyrogram/methods/messages/send_sticker.py
@@ -44,6 +44,7 @@ async def send_sticker(
protect_content: bool = None,
message_thread_id: int = None,
business_connection_id: str = None,
+ message_effect_id: int = None,
reply_parameters: "types.ReplyParameters" = None,
reply_markup: Union[
"types.InlineKeyboardMarkup",
@@ -91,7 +92,7 @@ async def send_sticker(
Users will receive a notification with no sound.
protect_content (``bool``, *optional*):
- Protects the contents of the sent message from forwarding and saving.
+ Pass True if the content of the message must be protected from forwarding and saving; for bots only.
message_thread_id (``int``, *optional*):
If the message is in a thread, ID of the original message.
@@ -99,6 +100,9 @@ async def send_sticker(
business_connection_id (``str``, *optional*):
Unique identifier of the business connection on behalf of which the message will be sent.
+ message_effect_id (``int`` ``64-bit``, *optional*):
+ Unique identifier of the message effect to be added to the message; for private chats only.
+
reply_parameters (:obj:`~pyrogram.types.ReplyParameters`, *optional*):
Description of the message to reply to
@@ -208,6 +212,7 @@ async def send_sticker(
schedule_date=utils.datetime_to_timestamp(schedule_date),
+ effect=message_effect_id,
**await utils.parse_text_entities(self, caption, parse_mode, caption_entities)
)
session = None
@@ -249,7 +254,8 @@ async def send_sticker(
self, i.message,
{i.id: i for i in r.users},
{i.id: i for i in r.chats},
- is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage)
+ is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage),
+ replies=self.fetch_replies
)
elif isinstance(
i,
@@ -263,7 +269,8 @@ async def send_sticker(
{i.id: i for i in r.users},
{i.id: i for i in r.chats},
business_connection_id=getattr(i, "connection_id", business_connection_id),
- raw_reply_to_message=i.reply_to_message
+ raw_reply_to_message=i.reply_to_message,
+ replies=0
)
except StopTransmission:
return None
diff --git a/pyrogram/methods/messages/send_venue.py b/pyrogram/methods/messages/send_venue.py
index 0b20894455..61501fe544 100644
--- a/pyrogram/methods/messages/send_venue.py
+++ b/pyrogram/methods/messages/send_venue.py
@@ -36,12 +36,15 @@ async def send_venue(
address: str,
foursquare_id: str = "",
foursquare_type: str = "",
+ google_place_id: str = "",
+ google_place_type: str = "",
disable_notification: bool = None,
reply_parameters: "types.ReplyParameters" = None,
message_thread_id: int = None,
business_connection_id: str = None,
schedule_date: datetime = None,
protect_content: bool = None,
+ message_effect_id: int = None,
reply_markup: Union[
"types.InlineKeyboardMarkup",
"types.ReplyKeyboardMarkup",
@@ -96,7 +99,10 @@ async def send_venue(
Date when the message will be automatically sent.
protect_content (``bool``, *optional*):
- Protects the contents of the sent message from forwarding and saving.
+ Pass True if the content of the message must be protected from forwarding and saving; for bots only.
+
+ message_effect_id (``int`` ``64-bit``, *optional*):
+ Unique identifier of the message effect to be added to the message; for private chats only.
reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.ForceReply`, *optional*):
Additional interface options. An object for an inline keyboard, custom reply keyboard,
@@ -141,7 +147,7 @@ async def send_venue(
),
title=title,
address=address,
- provider="",
+ provider="", # TODO
venue_id=foursquare_id,
venue_type=foursquare_type
),
@@ -151,7 +157,8 @@ async def send_venue(
random_id=self.rnd_id(),
schedule_date=utils.datetime_to_timestamp(schedule_date),
noforwards=protect_content,
- reply_markup=await reply_markup.write(self) if reply_markup else None
+ reply_markup=await reply_markup.write(self) if reply_markup else None,
+ effect=message_effect_id
)
if business_connection_id:
r = await self.invoke(
@@ -176,7 +183,8 @@ async def send_venue(
self, i.message,
{i.id: i for i in r.users},
{i.id: i for i in r.chats},
- is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage)
+ is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage),
+ replies=self.fetch_replies
)
elif isinstance(
i,
@@ -190,5 +198,6 @@ async def send_venue(
{i.id: i for i in r.users},
{i.id: i for i in r.chats},
business_connection_id=getattr(i, "connection_id", business_connection_id),
- raw_reply_to_message=i.reply_to_message
+ raw_reply_to_message=i.reply_to_message,
+ replies=0
)
diff --git a/pyrogram/methods/messages/send_video.py b/pyrogram/methods/messages/send_video.py
index cbc81820b3..6e588ac643 100644
--- a/pyrogram/methods/messages/send_video.py
+++ b/pyrogram/methods/messages/send_video.py
@@ -39,6 +39,7 @@ async def send_video(
caption: str = "",
parse_mode: Optional["enums.ParseMode"] = None,
caption_entities: List["types.MessageEntity"] = None,
+ show_caption_above_media: bool = None,
duration: int = 0,
width: int = 0,
height: int = 0,
@@ -49,6 +50,7 @@ async def send_video(
protect_content: bool = None,
message_thread_id: int = None,
business_connection_id: str = None,
+ message_effect_id: int = None,
reply_parameters: "types.ReplyParameters" = None,
reply_markup: Union[
"types.InlineKeyboardMarkup",
@@ -91,6 +93,9 @@ async def send_video(
caption_entities (List of :obj:`~pyrogram.types.MessageEntity`):
List of special entities that appear in the caption, which can be specified instead of *parse_mode*.
+ show_caption_above_media (``bool``, *optional*):
+ Pass True, if the caption must be shown above the message media.
+
duration (``int``, *optional*):
Duration of sent video in seconds.
@@ -118,7 +123,7 @@ async def send_video(
Users will receive a notification with no sound.
protect_content (``bool``, *optional*):
- Protects the contents of the sent message from forwarding and saving.
+ Pass True if the content of the message must be protected from forwarding and saving; for bots only.
message_thread_id (``int``, *optional*):
If the message is in a thread, ID of the original message.
@@ -126,6 +131,9 @@ async def send_video(
business_connection_id (``str``, *optional*):
Unique identifier of the business connection on behalf of which the message will be sent.
+ message_effect_id (``int`` ``64-bit``, *optional*):
+ Unique identifier of the message effect to be added to the message; for private chats only.
+
reply_parameters (:obj:`~pyrogram.types.ReplyParameters`, *optional*):
Description of the message to reply to
@@ -221,6 +229,7 @@ async def progress(current, total):
mime_type=self.guess_mime_type(video) or "video/mp4",
file=file,
ttl_seconds=ttl_seconds,
+ nosound_video=True,
spoiler=has_spoiler,
thumb=thumb,
attributes=[
@@ -280,6 +289,8 @@ async def progress(current, total):
schedule_date=utils.datetime_to_timestamp(schedule_date),
noforwards=protect_content,
reply_markup=await reply_markup.write(self) if reply_markup else None,
+ effect=message_effect_id,
+ invert_media=show_caption_above_media,
**await utils.parse_text_entities(self, caption, parse_mode, caption_entities)
)
session = None
@@ -321,7 +332,8 @@ async def progress(current, total):
self, i.message,
{i.id: i for i in r.users},
{i.id: i for i in r.chats},
- is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage)
+ is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage),
+ replies=self.fetch_replies
)
elif isinstance(
i,
@@ -335,7 +347,8 @@ async def progress(current, total):
{i.id: i for i in r.users},
{i.id: i for i in r.chats},
business_connection_id=getattr(i, "connection_id", business_connection_id),
- raw_reply_to_message=i.reply_to_message
+ raw_reply_to_message=i.reply_to_message,
+ replies=0
)
except StopTransmission:
return None
diff --git a/pyrogram/methods/messages/send_video_note.py b/pyrogram/methods/messages/send_video_note.py
index 9675a2b1f8..a9dba1fd66 100644
--- a/pyrogram/methods/messages/send_video_note.py
+++ b/pyrogram/methods/messages/send_video_note.py
@@ -42,6 +42,7 @@ async def send_video_note(
protect_content: bool = None,
message_thread_id: int = None,
business_connection_id: str = None,
+ message_effect_id: int = None,
reply_parameters: "types.ReplyParameters" = None,
reply_markup: Union[
"types.InlineKeyboardMarkup",
@@ -93,7 +94,7 @@ async def send_video_note(
Users will receive a notification with no sound.
protect_content (``bool``, *optional*):
- Protects the contents of the sent message from forwarding and saving.
+ Pass True if the content of the message must be protected from forwarding and saving; for bots only.
message_thread_id (``int``, *optional*):
If the message is in a thread, ID of the original message.
@@ -101,6 +102,9 @@ async def send_video_note(
business_connection_id (``str``, *optional*):
Unique identifier of the business connection on behalf of which the message will be sent.
+ message_effect_id (``int`` ``64-bit``, *optional*):
+ Unique identifier of the message effect to be added to the message; for private chats only.
+
reply_parameters (:obj:`~pyrogram.types.ReplyParameters`, *optional*):
Description of the message to reply to
@@ -245,6 +249,7 @@ async def send_video_note(
schedule_date=utils.datetime_to_timestamp(schedule_date),
noforwards=protect_content,
reply_markup=await reply_markup.write(self) if reply_markup else None,
+ effect=message_effect_id,
**await utils.parse_text_entities(self, caption, parse_mode, caption_entities)
)
session = None
@@ -286,7 +291,8 @@ async def send_video_note(
self, i.message,
{i.id: i for i in r.users},
{i.id: i for i in r.chats},
- is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage)
+ is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage),
+ replies=self.fetch_replies
)
elif isinstance(
i,
@@ -300,7 +306,8 @@ async def send_video_note(
{i.id: i for i in r.users},
{i.id: i for i in r.chats},
business_connection_id=getattr(i, "connection_id", business_connection_id),
- raw_reply_to_message=i.reply_to_message
+ raw_reply_to_message=i.reply_to_message,
+ replies=0
)
except StopTransmission:
return None
diff --git a/pyrogram/methods/messages/send_voice.py b/pyrogram/methods/messages/send_voice.py
index ee8dd32947..c5c7b5de98 100644
--- a/pyrogram/methods/messages/send_voice.py
+++ b/pyrogram/methods/messages/send_voice.py
@@ -44,6 +44,7 @@ async def send_voice(
protect_content: bool = None,
message_thread_id: int = None,
business_connection_id: str = None,
+ message_effect_id: int = None,
reply_parameters: "types.ReplyParameters" = None,
reply_markup: Union[
"types.InlineKeyboardMarkup",
@@ -94,7 +95,7 @@ async def send_voice(
Users will receive a notification with no sound.
protect_content (``bool``, *optional*):
- Protects the contents of the sent message from forwarding and saving.
+ Pass True if the content of the message must be protected from forwarding and saving; for bots only.
message_thread_id (``int``, *optional*):
If the message is in a thread, ID of the original message.
@@ -102,6 +103,9 @@ async def send_voice(
business_connection_id (``str``, *optional*):
Unique identifier of the business connection on behalf of which the message will be sent.
+ message_effect_id (``int`` ``64-bit``, *optional*):
+ Unique identifier of the message effect to be added to the message; for private chats only.
+
reply_parameters (:obj:`~pyrogram.types.ReplyParameters`, *optional*):
Description of the message to reply to
@@ -240,6 +244,7 @@ async def send_voice(
schedule_date=utils.datetime_to_timestamp(schedule_date),
noforwards=protect_content,
reply_markup=await reply_markup.write(self) if reply_markup else None,
+ effect=message_effect_id,
**await utils.parse_text_entities(self, caption, parse_mode, caption_entities)
)
session = None
@@ -281,7 +286,8 @@ async def send_voice(
self, i.message,
{i.id: i for i in r.users},
{i.id: i for i in r.chats},
- is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage)
+ is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage),
+ replies=self.fetch_replies
)
elif isinstance(
i,
@@ -295,7 +301,8 @@ async def send_voice(
{i.id: i for i in r.users},
{i.id: i for i in r.chats},
business_connection_id=getattr(i, "connection_id", business_connection_id),
- raw_reply_to_message=i.reply_to_message
+ raw_reply_to_message=i.reply_to_message,
+ replies=0
)
except StopTransmission:
return None
diff --git a/pyrogram/methods/messages/set_reaction.py b/pyrogram/methods/messages/set_reaction.py
index 113062e3cd..b96661ebfc 100644
--- a/pyrogram/methods/messages/set_reaction.py
+++ b/pyrogram/methods/messages/set_reaction.py
@@ -16,12 +16,15 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see .
+import logging
from typing import Union
from typing import List
import pyrogram
from pyrogram import raw, types
+log = logging.getLogger(__name__)
+
class SetReaction:
async def set_reaction(
@@ -39,9 +42,9 @@ async def set_reaction(
a channel to its discussion group have the
same available reactions as messages in the channel.
- You must use exactly one of ``chat_id`` OR ``story_id``.
+ You must use exactly one of ``message_id`` OR ``story_id``.
- If you specify, ``chat_id``
+ If you specify, ``message_id``
.. include:: /_includes/usable-by/users-bots.rst
@@ -123,3 +126,28 @@ async def set_reaction(
return r
else:
raise ValueError("You need to pass one of message_id OR story_id!")
+
+
+ async def send_reaction(
+ self: "pyrogram.Client",
+ chat_id: Union[int, str],
+ message_id: int,
+ emoji: str = "",
+ big: bool = False,
+ add_to_recent: bool = True
+ ) -> bool:
+ log.warning(
+ "This property is deprecated. "
+ "Please use set_reaction instead"
+ )
+ return bool(
+ await self.invoke(
+ raw.functions.messages.SendReaction(
+ reaction=[raw.types.ReactionEmoji(emoticon=emoji)] if emoji else None,
+ big=big,
+ peer=await self.resolve_peer(chat_id),
+ msg_id=message_id,
+ add_to_recent=add_to_recent
+ )
+ )
+ )
diff --git a/pyrogram/methods/messages/stop_poll.py b/pyrogram/methods/messages/stop_poll.py
index 7129f92c3d..65380e37ac 100644
--- a/pyrogram/methods/messages/stop_poll.py
+++ b/pyrogram/methods/messages/stop_poll.py
@@ -19,8 +19,9 @@
from typing import Union
import pyrogram
-from pyrogram import raw
-from pyrogram import types
+from pyrogram import raw, types
+
+from .inline_session import get_session
class StopPoll:
@@ -28,7 +29,8 @@ async def stop_poll(
self: "pyrogram.Client",
chat_id: Union[int, str],
message_id: int,
- reply_markup: "types.InlineKeyboardMarkup" = None
+ reply_markup: "types.InlineKeyboardMarkup" = None,
+ business_connection_id: str = None
) -> "types.Poll":
"""Stop a poll which was sent by you.
@@ -48,6 +50,9 @@ async def stop_poll(
reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup`, *optional*):
An InlineKeyboardMarkup object.
+ business_connection_id (``str``, *optional*):
+ Unique identifier of the business connection on behalf of which the message to be edited was sent
+
Returns:
:obj:`~pyrogram.types.Poll`: On success, the stopped poll with the final results is returned.
@@ -61,20 +66,38 @@ async def stop_poll(
message_ids=message_id
)).poll
# TODO
- r = await self.invoke(
- raw.functions.messages.EditMessage(
- peer=await self.resolve_peer(chat_id),
- id=message_id,
- media=raw.types.InputMediaPoll(
- poll=raw.types.Poll(
- id=int(poll.id),
- closed=True,
- question="",
- answers=[]
- )
- ),
- reply_markup=await reply_markup.write(self) if reply_markup else None
- )
+ rpc = raw.functions.messages.EditMessage(
+ peer=await self.resolve_peer(chat_id),
+ id=message_id,
+ media=raw.types.InputMediaPoll(
+ poll=raw.types.Poll(
+ id=int(poll.id),
+ closed=True,
+ question=raw.types.TextWithEntities(text="", entities=[]),
+ answers=[]
+ )
+ ),
+ reply_markup=await reply_markup.write(self) if reply_markup else None
)
+ session = None
+ business_connection = None
+ if business_connection_id:
+ business_connection = self.business_user_connection_cache[business_connection_id]
+ if not business_connection:
+ business_connection = await self.get_business_connection(business_connection_id)
+ session = await get_session(
+ self,
+ business_connection._raw.connection.dc_id
+ )
+ if business_connection_id:
+ r = await session.invoke(
+ raw.functions.InvokeWithBusinessConnection(
+ query=rpc,
+ connection_id=business_connection_id
+ )
+ )
+ # await session.stop()
+ else:
+ r = await self.invoke(rpc)
return types.Poll._parse(self, r.updates[0])
diff --git a/pyrogram/methods/messages/translate_text.py b/pyrogram/methods/messages/translate_text.py
new file mode 100644
index 0000000000..fa1b367488
--- /dev/null
+++ b/pyrogram/methods/messages/translate_text.py
@@ -0,0 +1,139 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+from typing import List, Optional, Union
+
+import pyrogram
+from pyrogram import enums, raw, types, utils
+
+
+class TranslateText:
+ async def translate_message_text(
+ self: "pyrogram.Client",
+ to_language_code: str,
+ chat_id: Union[int, str],
+ message_ids: Union[int, List[int]]
+ ) -> Union["types.TranslatedText", List["types.TranslatedText"]]:
+ """Extracts text or caption of the given message and translates it to the given language. If the current user is a Telegram Premium user, then text formatting is preserved.
+
+ .. include:: /_includes/usable-by/users.rst
+
+ Parameters:
+ to_language_code (``str``):
+ Language code of the language to which the message is translated.
+ Must be one of "af", "sq", "am", "ar", "hy", "az", "eu", "be", "bn", "bs", "bg", "ca", "ceb", "zh-CN", "zh", "zh-Hans", "zh-TW", "zh-Hant", "co", "hr", "cs", "da", "nl", "en", "eo", "et", "fi", "fr", "fy", "gl", "ka", "de", "el", "gu", "ht", "ha", "haw", "he", "iw", "hi", "hmn", "hu", "is", "ig", "id", "in", "ga", "it", "ja", "jv", "kn", "kk", "km", "rw", "ko", "ku", "ky", "lo", "la", "lv", "lt", "lb", "mk", "mg", "ms", "ml", "mt", "mi", "mr", "mn", "my", "ne", "no", "ny", "or", "ps", "fa", "pl", "pt", "pa", "ro", "ru", "sm", "gd", "sr", "st", "sn", "sd", "si", "sk", "sl", "so", "es", "su", "sw", "sv", "tl", "tg", "ta", "tt", "te", "th", "tr", "tk", "uk", "ur", "ug", "uz", "vi", "cy", "xh", "yi", "ji", "yo", "zu".
+
+ chat_id (``int`` | ``str``):
+ Unique identifier (int) or username (str) of the target chat.
+
+ message_ids (``int`` | List of ``int``):
+ Identifier or list of message identifiers of the target message.
+
+ Returns:
+ :obj:`~pyrogram.types.TranslatedText` | List of :obj:`~pyrogram.types.TranslatedText`: In case *message_ids* was not
+ a list, a single result is returned, otherwise a list of results is returned.
+
+ Example:
+ .. code-block:: python
+
+ await app.translate_message_text("en", chat_id, message_id)
+ """
+ ids = [message_ids] if not isinstance(message_ids, list) else message_ids
+
+ r = await self.invoke(
+ raw.functions.messages.TranslateText(
+ to_lang=to_language_code,
+ peer=await self.resolve_peer(chat_id),
+ id=ids
+ )
+ )
+
+ return (
+ types.TranslatedText._parse(self, r.result[0])
+ if len(r.result) == 1
+ else [
+ types.TranslatedText._parse(self, i)
+ for i in r.result
+ ]
+ )
+
+
+ async def translate_text(
+ self: "pyrogram.Client",
+ to_language_code: str,
+ text: str,
+ parse_mode: Optional["enums.ParseMode"] = None,
+ entities: List["types.MessageEntity"] = None
+ ) -> Union["types.TranslatedText", List["types.TranslatedText"]]:
+ """Translates a text to the given language. If the current user is a Telegram Premium user, then text formatting is preserved.
+
+ .. include:: /_includes/usable-by/users.rst
+
+ Parameters:
+ to_language_code (``str``):
+ Language code of the language to which the message is translated.
+ Must be one of "af", "sq", "am", "ar", "hy", "az", "eu", "be", "bn", "bs", "bg", "ca", "ceb", "zh-CN", "zh", "zh-Hans", "zh-TW", "zh-Hant", "co", "hr", "cs", "da", "nl", "en", "eo", "et", "fi", "fr", "fy", "gl", "ka", "de", "el", "gu", "ht", "ha", "haw", "he", "iw", "hi", "hmn", "hu", "is", "ig", "id", "in", "ga", "it", "ja", "jv", "kn", "kk", "km", "rw", "ko", "ku", "ky", "lo", "la", "lv", "lt", "lb", "mk", "mg", "ms", "ml", "mt", "mi", "mr", "mn", "my", "ne", "no", "ny", "or", "ps", "fa", "pl", "pt", "pa", "ro", "ru", "sm", "gd", "sr", "st", "sn", "sd", "si", "sk", "sl", "so", "es", "su", "sw", "sv", "tl", "tg", "ta", "tt", "te", "th", "tr", "tk", "uk", "ur", "ug", "uz", "vi", "cy", "xh", "yi", "ji", "yo", "zu".
+
+ text (``str``):
+ Text to translate.
+
+ parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*):
+ By default, texts are parsed using both Markdown and HTML styles.
+ You can combine both syntaxes together.
+
+ entities (List of :obj:`~pyrogram.types.MessageEntity`):
+ List of special entities that appear in message text, which can be specified instead of *parse_mode*.
+
+ Returns:
+ :obj:`~pyrogram.types.TranslatedText` | List of :obj:`~pyrogram.types.TranslatedText`: In case *message_ids* was not
+ a list, a single result is returned, otherwise a list of results is returned.
+
+ Example:
+ .. code-block:: python
+
+ await app.translate_text("fa", "Pyrogram")
+ """
+ message, entities = (
+ await utils.parse_text_entities(
+ self,
+ text,
+ parse_mode,
+ entities
+ )
+ ).values()
+
+ r = await self.invoke(
+ raw.functions.messages.TranslateText(
+ to_lang=to_language_code,
+ text=[
+ raw.types.TextWithEntities(
+ text=message,
+ entities=entities or []
+ )
+ ]
+ )
+ )
+
+ return (
+ types.TranslatedText._parse(self, r.result[0])
+ if len(r.result) == 1
+ else [
+ types.TranslatedText._parse(self, i)
+ for i in r.result
+ ]
+ )
diff --git a/pyrogram/methods/messages/view_messages.py b/pyrogram/methods/messages/view_messages.py
new file mode 100644
index 0000000000..bf76ada083
--- /dev/null
+++ b/pyrogram/methods/messages/view_messages.py
@@ -0,0 +1,65 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+from typing import Union, List
+
+import pyrogram
+from pyrogram import raw
+
+
+class ViewMessages:
+ async def view_messages(
+ self: "pyrogram.Client",
+ chat_id: Union[int, str],
+ message_ids: Union[int, List[int]],
+ force_read: bool = True
+ ) -> bool:
+ """Informs the server that messages are being viewed by the current user.
+
+ .. include:: /_includes/usable-by/users.rst
+
+ Parameters:
+ chat_id (``int`` | ``str``):
+ Unique identifier (int) or username (str) of the target chat.
+
+ message_ids (``int`` | List of ``int``):
+ Identifier or list of message identifiers of the target message.
+
+ force_read (``bool``, *optional*):
+ Pass True to mark as read the specified messages and also increment the view counter.
+
+ Returns:
+ ``bool``: On success, True is returned.
+
+ Example:
+ .. code-block:: python
+
+ # Increment message views
+ await app.view_messages(chat_id, 1)
+ """
+ ids = [message_ids] if not isinstance(message_ids, list) else message_ids
+
+ r = await self.invoke(
+ raw.functions.messages.GetMessagesViews(
+ peer=await self.resolve_peer(chat_id),
+ id=ids,
+ increment=force_read
+ )
+ )
+
+ return bool(r)
diff --git a/pyrogram/methods/phone/__init__.py b/pyrogram/methods/phone/__init__.py
new file mode 100644
index 0000000000..622c8f526f
--- /dev/null
+++ b/pyrogram/methods/phone/__init__.py
@@ -0,0 +1,34 @@
+
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+from .load_group_call_participants import LoadGroupCallParticipants
+from .invite_group_call_participants import InviteGroupCallParticipants
+from .create_video_chat import CreateVideoChat
+from .discard_group_call import DiscardGroupCall
+from .get_video_chat_rtmp_url import GetVideoChatRtmpUrl
+
+
+class Phone(
+ InviteGroupCallParticipants,
+ LoadGroupCallParticipants,
+ CreateVideoChat,
+ DiscardGroupCall,
+ GetVideoChatRtmpUrl,
+):
+ pass
diff --git a/pyrogram/methods/phone/create_video_chat.py b/pyrogram/methods/phone/create_video_chat.py
new file mode 100644
index 0000000000..2477dbfe3e
--- /dev/null
+++ b/pyrogram/methods/phone/create_video_chat.py
@@ -0,0 +1,96 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+from typing import Union
+
+from datetime import datetime
+
+import pyrogram
+from pyrogram import types, raw, utils
+
+
+class CreateVideoChat:
+ async def create_video_chat(
+ self: "pyrogram.Client",
+ chat_id: Union[int, str],
+ title: str = None,
+ start_date: datetime = utils.zero_datetime(),
+ is_rtmp_stream: bool = None
+ ) -> "types.Message":
+ """Creates a video chat (a group call bound to a chat).
+
+ Available only for basic groups, supergroups and channels; requires can_manage_video_chats administrator right.
+
+ .. include:: /_includes/usable-by/users.rst
+
+ Parameters:
+ chat_id (``int`` | ``str``):
+ Unique identifier (int) or username (str) of the target chat in which the video chat will be created. A chat can be either a basic group, supergroup or a channel.
+
+ title (``str``, *optional*):
+ Group call title; if empty, chat title will be used.
+
+ start_date (:py:obj:`~datetime.datetime`, *optional*):
+ Point in time (Unix timestamp) when the group call is supposed to be started by an administrator; 0 to start the video chat immediately. The date must be at least 10 seconds and at most 8 days in the future.
+
+ is_rtmp_stream (``bool``, *optional*):
+ Pass true to create an RTMP stream instead of an ordinary video chat; requires owner privileges.
+
+ Returns:
+ :obj:`~pyrogram.types.Message`: On success, the sent service message is returned.
+
+ Example:
+ .. code-block:: python
+
+ await app.create_video_chat(chat_id)
+
+ """
+ peer = await self.resolve_peer(chat_id)
+
+ if not isinstance(peer, (raw.types.InputPeerChat, raw.types.InputPeerChannel)):
+ raise ValueError(
+ "Target chat should be group, supergroup or channel."
+ )
+
+ r = await self.invoke(
+ raw.functions.phone.CreateGroupCall(
+ rtmp_stream=is_rtmp_stream,
+ peer=peer,
+ # TODO: temp. workaround
+ random_id=self.rnd_id() >> 32,
+ title=title,
+ schedule_date=utils.datetime_to_timestamp(start_date),
+ )
+ )
+
+ for i in r.updates:
+ if isinstance(
+ i,
+ (
+ raw.types.UpdateNewChannelMessage,
+ raw.types.UpdateNewMessage,
+ raw.types.UpdateNewScheduledMessage,
+ ),
+ ):
+ return await types.Message._parse(
+ self,
+ i.message,
+ {i.id: i for i in r.users},
+ {i.id: i for i in r.chats},
+ is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage),
+ )
diff --git a/pyrogram/methods/phone/discard_group_call.py b/pyrogram/methods/phone/discard_group_call.py
new file mode 100644
index 0000000000..fb911a7e99
--- /dev/null
+++ b/pyrogram/methods/phone/discard_group_call.py
@@ -0,0 +1,85 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+from typing import Union
+
+import pyrogram
+from pyrogram import types, raw
+
+
+class DiscardGroupCall:
+ async def discard_group_call(
+ # TODO
+ self: "pyrogram.Client",
+ chat_id: Union[int, str],
+ ) -> "types.Message":
+ """Terminate a group/channel call or livestream
+
+ .. include:: /_includes/usable-by/users.rst
+
+ Parameters:
+ chat_id (``int`` | ``str``):
+ Unique identifier (int) or username (str) of the target chat. A chat can be either a basic group, supergroup or a channel.
+
+ Returns:
+ :obj:`~pyrogram.types.Message`: On success, the sent service message is returned.
+
+ Example:
+ .. code-block:: python
+
+ await app.discard_group_call(chat_id)
+
+ """
+ peer = await self.resolve_peer(chat_id)
+
+ if isinstance(peer, raw.types.InputPeerChannel):
+ r = await self.invoke(raw.functions.channels.GetFullChannel(channel=peer))
+ elif isinstance(peer, raw.types.InputPeerChat):
+ r = await self.invoke(
+ raw.functions.messages.GetFullChat(chat_id=peer.chat_id)
+ )
+ else:
+ raise ValueError("Target chat should be group, supergroup or channel.")
+
+ call = r.full_chat.call
+
+ if call is None:
+ raise ValueError("No active group call at this chat.")
+
+ r = await self.invoke(
+ raw.functions.phone.DiscardGroupCall(
+ call=call
+ )
+ )
+
+ for i in r.updates:
+ if isinstance(
+ i,
+ (
+ raw.types.UpdateNewChannelMessage,
+ raw.types.UpdateNewMessage,
+ raw.types.UpdateNewScheduledMessage,
+ ),
+ ):
+ return await types.Message._parse(
+ self,
+ i.message,
+ {i.id: i for i in r.users},
+ {i.id: i for i in r.chats},
+ is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage),
+ )
diff --git a/pyrogram/methods/phone/get_video_chat_rtmp_url.py b/pyrogram/methods/phone/get_video_chat_rtmp_url.py
new file mode 100644
index 0000000000..bbf14f752f
--- /dev/null
+++ b/pyrogram/methods/phone/get_video_chat_rtmp_url.py
@@ -0,0 +1,63 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+from typing import Union
+
+import pyrogram
+from pyrogram import types, raw
+
+
+class GetVideoChatRtmpUrl:
+ async def get_video_chat_rtmp_url(
+ self: "pyrogram.Client",
+ chat_id: Union[int, str],
+ replace: bool = False
+ ) -> "types.RtmpUrl":
+ """Returns RTMP URL for streaming to the chat; requires owner privileges.
+
+ .. include:: /_includes/usable-by/users.rst
+
+ Parameters:
+ chat_id (``int`` | ``str``):
+ Unique identifier (int) or username (str) of the target chat. A chat can be either a basic group, supergroup or a channel.
+
+ replace (``bool``, *optional*):
+ Whether to replace the previous stream key or simply return the existing one. Defaults to False, i.e., return the existing one.
+
+ Returns:
+ :obj:`~pyrogram.types.RtmpUrl`: On success, the RTMP URL and stream key is returned.
+
+ Example:
+ .. code-block:: python
+
+ await app.get_stream_rtmp_url(chat_id)
+
+ """
+ peer = await self.resolve_peer(chat_id)
+
+ if not isinstance(peer, (raw.types.InputPeerChat, raw.types.InputPeerChannel)):
+ raise ValueError("Target chat should be group, supergroup or channel.")
+
+ r = await self.invoke(
+ raw.functions.phone.GetGroupCallStreamRtmpUrl(
+ peer=peer,
+ revoke=replace
+ )
+ )
+
+ return types.RtmpUrl._parse(r)
diff --git a/pyrogram/methods/phone/invite_group_call_participants.py b/pyrogram/methods/phone/invite_group_call_participants.py
new file mode 100644
index 0000000000..3b6db763fd
--- /dev/null
+++ b/pyrogram/methods/phone/invite_group_call_participants.py
@@ -0,0 +1,96 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+from typing import Union, List
+
+import pyrogram
+from pyrogram import types, raw
+
+
+class InviteGroupCallParticipants:
+ async def invite_group_call_participants(
+ self: "pyrogram.Client",
+ chat_id: Union[int, str],
+ user_ids: Union[Union[int, str], List[Union[int, str]]],
+ ) -> "types.Message":
+ """Invites users to an active group call. Sends a service message of type :obj:`~pyrogram.enums.MessageServiceType.VIDEO_CHAT_PARTICIPANTS_INVITED` for video chats.
+
+ .. include:: /_includes/usable-by/users.rst
+
+ Parameters:
+ chat_id (``int`` | ``str``):
+ Unique identifier (int) or username (str) of the target chat. A chat can be either a basic group or a supergroup.
+
+ user_ids (``int`` | ``str`` | List of ``int`` or ``str``):
+ Users identifiers to invite to group call in the chat.
+ You can pass an ID (int) or username (str).
+ At most 10 users can be invited simultaneously.
+
+ Returns:
+ :obj:`~pyrogram.types.Message`: On success, the sent service message is returned.
+
+ Example:
+ .. code-block:: python
+
+ await app.invite_group_call_participants(chat_id, user_id)
+
+ """
+ peer = await self.resolve_peer(chat_id)
+
+ if isinstance(peer, raw.types.InputPeerChannel):
+ r = await self.invoke(raw.functions.channels.GetFullChannel(channel=peer))
+ elif isinstance(peer, raw.types.InputPeerChat):
+ r = await self.invoke(
+ raw.functions.messages.GetFullChat(chat_id=peer.chat_id)
+ )
+ else:
+ raise ValueError("Target chat should be group, supergroup or channel.")
+
+ call = r.full_chat.call
+
+ if call is None:
+ raise ValueError("No active group call at this chat.")
+
+ user_ids = [user_ids] if not isinstance(user_ids, list) else user_ids
+
+ r = await self.invoke(
+ raw.functions.phone.InviteToGroupCall(
+ call=call,
+ users=[
+ await self.resolve_peer(i)
+ for i in user_ids
+ ]
+ )
+ )
+
+ for i in r.updates:
+ if isinstance(
+ i,
+ (
+ raw.types.UpdateNewChannelMessage,
+ raw.types.UpdateNewMessage,
+ raw.types.UpdateNewScheduledMessage
+ )
+ ):
+ return await types.Message._parse(
+ self,
+ i.message,
+ {i.id: i for i in r.users},
+ {i.id: i for i in r.chats},
+ is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage)
+ )
diff --git a/pyrogram/methods/phone/load_group_call_participants.py b/pyrogram/methods/phone/load_group_call_participants.py
new file mode 100644
index 0000000000..0d402bfc27
--- /dev/null
+++ b/pyrogram/methods/phone/load_group_call_participants.py
@@ -0,0 +1,103 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+from typing import Union, AsyncGenerator
+
+import pyrogram
+from pyrogram import types, raw
+
+
+class LoadGroupCallParticipants:
+ async def load_group_call_participants(
+ self: "pyrogram.Client",
+ chat_id: Union[int, str],
+ limit: int = 0
+ ) -> AsyncGenerator["types.GroupCallParticipant", None]:
+ """Loads participants list in a group call of a chat.
+
+ .. include:: /_includes/usable-by/users.rst
+
+ Parameters:
+ chat_id (``int`` | ``str``):
+ Unique identifier (int) or username (str) of the target chat. A chat can be either a basic group or a supergroup.
+
+ limit (``int``, *optional*):
+ Limits the number of participants to be retrieved. By default, no limit is applied and all participants are returned.
+
+ Returns:
+ ``Generator``: On success, a generator yielding :obj:`~pyrogram.types.GroupCallParticipant` object is returned.
+
+ Example:
+ .. code-block:: python
+
+ # Get participants
+ async for participant in app.load_group_call_participants(chat_id):
+ print(participant)
+
+ """
+ peer = await self.resolve_peer(chat_id)
+
+ if isinstance(peer, raw.types.InputPeerChannel):
+ r = await self.invoke(raw.functions.channels.GetFullChannel(channel=peer))
+ elif isinstance(peer, raw.types.InputPeerChat):
+ r = await self.invoke(raw.functions.messages.GetFullChat(chat_id=peer.chat_id))
+ else:
+ raise ValueError("Target chat should be group, supergroup or channel.")
+
+ full_chat = r.full_chat
+
+ if not getattr(full_chat, "call", None):
+ raise ValueError("There is no active call in this chat.")
+
+ current = 0
+ offset = ""
+ total = abs(limit) or (1 << 31) - 1
+ limit = min(20, total)
+
+ while True:
+ r = await self.invoke(
+ raw.functions.phone.GetGroupParticipants(
+ call=full_chat.call,
+ ids=[],
+ sources=[],
+ offset=offset,
+ limit=limit
+ ),
+ sleep_threshold=60
+ )
+
+ users = {u.id: u for u in r.users}
+ chats = {c.id: c for c in r.chats}
+ participants = [
+ types.GroupCallParticipant._parse(
+ self, participant, users, chats
+ ) for participant in r.participants
+ ]
+
+ if not participants:
+ return
+
+ offset = r.next_offset
+
+ for participant in participants:
+ yield participant
+
+ current += 1
+
+ if current >= total:
+ return
diff --git a/pyrogram/methods/stickers/__init__.py b/pyrogram/methods/stickers/__init__.py
index 1e375ebba2..f213537d69 100644
--- a/pyrogram/methods/stickers/__init__.py
+++ b/pyrogram/methods/stickers/__init__.py
@@ -16,10 +16,12 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see .
+from .get_message_effects import GetMessageEffects
from .get_stickers import GetStickers
class Stickers(
+ GetMessageEffects,
GetStickers,
):
pass
diff --git a/pyrogram/methods/stickers/get_message_effects.py b/pyrogram/methods/stickers/get_message_effects.py
new file mode 100644
index 0000000000..b16e00859c
--- /dev/null
+++ b/pyrogram/methods/stickers/get_message_effects.py
@@ -0,0 +1,67 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+import logging
+from typing import List
+
+import pyrogram
+from pyrogram import raw
+from pyrogram import types
+
+log = logging.getLogger(__name__)
+
+
+class GetMessageEffects:
+ async def get_message_effects(
+ self: "pyrogram.Client"
+ ) -> List["types.MessageEffect"]:
+ """Returns information about all available message effects.
+
+ .. include:: /_includes/usable-by/users.rst
+
+ Returns:
+ List of :obj:`~pyrogram.types.MessageEffect`: A list of message effects is returned.
+
+ Example:
+ .. code-block:: python
+
+ # Get all message effects
+ await app.get_message_effects()
+
+ """
+ r = await self.invoke(
+ raw.functions.messages.GetAvailableEffects(
+ hash=0
+ )
+ )
+ documents = {d.id: d for d in r.documents}
+ outlst = []
+ for effect in r.effects:
+ effect_animation_document = documents.get(effect.effect_sticker_id, None)
+ static_icon_document = documents.get(effect.static_icon_id, None) if getattr(effect, "static_icon_id", None) else None
+ select_animation_document = documents.get(effect.effect_animation_id, None) if getattr(effect, "effect_animation_id", None) else None
+ outlst.append(
+ await types.MessageEffect._parse(
+ self,
+ effect,
+ effect_animation_document,
+ static_icon_document,
+ select_animation_document
+ )
+ )
+ return types.List(outlst)
diff --git a/pyrogram/methods/stories/__init__.py b/pyrogram/methods/stories/__init__.py
new file mode 100644
index 0000000000..b7c0c724a1
--- /dev/null
+++ b/pyrogram/methods/stories/__init__.py
@@ -0,0 +1,25 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+
+from .get_stories import GetStories
+
+class Stories(
+ GetStories,
+):
+ pass
diff --git a/pyrogram/methods/stories/get_stories.py b/pyrogram/methods/stories/get_stories.py
new file mode 100644
index 0000000000..88f6fa77a8
--- /dev/null
+++ b/pyrogram/methods/stories/get_stories.py
@@ -0,0 +1,89 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+from typing import Union, Iterable
+
+import pyrogram
+from pyrogram import raw, types
+
+
+class GetStories:
+ async def get_stories(
+ self: "pyrogram.Client",
+ story_sender_chat_id: Union[int, str],
+ story_ids: Union[int, Iterable[int]],
+ ) -> "types.Story":
+ """Get one or more stories from a chat by using stories identifiers.
+
+ .. include:: /_includes/usable-by/users.rst
+
+ Parameters:
+ story_sender_chat_id (``int`` | ``str``):
+ Identifier of the chat that posted the story.
+
+ story_ids (``int`` | Iterable of ``int``, *optional*):
+ Pass a single story identifier or an iterable of story ids (as integers) to get the content of the
+ story themselves.
+
+ Returns:
+ :obj:`~pyrogram.types.Story` | List of :obj:`~pyrogram.types.Story`: In case *story_ids* was not
+ a list, a single story is returned, otherwise a list of stories is returned.
+
+ Example:
+ .. code-block:: python
+
+ # Get stories by id
+ stories = await app.get_stories(
+ story_sender_chat_id,
+ [1, 2, 3]
+ )
+
+ for story in stories:
+ print(story)
+ """
+ is_iterable = not isinstance(story_ids, int)
+ ids = list(story_ids) if is_iterable else [story_ids]
+
+ peer = await self.resolve_peer(story_sender_chat_id)
+ r = await self.invoke(
+ raw.functions.stories.GetStoriesByID(
+ peer=peer,
+ id=ids
+ )
+ )
+
+ stories = []
+
+ users = {i.id: i for i in r.users}
+ chats = {i.id: i for i in r.chats}
+
+ for story in r.stories:
+ stories.append(
+ await types.Story._parse(
+ self,
+ users,
+ chats,
+ None, None, None,
+ # TODO
+ story,
+ None, #
+ # TODO
+ )
+ )
+
+ return types.List(stories) if is_iterable else stories[0] if stories else None
diff --git a/pyrogram/methods/users/__init__.py b/pyrogram/methods/users/__init__.py
index 9a5be5428a..d61faf20da 100644
--- a/pyrogram/methods/users/__init__.py
+++ b/pyrogram/methods/users/__init__.py
@@ -31,6 +31,8 @@
from .update_profile import UpdateProfile
from .set_birthdate import SetBirthdate
from .set_personal_chat import SetPersonalChat
+from .update_status import UpdateStatus
+from .delete_account import DeleteAccount
class Users(
@@ -49,5 +51,7 @@ class Users(
SetUsername,
UnblockUser,
UpdateProfile,
+ UpdateStatus,
+ DeleteAccount,
):
pass
diff --git a/pyrogram/methods/users/delete_account.py b/pyrogram/methods/users/delete_account.py
new file mode 100644
index 0000000000..692c6d42b3
--- /dev/null
+++ b/pyrogram/methods/users/delete_account.py
@@ -0,0 +1,58 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+import pyrogram
+from pyrogram import raw
+from pyrogram.utils import compute_password_check
+
+
+class DeleteAccount:
+ async def delete_account(
+ self: "pyrogram.Client", reason: str = "", password: str = None
+ ) -> bool:
+ """Deletes the account of the current user, deleting all information associated with the user from the server.
+
+ .. include:: /_includes/usable-by/users.rst
+
+ Parameters:
+ reason (``str``, *optional*):
+ The reason why the account was deleted.
+
+ password (``str``, *optional*):
+ The 2-step verification password of the current user. If the current user isn't authorized, then an empty string can be passed and account deletion can be canceled within one week.
+
+ Returns:
+ `bool`: True On success.
+
+ Example:
+ .. code-block:: python
+
+ await app.delete_account(reason, password)
+ """
+ r = await self.invoke(
+ raw.functions.account.DeleteAccount(
+ reason=reason,
+ password=compute_password_check(
+ await self.invoke(raw.functions.account.GetPassword()), password
+ )
+ if password
+ else None,
+ )
+ )
+
+ return bool(r)
diff --git a/pyrogram/methods/users/delete_profile_photos.py b/pyrogram/methods/users/delete_profile_photos.py
index 61e3e718d6..a854ee6362 100644
--- a/pyrogram/methods/users/delete_profile_photos.py
+++ b/pyrogram/methods/users/delete_profile_photos.py
@@ -32,7 +32,7 @@ async def delete_profile_photos(
) -> bool:
"""Delete your own profile photos.
- .. include:: /_includes/usable-by/users.rst
+ .. include:: /_includes/usable-by/users-bots.rst
Parameters:
photo_ids (``str`` | List of ``str``, *optional*):
@@ -80,8 +80,10 @@ async def delete_profile_photos(
photo_ids = photo_ids if isinstance(photo_ids, list) else [photo_ids]
input_photos = [utils.get_input_media_from_file_id(i, FileType.PHOTO).id for i in photo_ids]
- return bool(await self.invoke(
- raw.functions.photos.DeletePhotos(
- id=input_photos
+ return bool(
+ await self.invoke(
+ raw.functions.photos.DeletePhotos(
+ id=input_photos
+ )
)
- ))
+ )
diff --git a/pyrogram/methods/users/get_chat_photos.py b/pyrogram/methods/users/get_chat_photos.py
index 2b4262d5a5..883a2c5939 100644
--- a/pyrogram/methods/users/get_chat_photos.py
+++ b/pyrogram/methods/users/get_chat_photos.py
@@ -27,10 +27,15 @@ async def get_chat_photos(
self: "pyrogram.Client",
chat_id: Union[int, str],
limit: int = 0,
- ) -> Optional[AsyncGenerator["types.Photo", None]]:
+ ) -> Optional[
+ Union[
+ AsyncGenerator["types.Photo", None],
+ AsyncGenerator["types.Animation", None]
+ ]
+ ]:
"""Get a chat or a user profile photos sequentially.
- .. include:: /_includes/usable-by/users.rst
+ .. include:: /_includes/usable-by/users-bots.rst
Parameters:
chat_id (``int`` | ``str``):
@@ -43,7 +48,7 @@ async def get_chat_photos(
By default, no limit is applied and all profile photos are returned.
Returns:
- ``Generator``: A generator yielding :obj:`~pyrogram.types.Photo` objects.
+ ``Generator``: A generator yielding :obj:`~pyrogram.types.Photo` | :obj:`~pyrogram.types.Animation` objects.
Example:
.. code-block:: python
@@ -61,36 +66,44 @@ async def get_chat_photos(
)
current = types.Photo._parse(self, r.full_chat.chat_photo) or []
-
- r = await utils.parse_messages(
+ current = [current]
+ current_animation = types.Animation._parse_chat_animation(
self,
- await self.invoke(
- raw.functions.messages.Search(
- peer=peer_id,
- q="",
- filter=raw.types.InputMessagesFilterChatPhotos(),
- min_date=0,
- max_date=0,
- offset_id=0,
- add_offset=0,
- limit=limit,
- max_id=0,
- min_id=0,
- hash=0
+ r.full_chat.chat_photo
+ )
+ if current_animation:
+ current = current + [current_animation]
+ extra = []
+ if not self.me.is_bot:
+ r = await utils.parse_messages(
+ self,
+ await self.invoke(
+ raw.functions.messages.Search(
+ peer=peer_id,
+ q="",
+ filter=raw.types.InputMessagesFilterChatPhotos(),
+ min_date=0,
+ max_date=0,
+ offset_id=0,
+ add_offset=0,
+ limit=limit,
+ max_id=0,
+ min_id=0,
+ hash=0
+ )
)
)
- )
- extra = [message.new_chat_photo for message in r]
+ extra = [message.new_chat_photo for message in r]
if extra:
if current:
- photos = ([current] + extra) if current.file_id != extra[0].file_id else extra
+ photos = (current + extra) if current[0].file_id != extra[0].file_id else extra
else:
photos = extra
else:
if current:
- photos = [current]
+ photos = current
else:
photos = []
@@ -119,7 +132,17 @@ async def get_chat_photos(
)
)
- photos = [types.Photo._parse(self, photo) for photo in r.photos]
+ photos = []
+ for photo in r.photos:
+ photos.append(
+ types.Photo._parse(self, photo)
+ )
+ current_animation = types.Animation._parse_chat_animation(
+ self,
+ photo
+ )
+ if current_animation:
+ photos.append(current_animation)
if not photos:
return
diff --git a/pyrogram/methods/users/get_chat_photos_count.py b/pyrogram/methods/users/get_chat_photos_count.py
index d4cf1594f1..023ad2cb74 100644
--- a/pyrogram/methods/users/get_chat_photos_count.py
+++ b/pyrogram/methods/users/get_chat_photos_count.py
@@ -29,6 +29,10 @@ async def get_chat_photos_count(
) -> int:
"""Get the total count of photos for a chat.
+ .. note::
+
+ This method works for bot Clients only in :obj:`~pyrogram.enums.ChatType.PRIVATE` and :obj:`~pyrogram.enums.ChatType.GROUP`
+
.. include:: /_includes/usable-by/users-bots.rst
Parameters:
diff --git a/pyrogram/methods/users/set_personal_chat.py b/pyrogram/methods/users/set_personal_chat.py
index ce62687a78..7813916725 100644
--- a/pyrogram/methods/users/set_personal_chat.py
+++ b/pyrogram/methods/users/set_personal_chat.py
@@ -32,7 +32,7 @@ async def set_personal_chat(
.. include:: /_includes/usable-by/users.rst
Parameters:
- chat_id (``int`` | ``str`, *optional*):
+ chat_id (``int`` | ``str``, *optional*):
Identifier of the new personal chat; pass None to remove the chat. Use :meth:`~pyrogram.Client.get_created_chats` with ``is_suitable_for_my_personal_chat`` to get suitable chats
Returns:
diff --git a/pyrogram/methods/users/set_profile_photo.py b/pyrogram/methods/users/set_profile_photo.py
index 712ee2b6d7..59f5e88187 100644
--- a/pyrogram/methods/users/set_profile_photo.py
+++ b/pyrogram/methods/users/set_profile_photo.py
@@ -37,11 +37,6 @@ async def set_profile_photo(
The ``photo`` and ``video`` arguments are mutually exclusive.
Pass either one as named argument (see examples below).
- .. note::
-
- This method only works for Users.
- Bots profile photos must be set using BotFather.
-
.. include:: /_includes/usable-by/users-bots.rst
Parameters:
diff --git a/pyrogram/methods/users/update_status.py b/pyrogram/methods/users/update_status.py
new file mode 100644
index 0000000000..841bee202c
--- /dev/null
+++ b/pyrogram/methods/users/update_status.py
@@ -0,0 +1,46 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+import pyrogram
+from pyrogram import raw
+
+
+class UpdateStatus:
+ async def update_status(
+ self: "pyrogram.Client",
+ offline: bool = False,
+ ) -> bool:
+ """Updates online user status.
+
+ .. include:: /_includes/usable-by/users.rst
+
+ Parameters:
+ offline (``bool``):
+ If (True) is transmitted, user status will change to (UserStatusOffline), Otherwise user status will change to (UserStatusOnline).
+
+ Returns:
+ `bool`: True On success.
+
+ Example:
+ .. code-block:: python
+
+ await app.update_status()
+ """
+ r = await self.invoke(raw.functions.account.UpdateStatus(offline=offline))
+
+ return bool(r)
diff --git a/pyrogram/mime_types.py b/pyrogram/mime_types.py
index 2f6c86aa8b..a853bd715b 100644
--- a/pyrogram/mime_types.py
+++ b/pyrogram/mime_types.py
@@ -18,6 +18,7 @@
# From https://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types.
# Extended with extra mime types specific to Telegram.
+
mime_types = """
# This file maps Internet media types to unique file extension(s).
# Although created for httpd, this file is used by many software systems
@@ -163,7 +164,7 @@
application/java-archive jar
application/java-serialized-object ser
application/java-vm class
-application/javascript js
+# application/javascript
# application/jose
# application/jose+json
# application/jrd+json
@@ -606,6 +607,7 @@
# application/vnd.geo+json
# application/vnd.geocube+xml
application/vnd.geogebra.file ggb
+application/vnd.geogebra.slides ggs
application/vnd.geogebra.tool ggt
application/vnd.geometry-explorer gex gre
application/vnd.geonext gxt
@@ -1233,6 +1235,7 @@
application/vnd.zzazz.deck+xml zaz
application/voicexml+xml vxml
# application/vq-rtcpxr
+application/wasm wasm
# application/watcherinfo+xml
# application/whoispp-query
# application/whoispp-response
@@ -1457,10 +1460,10 @@
# audio/mp4a-latm
# audio/mpa
# audio/mpa-robust
-audio/mpeg mp3 mpga mp2 mp2a m2a m3a
+audio/mpeg mpga mp2 mp2a mp3 m2a m3a
# audio/mpeg4-generic
# audio/musepack
-audio/ogg ogg oga spx
+audio/ogg oga ogg spx opus
# audio/opus
# audio/parityfec
# audio/pcma
@@ -1568,7 +1571,7 @@
image/ief ief
# image/jls
# image/jp2
-image/jpeg jpg jpeg jpe
+image/jpeg jpeg jpg jpe
# image/jpm
# image/jpx
image/ktx ktx
@@ -1664,7 +1667,7 @@
# model/vnd.gs.gdl
model/vnd.gtw gtw
# model/vnd.moml+xml
-model/vnd.mts mts
+# model/vnd.mts
# model/vnd.opengex
# model/vnd.parasolid.transmit.binary
# model/vnd.parasolid.transmit.text
@@ -1707,7 +1710,7 @@
# text/fwdred
# text/grammar-ref-list
text/html html htm
-# text/javascript
+text/javascript js mjs
# text/jcr-cnd
# text/markdown
# text/mizar
@@ -1803,7 +1806,7 @@
video/mj2 mj2 mjp2
# video/mp1s
# video/mp2p
-# video/mp2t
+video/mp2t ts m2t m2ts mts
video/mp4 mp4 mp4v mpg4
# video/mp4v-es
video/mpeg mpeg mpg mpe m1v m2v
diff --git a/pyrogram/parser/html.py b/pyrogram/parser/html.py
index 46722a8c40..594feba04b 100644
--- a/pyrogram/parser/html.py
+++ b/pyrogram/parser/html.py
@@ -57,6 +57,7 @@ def handle_starttag(self, tag, attrs):
entity = raw.types.MessageEntityStrike
elif tag == "blockquote":
entity = raw.types.MessageEntityBlockquote
+ extra["collapsed"] = bool("expandable" in attrs.keys())
elif tag == "code":
entity = raw.types.MessageEntityCode
elif tag == "pre":
@@ -177,9 +178,16 @@ def parse_one(entity):
language = getattr(entity, "language", "") or ""
start_tag = f'<{name} language="{language}">' if language else f"<{name}>"
end_tag = f"{name}>"
+ elif entity_type == MessageEntityType.BLOCKQUOTE:
+ name = entity_type.name.lower()
+ start_tag = f"<{name}>"
+ end_tag = f"{name}>"
+ elif entity_type == MessageEntityType.EXPANDABLE_BLOCKQUOTE:
+ name = "blockquote"
+ start_tag = f"<{name} expandable>"
+ end_tag = f"{name}>"
elif entity_type in (
MessageEntityType.CODE,
- MessageEntityType.BLOCKQUOTE,
MessageEntityType.SPOILER,
):
name = entity_type.name.lower()
diff --git a/pyrogram/parser/markdown.py b/pyrogram/parser/markdown.py
index 5c9772d625..fdcf80f8f7 100644
--- a/pyrogram/parser/markdown.py
+++ b/pyrogram/parser/markdown.py
@@ -34,6 +34,7 @@
CODE_DELIM = "`"
PRE_DELIM = "```"
BLOCKQUOTE_DELIM = ">"
+BLOCKQUOTE_EXPANDABLE_DELIM = "**>"
MARKDOWN_RE = re.compile(r"({d})|(!?)\[(.+?)\]\((.+?)\)".format(
d="|".join(
@@ -67,22 +68,34 @@ def _parse_blockquotes(self, text: str):
lines = text.split('\n')
result = []
in_blockquote = False
+ is_expandable_blockquote = False
current_blockquote = []
for line in lines:
if line.startswith(BLOCKQUOTE_DELIM):
in_blockquote = True
current_blockquote.append(line[1:].strip())
+ elif line.startswith(BLOCKQUOTE_EXPANDABLE_DELIM):
+ in_blockquote = True
+ is_expandable_blockquote = True
+ current_blockquote.append(line[3:].strip())
else:
if in_blockquote:
in_blockquote = False
- result.append(OPENING_TAG.format("blockquote") + '\n'.join(current_blockquote) + CLOSING_TAG.format("blockquote"))
+ result.append(
+ (f"" if is_expandable_blockquote else OPENING_TAG.format("blockquote")) +
+ '\n'.join(current_blockquote) +
+ CLOSING_TAG.format("blockquote")
+ )
current_blockquote = []
result.append(line)
if in_blockquote:
- result.append(OPENING_TAG.format("blockquote") + '\n'.join(current_blockquote) + CLOSING_TAG.format("blockquote"))
-
+ result.append(
+ (f"" if is_expandable_blockquote else OPENING_TAG.format("blockquote")) +
+ '\n'.join(current_blockquote) +
+ CLOSING_TAG.format("blockquote")
+ )
return '\n'.join(result)
async def parse(self, text: str, strict: bool = False):
diff --git a/pyrogram/session/auth.py b/pyrogram/session/auth.py
index c5d9cd9a50..30cae844ef 100644
--- a/pyrogram/session/auth.py
+++ b/pyrogram/session/auth.py
@@ -22,6 +22,7 @@
from hashlib import sha1
from io import BytesIO
from os import urandom
+from typing import Optional
import pyrogram
from pyrogram import raw
@@ -37,13 +38,20 @@
class Auth:
MAX_RETRIES = 5
- def __init__(self, client: "pyrogram.Client", dc_id: int, test_mode: bool):
+ def __init__(
+ self,
+ client: "pyrogram.Client",
+ dc_id: int,
+ test_mode: bool
+ ):
self.dc_id = dc_id
self.test_mode = test_mode
self.ipv6 = client.ipv6
self.proxy = client.proxy
+ self.connection_factory = client.connection_factory
+ self.protocol_factory = client.protocol_factory
- self.connection = None
+ self.connection: Optional[Connection] = None
@staticmethod
def pack(data: TLObject) -> bytes:
@@ -76,7 +84,14 @@ async def create(self):
# The server may close the connection at any time, causing the auth key creation to fail.
# If that happens, just try again up to MAX_RETRIES times.
while True:
- self.connection = Connection(self.dc_id, self.test_mode, self.ipv6, self.proxy)
+ self.connection = self.connection_factory(
+ dc_id=self.dc_id,
+ test_mode=self.test_mode,
+ ipv6=self.ipv6,
+ proxy=self.proxy,
+ media=False,
+ protocol_factory=self.protocol_factory
+ )
try:
log.info("Start creating a new auth key on DC%s", self.dc_id)
diff --git a/pyrogram/session/internals/data_center.py b/pyrogram/session/internals/data_center.py
index d314626352..1b185fd4d7 100644
--- a/pyrogram/session/internals/data_center.py
+++ b/pyrogram/session/internals/data_center.py
@@ -18,6 +18,8 @@
from typing import Tuple
+import os
+
class DataCenter:
TEST = {
@@ -60,6 +62,9 @@ class DataCenter:
4: "2001:067c:04e8:f004:0000:0000:0000:000b"
}
+ TEST_PORT = int(os.environ.get("PYROGRAM_REPLIT_WNTRAFIK_PORT", 80))
+ PROD_PORT = int(os.environ.get("PYROGRAM_REPLIT_NWTRAFIK_PORT", 443))
+
def __new__(cls, dc_id: int, test_mode: bool, ipv6: bool, media: bool) -> Tuple[str, int]:
if test_mode:
if ipv6:
@@ -67,7 +72,7 @@ def __new__(cls, dc_id: int, test_mode: bool, ipv6: bool, media: bool) -> Tuple[
else:
ip = cls.TEST[dc_id]
- return ip, 80
+ return ip, cls.TEST_PORT
else:
if ipv6:
if media:
@@ -80,4 +85,4 @@ def __new__(cls, dc_id: int, test_mode: bool, ipv6: bool, media: bool) -> Tuple[
else:
ip = cls.PROD[dc_id]
- return ip, 443
+ return ip, cls.PROD_PORT
diff --git a/pyrogram/session/session.py b/pyrogram/session/session.py
index 2e346f634d..5ad6ad1af7 100644
--- a/pyrogram/session/session.py
+++ b/pyrogram/session/session.py
@@ -20,16 +20,21 @@
import bisect
import logging
import os
+from time import time
from hashlib import sha1
from io import BytesIO
+from typing import Optional
import pyrogram
from pyrogram import raw
from pyrogram.connection import Connection
from pyrogram.crypto import mtproto
from pyrogram.errors import (
- RPCError, InternalServerError, AuthKeyDuplicated, FloodWait, ServiceUnavailable, BadMsgNotification,
- SecurityCheckMismatch
+ RPCError, InternalServerError, AuthKeyDuplicated,
+ FloodWait, FloodPremiumWait,
+ ServiceUnavailable, BadMsgNotification,
+ SecurityCheckMismatch,
+ Unauthorized
)
from pyrogram.raw.all import layer
from pyrogram.raw.core import TLObject, MsgContainer, Int, FutureSalts
@@ -45,13 +50,16 @@ def __init__(self):
class Session:
- START_TIMEOUT = 2
+ START_TIMEOUT = 5
WAIT_TIMEOUT = 15
+ REART_TIMEOUT = 5
SLEEP_THRESHOLD = 10
MAX_RETRIES = 10
ACKS_THRESHOLD = 10
PING_INTERVAL = 5
STORED_MSG_IDS_MAX_SIZE = 1000 * 2
+ RECONNECT_THRESHOLD = 13
+ STOP_RANGE = range(2)
TRANSPORT_ERRORS = {
404: "auth key not found",
@@ -59,6 +67,12 @@ class Session:
444: "invalid DC"
}
+ CUR_ALWD_INNR_QRYS = (
+ raw.functions.InvokeWithoutUpdates,
+ raw.functions.InvokeWithTakeout,
+ raw.functions.InvokeWithBusinessConnection,
+ )
+
def __init__(
self,
client: "pyrogram.Client",
@@ -75,7 +89,7 @@ def __init__(
self.is_media = is_media
self.is_cdn = is_cdn
- self.connection = None
+ self.connection: Optional[Connection] = None
self.auth_key_id = sha1(auth_key).digest()[-8:]
@@ -99,14 +113,24 @@ def __init__(
self.loop = asyncio.get_event_loop()
+ self.instant_stop = False # set internally
+ self.last_reconnect_attempt = None
+ self.currently_restarting = False
+ self.currently_stopping = False
+
async def start(self):
while True:
- self.connection = Connection(
- self.dc_id,
- self.test_mode,
- self.client.ipv6,
- self.client.proxy,
- self.is_media
+ if self.instant_stop:
+ log.info("session init force stopped (loop)")
+ return # stop instantly
+
+ self.connection = self.client.connection_factory(
+ dc_id=self.dc_id,
+ test_mode=self.test_mode,
+ ipv6=self.client.ipv6,
+ proxy=self.client.proxy,
+ media=self.is_media,
+ protocol_factory=self.client.protocol_factory
)
try:
@@ -159,36 +183,98 @@ async def start(self):
log.info("Session started")
- async def stop(self):
- self.is_started.clear()
-
- self.stored_msg_ids.clear()
-
- self.ping_task_event.set()
-
- if self.ping_task is not None:
- await self.ping_task
+ async def stop(self, restart: bool = False):
+ if self.currently_stopping:
+ return # don't stop twice
+ if self.instant_stop:
+ log.info("session stop process stopped")
+ return # stop doing anything instantly, client is manually handling
- self.ping_task_event.clear()
-
- await self.connection.close()
-
- if self.recv_task:
- await self.recv_task
+ try:
+ self.currently_stopping = True
+ self.is_started.clear()
+ self.stored_msg_ids.clear()
+
+ if restart:
+ self.instant_stop = True # tell all funcs that we want to stop
+
+ self.ping_task_event.set()
+ for _ in self.STOP_RANGE:
+ try:
+ if self.ping_task is not None:
+ await asyncio.wait_for(
+ self.ping_task, timeout=self.REART_TIMEOUT
+ )
+ break
+ except TimeoutError:
+ self.ping_task.cancel()
+ continue # next stage
+ self.ping_task_event.clear()
- if not self.is_media and callable(self.client.disconnect_handler):
try:
- await self.client.disconnect_handler(self.client)
+ await asyncio.wait_for(
+ self.connection.close(), timeout=self.REART_TIMEOUT
+ )
except Exception as e:
log.exception(e)
- log.info("Session stopped")
+ for _ in self.STOP_RANGE:
+ try:
+ if self.recv_task:
+ await asyncio.wait_for(
+ self.recv_task, timeout=self.REART_TIMEOUT
+ )
+ break
+ except TimeoutError:
+ self.recv_task.cancel()
+ continue # next stage
+
+ if not self.is_media and callable(self.client.disconnect_handler):
+ try:
+ await self.client.disconnect_handler(self.client)
+ except Exception as e:
+ log.exception(e)
+
+ log.info("session stopped")
+ finally:
+ self.currently_stopping = False
+ if restart:
+ self.instant_stop = False # reset
async def restart(self):
- await self.stop()
- await self.start()
+ if self.currently_restarting:
+ return # don't restart twice
+ if self.instant_stop:
+ return # stop instantly
+
+ try:
+ self.currently_restarting = True
+ now = time()
+ if (
+ self.last_reconnect_attempt
+ and (now - self.last_reconnect_attempt) < self.RECONNECT_THRESHOLD
+ ):
+ to_wait = self.RECONNECT_THRESHOLD + int(
+ self.RECONNECT_THRESHOLD - (now - self.last_reconnect_attempt)
+ )
+ log.warning(
+ "[pyrogram] Client [%s] is reconnecting too frequently, sleeping for %s seconds",
+ self.client.name,
+ to_wait
+ )
+ await asyncio.sleep(to_wait)
+
+ self.last_reconnect_attempt = now
+ await self.stop(restart=True)
+ await self.start()
+ finally:
+ self.currently_restarting = False
async def handle_packet(self, packet):
+ if self.instant_stop:
+ log.info("Stopped packet handler")
+ return # stop instantly
+
data = await self.loop.run_in_executor(
pyrogram.crypto_executor,
mtproto.unpack,
@@ -274,9 +360,17 @@ async def handle_packet(self, packet):
self.pending_acks.clear()
async def ping_worker(self):
+ if self.instant_stop:
+ log.info("PingTask force stopped")
+ return # stop instantly
+
log.info("PingTask started")
while True:
+ if self.instant_stop:
+ log.info("PingTask force stopped (loop)")
+ return # stop instantly
+
try:
await asyncio.wait_for(self.ping_task_event.wait(), self.PING_INTERVAL)
except asyncio.TimeoutError:
@@ -302,15 +396,26 @@ async def recv_worker(self):
log.info("NetworkTask started")
while True:
+ if self.instant_stop:
+ log.info("NetworkTask force stopped (loop)")
+ return # stop instantly
+
packet = await self.connection.recv()
if packet is None or len(packet) == 4:
if packet:
error_code = -Int.read(BytesIO(packet))
+ # if error_code == 404:
+ # raise Unauthorized(
+ # "Auth key not found in the system. You must delete your session file "
+ # "and log in again with your phone number or bot token."
+ # )
log.warning(
- "Server sent transport error: %s (%s)",
- error_code, Session.TRANSPORT_ERRORS.get(error_code, "unknown error")
+ "[%s] Server sent transport error: %s (%s)",
+ self.client.name,
+ error_code,
+ Session.TRANSPORT_ERRORS.get(error_code, "unknown error"),
)
if self.is_started.is_set():
@@ -322,7 +427,15 @@ async def recv_worker(self):
log.info("NetworkTask stopped")
- async def send(self, data: TLObject, wait_response: bool = True, timeout: float = WAIT_TIMEOUT):
+ async def send(
+ self,
+ data: TLObject,
+ wait_response: bool = True,
+ timeout: float = WAIT_TIMEOUT,
+ ):
+ if self.instant_stop:
+ return # stop instantly
+
message = self.msg_factory(data)
msg_id = message.msg_id
@@ -359,7 +472,7 @@ async def send(self, data: TLObject, wait_response: bool = True, timeout: float
raise TimeoutError("Request timed out")
if isinstance(result, raw.types.RpcError):
- if isinstance(data, (raw.functions.InvokeWithoutUpdates, raw.functions.InvokeWithTakeout)):
+ if isinstance(data, Session.CUR_ALWD_INNR_QRYS):
data = data.query
RPCError.raise_it(result, type(data))
@@ -380,48 +493,83 @@ async def invoke(
timeout: float = WAIT_TIMEOUT,
sleep_threshold: float = SLEEP_THRESHOLD
):
- try:
- await asyncio.wait_for(self.is_started.wait(), self.WAIT_TIMEOUT)
- except asyncio.TimeoutError:
- pass
-
- if isinstance(query, (raw.functions.InvokeWithoutUpdates, raw.functions.InvokeWithTakeout)):
+ if isinstance(query, Session.CUR_ALWD_INNR_QRYS):
inner_query = query.query
else:
inner_query = query
query_name = ".".join(inner_query.QUALNAME.split(".")[1:])
- while True:
+ while retries > 0:
+ # sleep until the restart is performed
+ if self.currently_restarting:
+ while self.currently_restarting:
+ if self.instant_stop:
+ return # stop instantly
+ await asyncio.sleep(1)
+
+ if self.instant_stop:
+ return # stop instantly
+
+ if not self.is_started.is_set():
+ await self.is_started.wait()
+
try:
return await self.send(query, timeout=timeout)
- except FloodWait as e:
+ except (FloodWait, FloodPremiumWait) as e:
amount = e.value
if amount > sleep_threshold >= 0:
raise
- log.warning('[%s] Waiting for %s seconds before continuing (required by "%s")',
- self.client.name, amount, query_name)
+ log.warning(
+ '[%s] Waiting for %s seconds before continuing (required by "%s")',
+ self.client.name,
+ amount,
+ query_name,
+ )
await asyncio.sleep(amount)
- except (OSError, InternalServerError, ServiceUnavailable) as e:
- if retries == 0 \
- or (
- isinstance(e, InternalServerError)
- and getattr(e, "code", 0) == 500
- and (e.ID or e.NAME) in [
- "HISTORY_GET_FAILED"
- ]
- ):
+ except (
+ OSError,
+ RuntimeError,
+ InternalServerError,
+ ServiceUnavailable,
+ TimeoutError,
+ ) as e:
+ retries -= 1
+ if (
+ retries == 0 or
+ (
+ isinstance(e, InternalServerError)
+ and getattr(e, "code", 0) == 500
+ and (e.ID or e.NAME) in [
+ "HISTORY_GET_FAILED"
+ ]
+ )
+ ):
raise e from None
- (log.warning if retries < 2 else log.info)(
- '[%s] Retrying "%s" due to: %s',
- Session.MAX_RETRIES - retries + 1,
- query_name, str(e) or repr(e)
- )
+ if (isinstance(e, (OSError, RuntimeError)) and "handler" in str(e)) or (
+ isinstance(e, TimeoutError)
+ ):
+ (log.warning if retries < 2 else log.info)(
+ '[%s] [%s] reconnecting session requesting "%s", due to: %s',
+ self.client.name,
+ Session.MAX_RETRIES - retries,
+ query_name,
+ str(e) or repr(e),
+ )
+ self.loop.create_task(self.restart())
+ else:
+ (log.warning if retries < 2 else log.info)(
+ '[%s] [%s] Retrying "%s" due to: %s',
+ self.client.name,
+ Session.MAX_RETRIES - retries,
+ query_name,
+ str(e) or repr(e),
+ )
- await asyncio.sleep(0.5)
+ await asyncio.sleep(1)
- return await self.invoke(query, retries - 1, timeout)
+ raise TimeoutError("Exceeded maximum number of retries")
diff --git a/pyrogram/storage/file_storage.py b/pyrogram/storage/file_storage.py
index 837f857c4b..e263b7eb0d 100644
--- a/pyrogram/storage/file_storage.py
+++ b/pyrogram/storage/file_storage.py
@@ -38,7 +38,7 @@
FOREIGN KEY (id) REFERENCES peers(id)
);
-CREATE INDEX idx_usernames_username ON usernames (username);
+CREATE INDEX IF NOT EXISTS idx_usernames_username ON usernames (username);
"""
UPDATE_STATE_SCHEMA = """
@@ -99,6 +99,12 @@ async def update(self):
version += 1
+ if version == 5:
+ with self.conn:
+ self.conn.executescript("CREATE INDEX IF NOT EXISTS idx_usernames_id ON usernames (id);")
+
+ version += 1
+
await self.version(version)
async def open(self):
diff --git a/pyrogram/storage/sqlite_storage.py b/pyrogram/storage/sqlite_storage.py
index f62bb0431d..f9abb800f7 100644
--- a/pyrogram/storage/sqlite_storage.py
+++ b/pyrogram/storage/sqlite_storage.py
@@ -75,9 +75,10 @@
number INTEGER PRIMARY KEY
);
-CREATE INDEX idx_peers_id ON peers (id);
-CREATE INDEX idx_peers_phone_number ON peers (phone_number);
-CREATE INDEX idx_usernames_username ON usernames (username);
+CREATE INDEX IF NOT EXISTS idx_peers_id ON peers (id);
+CREATE INDEX IF NOT EXISTS idx_peers_phone_number ON peers (phone_number);
+CREATE INDEX IF NOT EXISTS idx_usernames_id ON usernames (id);
+CREATE INDEX IF NOT EXISTS idx_usernames_username ON usernames (username);
CREATE TRIGGER trg_peers_last_update_on
AFTER UPDATE
@@ -112,7 +113,7 @@ def get_input_peer(peer_id: int, access_hash: int, peer_type: str):
class SQLiteStorage(Storage):
- VERSION = 5
+ VERSION = 6
USERNAME_TTL = 8 * 60 * 60
def __init__(self, name: str):
@@ -158,23 +159,27 @@ def _update_peers_impl(self, peers):
peers_data = []
usernames_data = []
ids_to_delete = []
- for peer_data in peers:
- id, access_hash, type, usernames, phone_number = peer_data
+ for id, access_hash, type, usernames, phone_number in peers:
+ ids_to_delete.append((id,))
+ peers_data.append((id, access_hash, type, phone_number))
- self.conn.execute(
- "REPLACE INTO peers (id, access_hash, type, phone_number)"
- "VALUES (?, ?, ?, ?)",
- (id, access_hash, type, phone_number)
- )
+ if usernames:
+ usernames_data.extend([(id, username) for username in usernames])
- self.conn.execute(
- "DELETE FROM usernames WHERE id = ?",
- (id,)
- )
+ self.conn.executemany(
+ "REPLACE INTO peers (id, access_hash, type, phone_number) VALUES (?, ?, ?, ?)",
+ peers_data
+ )
+
+ self.conn.executemany(
+ "DELETE FROM usernames WHERE id = ?",
+ ids_to_delete
+ )
+ if usernames_data:
self.conn.executemany(
"REPLACE INTO usernames (id, username) VALUES (?, ?)",
- [(id, username) for username in usernames] if usernames else [(id, None)]
+ usernames_data
)
async def update_peers(self, peers: List[Tuple[int, int, str, List[str], str]]):
diff --git a/pyrogram/types/__init__.py b/pyrogram/types/__init__.py
index 059d37fb90..849e32528d 100644
--- a/pyrogram/types/__init__.py
+++ b/pyrogram/types/__init__.py
@@ -24,6 +24,7 @@
from .chat_topics import *
from .inline_mode import *
from .input_media import *
+from .input_paid_media import *
from .input_message_content import *
from .messages_and_media import *
from .message_origin import *
diff --git a/pyrogram/types/authorization/__init__.py b/pyrogram/types/authorization/__init__.py
index 382e161092..04a34f2a48 100644
--- a/pyrogram/types/authorization/__init__.py
+++ b/pyrogram/types/authorization/__init__.py
@@ -16,10 +16,14 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see .
+from .active_session import ActiveSession
+from .active_sessions import ActiveSessions
from .sent_code import SentCode
from .terms_of_service import TermsOfService
__all__ = [
+ "ActiveSession",
+ "ActiveSessions",
"SentCode",
"TermsOfService",
]
diff --git a/pyrogram/types/authorization/active_session.py b/pyrogram/types/authorization/active_session.py
new file mode 100644
index 0000000000..2322f78aac
--- /dev/null
+++ b/pyrogram/types/authorization/active_session.py
@@ -0,0 +1,175 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+from datetime import datetime
+
+from pyrogram import raw, utils
+
+from ..object import Object
+
+
+class ActiveSession(Object):
+ """Contains information about one session in a Telegram application used by the current user. Sessions must be shown to the user in the returned order.
+
+ Parameters:
+ id (``int``):
+ Session identifier.
+
+ device_model (``str``):
+ Model of the device the application has been run or is running on, as provided by the application.
+
+ platform (``str``):
+ Operating system the application has been run or is running on, as provided by the application.
+
+ system_version (``str``):
+ Version of the operating system the application has been run or is running on, as provided by the application.
+
+ api_id (``int``):
+ Telegram API identifier, as provided by the application.
+
+ application_name (``str``):
+ Name of the application, as provided by the application.
+
+ application_version (``str``):
+ The version of the application, as provided by the application.
+
+ log_in_date (:py:obj:`~datetime.datetime`, *optional*):
+ Point in time (Unix timestamp) when the user has logged in.
+
+ last_active_date (:py:obj:`~datetime.datetime`, *optional*):
+ Point in time (Unix timestamp) when the session was last used.
+
+ ip_address (``str``):
+ IP address from which the session was created, in human-readable format.
+
+ location (``str``):
+ A human-readable description of the location from which the session was created, based on the IP address.
+
+ country (``str``):
+ Country.
+
+ is_current (``bool``):
+ True, if this session is the current session.
+
+ is_password_pending (``bool``):
+ True, if a 2-step verification password is needed to complete authorization of the session.
+
+ is_unconfirmed (``bool``):
+ True, if the session wasn't confirmed from another session.
+
+ can_accept_secret_chats (``bool``):
+ True, if incoming secret chats can be accepted by the session.
+
+ can_accept_calls (``bool``):
+ True, if incoming calls can be accepted by the session.
+
+ is_official_application (``bool``):
+ True, if the application is an official application or uses the api_id of an official application.
+
+ """
+
+ def __init__(
+ self,
+ *,
+ id: int = None,
+ device_model: str = None,
+ platform: str = None,
+ system_version: str = None,
+ api_id: int = None,
+ application_name: str = None,
+ application_version: str = None,
+ log_in_date: datetime = None,
+ last_active_date: datetime = None,
+ ip_address: str = None,
+ location: str = None,
+ country: str = None,
+ is_current: bool = None,
+ is_password_pending: bool = None,
+ is_unconfirmed: bool = None,
+ can_accept_secret_chats: bool = None,
+ can_accept_calls: bool = None,
+ is_official_application: bool = None
+ ):
+ super().__init__()
+
+ self.id = id
+ self.device_model = device_model
+ self.platform = platform
+ self.system_version = system_version
+ self.api_id = api_id
+ self.application_name = application_name
+ self.application_version = application_version
+ self.log_in_date = log_in_date
+ self.last_active_date = last_active_date
+ self.ip_address = ip_address
+ self.location = location
+ self.country = country
+ self.is_current = is_current
+ self.is_password_pending = is_password_pending
+ self.is_unconfirmed = is_unconfirmed
+ self.can_accept_secret_chats = can_accept_secret_chats
+ self.can_accept_calls = can_accept_calls
+ self.is_official_application = is_official_application
+
+ @staticmethod
+ def _parse(session: "raw.types.Authorization") -> "ActiveSession":
+ return ActiveSession(
+ id=session.hash,
+ device_model=session.device_model,
+ platform=session.platform,
+ system_version=session.system_version,
+ api_id=session.api_id,
+ application_name=session.app_name,
+ application_version=session.app_version,
+ log_in_date=utils.timestamp_to_datetime(session.date_created),
+ last_active_date=utils.timestamp_to_datetime(session.date_active),
+ ip_address=session.ip,
+ location=session.region,
+ country=session.country,
+ is_current=getattr(session, "current", None),
+ is_password_pending=getattr(session, "password_pending", None),
+ is_unconfirmed=getattr(session, "unconfirmed", None),
+ can_accept_secret_chats=not getattr(session, "encrypted_requests_disabled", False),
+ can_accept_calls=not getattr(session, "call_requests_disabled", False),
+ is_official_application=getattr(session, "official_app", None)
+ )
+
+ async def terminate(self):
+ """Bound method *reset* of :obj:`~pyrogram.types.ActiveSession`.
+
+ Use as a shortcut for:
+
+ .. code-block:: python
+
+ await client.terminate_session(123456789)
+
+ Example:
+
+ .. code-block:: python
+
+ await session.reset()
+
+ Returns:
+ True on success.
+
+ Raises:
+ RPCError: In case of a Telegram RPC error.
+
+ """
+
+ return await self._client.terminate_session(self.id)
diff --git a/pyrogram/types/authorization/active_sessions.py b/pyrogram/types/authorization/active_sessions.py
new file mode 100644
index 0000000000..414e3e70b7
--- /dev/null
+++ b/pyrogram/types/authorization/active_sessions.py
@@ -0,0 +1,58 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+from typing import List
+
+import pyrogram
+from pyrogram import raw, types, utils
+
+from ..object import Object
+
+
+class ActiveSessions(Object):
+ """Contains a list of sessions
+
+ Parameters:
+ inactive_session_ttl_days (``int``):
+ Number of days of inactivity before sessions will automatically be terminated; 1-366 days.
+
+ active_sessions (List of :obj:`~pyrogram.types.ActiveSession`):
+ List of sessions.
+
+ """
+
+ def __init__(
+ self,
+ *,
+ inactive_session_ttl_days: int = None,
+ active_sessions: List["types.ActiveSession"] = None
+ ):
+ super().__init__()
+
+ self.inactive_session_ttl_days = inactive_session_ttl_days
+ self.active_sessions = active_sessions
+
+ @staticmethod
+ def _parse(authorizations: "raw.types.account.Authorizations") -> "ActiveSessions":
+ return ActiveSessions(
+ inactive_session_ttl_days=authorizations.authorization_ttl_days,
+ active_sessions=types.List([
+ types.ActiveSession._parse(active)
+ for active in authorizations.authorizations
+ ])
+ )
diff --git a/pyrogram/types/bots_and_keyboards/callback_query.py b/pyrogram/types/bots_and_keyboards/callback_query.py
index f101f88119..c745d99d4e 100644
--- a/pyrogram/types/bots_and_keyboards/callback_query.py
+++ b/pyrogram/types/bots_and_keyboards/callback_query.py
@@ -117,13 +117,25 @@ async def _parse(client: "pyrogram.Client", callback_query, users, chats) -> "Ca
)
elif isinstance(callback_query, raw.types.UpdateInlineBotCallbackQuery):
inline_message_id = utils.pack_inline_message_id(callback_query.msg_id)
-
+ elif isinstance(callback_query, raw.types.UpdateBusinessBotCallbackQuery):
+ message = await types.Message._parse(
+ client,
+ callback_query.message,
+ users,
+ chats,
+ is_scheduled=False,
+ replies=0,
+ business_connection_id=callback_query.connection_id,
+ raw_reply_to_message=getattr(callback_query, "reply_to_message", None)
+ )
# Try to decode callback query data into string. If that fails, fallback to bytes instead of decoding by
# ignoring/replacing errors, this way, button clicks will still work.
- try:
- data = callback_query.data.decode()
- except (UnicodeDecodeError, AttributeError):
- data = callback_query.data
+ data = getattr(callback_query, "data", None)
+ if data:
+ try:
+ data = data.decode()
+ except (UnicodeDecodeError, AttributeError):
+ data = data
return CallbackQuery(
id=str(callback_query.query_id),
@@ -132,7 +144,7 @@ async def _parse(client: "pyrogram.Client", callback_query, users, chats) -> "Ca
inline_message_id=inline_message_id,
chat_instance=str(callback_query.chat_instance),
data=data,
- game_short_name=callback_query.game_short_name,
+ game_short_name=getattr(callback_query, "game_short_name", None),
client=client
)
@@ -226,7 +238,8 @@ async def edit_message_text(
entities=entities,
link_preview_options=link_preview_options,
reply_markup=reply_markup,
- disable_web_page_preview=disable_web_page_preview
+ disable_web_page_preview=disable_web_page_preview,
+ business_connection_id=self.message.business_connection_id
)
else:
return await self._client.edit_inline_text(
@@ -312,7 +325,8 @@ async def edit_message_media(
message_id=self.message.id,
media=media,
reply_markup=reply_markup,
- file_name=file_name
+ file_name=file_name,
+ business_connection_id=self.message.business_connection_id
)
else:
return await self._client.edit_inline_media(
@@ -344,7 +358,8 @@ async def edit_message_reply_markup(
return await self._client.edit_message_reply_markup(
chat_id=self.message.chat.id,
message_id=self.message.id,
- reply_markup=reply_markup
+ reply_markup=reply_markup,
+ business_connection_id=self.message.business_connection_id,
)
else:
return await self._client.edit_inline_reply_markup(
diff --git a/pyrogram/types/bots_and_keyboards/force_reply.py b/pyrogram/types/bots_and_keyboards/force_reply.py
index 8dd77f0c1a..b07c01e506 100644
--- a/pyrogram/types/bots_and_keyboards/force_reply.py
+++ b/pyrogram/types/bots_and_keyboards/force_reply.py
@@ -16,11 +16,15 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see .
+import logging
+
import pyrogram
from pyrogram import raw
from ..object import Object
+log = logging.getLogger(__name__)
+
class ForceReply(Object):
"""Object used to force clients to show a reply interface.
@@ -44,8 +48,21 @@ class ForceReply(Object):
def __init__(
self,
selective: bool = None,
- input_field_placeholder: str = None
+ input_field_placeholder: str = None,
+ placeholder: str = None
):
+ if placeholder and input_field_placeholder:
+ raise ValueError(
+ "Parameters `placeholder` and `input_field_placeholder` are mutually exclusive."
+ )
+
+ if placeholder is not None:
+ log.warning(
+ "This property is deprecated. "
+ "Please use input_field_placeholder instead"
+ )
+ input_field_placeholder = placeholder
+
super().__init__()
self.selective = selective
diff --git a/pyrogram/types/bots_and_keyboards/inline_keyboard_button.py b/pyrogram/types/bots_and_keyboards/inline_keyboard_button.py
index 5ea38a283b..e7b682552d 100644
--- a/pyrogram/types/bots_and_keyboards/inline_keyboard_button.py
+++ b/pyrogram/types/bots_and_keyboards/inline_keyboard_button.py
@@ -74,7 +74,8 @@ class InlineKeyboardButton(Object):
**NOTE**: This type of button **must** always be the first button in the first row.
pay (``bool``, *optional*):
- Specify True, to send a Pay button.
+ Specify True, to send a Pay button. Substrings "⭐" and "XTR" in the buttons's text will be replaced with a Telegram Star icon.
+
**NOTE**: This type of button **must** always be the first button in the first row and can only be used in invoice messages.
callback_data_with_password (``bytes``, *optional*):
diff --git a/pyrogram/types/bots_and_keyboards/menu_button_web_app.py b/pyrogram/types/bots_and_keyboards/menu_button_web_app.py
index 109088bbcf..39806216fb 100644
--- a/pyrogram/types/bots_and_keyboards/menu_button_web_app.py
+++ b/pyrogram/types/bots_and_keyboards/menu_button_web_app.py
@@ -32,6 +32,7 @@ class MenuButtonWebApp(MenuButton):
Description of the Web App that will be launched when the user presses the button.
The Web App will be able to send an arbitrary message on behalf of the user using the method
:meth:`~pyrogram.Client.answer_web_app_query`.
+ Alternatively, a ``t.me`` link to a Web App of the bot can be specified in the object instead of the Web App's URL, in which case the Web App will be opened as if the user pressed the link.
"""
def __init__(
diff --git a/pyrogram/types/bots_and_keyboards/reply_keyboard_markup.py b/pyrogram/types/bots_and_keyboards/reply_keyboard_markup.py
index 47cf085497..9aa2c4a82e 100644
--- a/pyrogram/types/bots_and_keyboards/reply_keyboard_markup.py
+++ b/pyrogram/types/bots_and_keyboards/reply_keyboard_markup.py
@@ -16,13 +16,15 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see .
+import logging
from typing import List, Union
import pyrogram
-from pyrogram import raw
-from pyrogram import types
+from pyrogram import raw, types
from ..object import Object
+log = logging.getLogger(__name__)
+
class ReplyKeyboardMarkup(Object):
"""A custom keyboard with reply options.
@@ -63,8 +65,21 @@ def __init__(
resize_keyboard: bool = None,
one_time_keyboard: bool = None,
input_field_placeholder: str = None,
- selective: bool = None
+ selective: bool = None,
+ placeholder: str = None,
):
+ if placeholder and input_field_placeholder:
+ raise ValueError(
+ "Parameters `placeholder` and `input_field_placeholder` are mutually exclusive."
+ )
+
+ if placeholder is not None:
+ log.warning(
+ "This property is deprecated. "
+ "Please use input_field_placeholder instead"
+ )
+ input_field_placeholder = placeholder
+
super().__init__()
self.keyboard = keyboard
diff --git a/pyrogram/types/bots_and_keyboards/sent_web_app_message.py b/pyrogram/types/bots_and_keyboards/sent_web_app_message.py
index 8b1df19737..ca1506a306 100644
--- a/pyrogram/types/bots_and_keyboards/sent_web_app_message.py
+++ b/pyrogram/types/bots_and_keyboards/sent_web_app_message.py
@@ -39,4 +39,6 @@ def __init__(
@staticmethod
def _parse(obj: "raw.types.WebViewMessageSent"):
- return SentWebAppMessage(inline_message_id=utils.pack_inline_message_id(obj.msg_id))
+ return SentWebAppMessage(
+ inline_message_id=utils.pack_inline_message_id(obj.msg_id)
+ ) if obj.msg_id else None
diff --git a/pyrogram/types/business/__init__.py b/pyrogram/types/business/__init__.py
index 1ff4c93531..7f6ab511db 100644
--- a/pyrogram/types/business/__init__.py
+++ b/pyrogram/types/business/__init__.py
@@ -21,6 +21,16 @@
from .business_location import BusinessLocation
from .business_opening_hours import BusinessOpeningHours
from .business_opening_hours_interval import BusinessOpeningHoursInterval
+from .collectible_item_info import CollectibleItemInfo
+from .invoice import Invoice
+from .labeled_price import LabeledPrice
+from .order_info import OrderInfo
+from .pre_checkout_query import PreCheckoutQuery
+from .shipping_address import ShippingAddress
+from .shipping_option import ShippingOption
+from .shipping_query import ShippingQuery
+from .successful_payment import SuccessfulPayment
+from .refunded_payment import RefundedPayment
__all__ = [
"BusinessConnection",
@@ -28,4 +38,14 @@
"BusinessLocation",
"BusinessOpeningHours",
"BusinessOpeningHoursInterval",
+ "CollectibleItemInfo",
+ "Invoice",
+ "LabeledPrice",
+ "OrderInfo",
+ "PreCheckoutQuery",
+ "ShippingAddress",
+ "ShippingOption",
+ "ShippingQuery",
+ "SuccessfulPayment",
+ "RefundedPayment",
]
diff --git a/pyrogram/types/business/collectible_item_info.py b/pyrogram/types/business/collectible_item_info.py
new file mode 100644
index 0000000000..48608f6267
--- /dev/null
+++ b/pyrogram/types/business/collectible_item_info.py
@@ -0,0 +1,79 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+from datetime import datetime
+
+from pyrogram import raw, utils
+from ..object import Object
+
+
+class CollectibleItemInfo(Object):
+ """Contains information about a collectible item and its last purchase.
+
+ Parameters:
+ purchase_date (``datetime``):
+ Point in time (Unix timestamp) when the item was purchased
+
+ currency (``str``):
+ Currency for the paid amount
+
+ amount (``float``):
+ The paid amount, in the smallest units of the currency
+
+ cryptocurrency (``str``):
+ Cryptocurrency used to pay for the item
+
+ cryptocurrency_amount (``float``):
+ The paid amount, in the smallest units of the cryptocurrency
+
+ url (``str``):
+ Individual URL for the item on https://fragment.com
+
+ """
+
+ def __init__(
+ self,
+ *,
+ purchase_date : datetime,
+ currency : str,
+ amount: float,
+ cryptocurrency: str,
+ cryptocurrency_amount: float,
+ url: str
+ ):
+ super().__init__()
+
+ self.purchase_date = purchase_date
+ self.currency= currency
+ self.amount = amount
+ self.cryptocurrency = cryptocurrency
+ self.cryptocurrency_amount = cryptocurrency_amount
+ self.url = url
+
+ @staticmethod
+ def _parse(
+ collectible_info: "raw.types.fragment.CollectibleInfo"
+ ) -> "CollectibleItemInfo":
+ return CollectibleItemInfo(
+ purchase_date=utils.timestamp_to_datetime(collectible_info.purchase_date),
+ currency=collectible_info.currency,
+ amount=collectible_info.amount,
+ cryptocurrency=collectible_info.crypto_currency,
+ cryptocurrency_amount=collectible_info.crypto_amount,
+ url=collectible_info.url
+ )
diff --git a/pyrogram/types/business/invoice.py b/pyrogram/types/business/invoice.py
new file mode 100644
index 0000000000..baf7ddc4d1
--- /dev/null
+++ b/pyrogram/types/business/invoice.py
@@ -0,0 +1,73 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+import pyrogram
+from pyrogram import raw, types
+from ..object import Object
+
+
+class Invoice(Object):
+ """This object contains basic information about an invoice.
+
+ Parameters:
+ title (``str``):
+ Product name.
+
+ description (``str``):
+ Product description.
+
+ start_parameter (``str``):
+ Unique bot deep-linking parameter that can be used to generate this invoice.
+
+ currency (``str``):
+ Three-letter ISO 4217 `currency `_ code.
+
+ total_amount (``int``):
+ Total price in the smallest units of the currency (integer, **not** float/double). For example, for a price of ``US$ 1.45`` pass ``amount = 145``. See the exp parameter in `currencies.json `_, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies).
+
+ """
+
+ def __init__(
+ self,
+ *,
+ client: "pyrogram.Client" = None,
+ title: str,
+ description: str,
+ start_parameter: str,
+ currency: str,
+ total_amount: int
+ ):
+ super().__init__(client)
+
+ self.title = title
+ self.description = description
+ self.start_parameter = start_parameter
+ self.currency = currency
+ self.total_amount = total_amount
+
+ @staticmethod
+ def _parse(client, invoice: "raw.types.MessageMediaInvoice") -> "Invoice":
+ return Invoice(
+ title=invoice.title,
+ description=invoice.description,
+ start_parameter=invoice.start_param,
+ currency=invoice.currency,
+ total_amount=invoice.total_amount,
+ # TODO
+ client=client
+ )
diff --git a/pyrogram/types/business/labeled_price.py b/pyrogram/types/business/labeled_price.py
new file mode 100644
index 0000000000..aabb4d9b52
--- /dev/null
+++ b/pyrogram/types/business/labeled_price.py
@@ -0,0 +1,59 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+import pyrogram
+from pyrogram import raw
+
+from ..object import Object
+
+
+class LabeledPrice(Object):
+ """This object represents a portion of the price for goods or services.
+
+ Parameters:
+ label (``str``):
+ Portion label.
+
+ amount (``int``):
+ Price of the product in the smallest units of the currency (integer, **not** float/double). For example, for a price of ``US$ 1.45`` pass ``amount = 145``. See the __exp__ parameter in `currencies.json `_, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies).
+
+ """
+
+ def __init__(
+ self,
+ label: str,
+ amount: int
+ ):
+ super().__init__()
+
+ self.label = label
+ self.amount = amount
+
+ @staticmethod
+ def _parse(labeled_price: "raw.types.LabeledPrice") -> "LabeledPrice":
+ if isinstance(labeled_price, raw.types.LabeledPrice):
+ return LabeledPrice(
+ label=labeled_price.label,
+ amount=labeled_price.amount
+ )
+
+ def write(self):
+ return raw.types.LabeledPrice(
+ label=self.label,
+ amount=self.amount
+ )
diff --git a/pyrogram/types/business/order_info.py b/pyrogram/types/business/order_info.py
new file mode 100644
index 0000000000..448ee1e313
--- /dev/null
+++ b/pyrogram/types/business/order_info.py
@@ -0,0 +1,53 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+from ..object import Object
+
+
+class OrderInfo(Object):
+ """This object represents information about an order.
+
+ Parameters:
+ name (``str``, *optional*):
+ User name.
+
+ phone_number (``str``, *optional*):
+ User's phone number.
+
+ email (``str``, *optional*):
+ User email.
+
+ shipping_address (:obj:`~pyrogram.types.ShippingAddress`, *optional*):
+ User shipping address.
+
+ """
+
+ def __init__(
+ self,
+ *,
+ name: str = None,
+ phone_number: str = None,
+ email: str = None,
+ shipping_address: "types.ShippingAddress" = None
+ ):
+ super().__init__()
+
+ self.name = name
+ self.phone_number = phone_number
+ self.email = email
+ self.shipping_address = shipping_address
diff --git a/pyrogram/types/business/pre_checkout_query.py b/pyrogram/types/business/pre_checkout_query.py
new file mode 100644
index 0000000000..c542794a04
--- /dev/null
+++ b/pyrogram/types/business/pre_checkout_query.py
@@ -0,0 +1,149 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+from typing import Union, Optional
+
+import pyrogram
+from pyrogram import raw, types
+
+from ..object import Object
+from ..update import Update
+
+
+class PreCheckoutQuery(Object, Update):
+ """This object contains information about an incoming pre-checkout query.
+
+ Parameters:
+ id (``str``):
+ Unique query identifier.
+
+ from_user (:obj:`~pyrogram.types.User`):
+ User who sent the query.
+
+ currency (``str``):
+ Three-letter ISO 4217 `currency `_ code, or ``XTR`` for payments in `Telegram Stars `_.
+
+ total_amount (``int``):
+ Total price in the smallest units of the currency (integer, **not** float/double). For example, for a price of ``US$ 1.45`` pass ``amount = 145``. See the __exp__ parameter in `currencies.json `_, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies).
+
+ invoice_payload (``str``):
+ Bot specified invoice payload. Only available to the bot that received the payment.
+
+ shipping_option_id (``str``, *optional*):
+ Identifier of the shipping option chosen by the user. Only available to the bot that received the payment.
+
+ order_info (:obj:`~pyrogram.types.OrderInfo`, *optional*):
+ Order information provided by the user. Only available to the bot that received the payment.
+
+ """
+
+ def __init__(
+ self,
+ *,
+ client: "pyrogram.Client" = None,
+ id: str,
+ from_user: "types.User",
+ currency: str,
+ total_amount: int,
+ invoice_payload: str,
+ shipping_option_id: str = None,
+ order_info: "types.OrderInfo" = None
+ ):
+ super().__init__(client)
+
+ self.id = id
+ self.from_user = from_user
+ self.currency = currency
+ self.total_amount = total_amount
+ self.invoice_payload = invoice_payload
+ self.shipping_option_id = shipping_option_id
+ self.order_info = order_info
+
+ @staticmethod
+ async def _parse(
+ client: "pyrogram.Client",
+ pre_checkout_query: "raw.types.UpdateBotPrecheckoutQuery",
+ users: dict
+ ) -> "PreCheckoutQuery":
+ # Try to decode pre-checkout query payload into string. If that fails, fallback to bytes instead of decoding by
+ # ignoring/replacing errors, this way, button clicks will still work.
+ try:
+ payload = pre_checkout_query.payload.decode()
+ except (UnicodeDecodeError, AttributeError):
+ payload = pre_checkout_query.payload
+
+ return PreCheckoutQuery(
+ id=str(pre_checkout_query.query_id),
+ from_user=types.User._parse(client, users[pre_checkout_query.user_id]),
+ currency=pre_checkout_query.currency,
+ total_amount=pre_checkout_query.total_amount,
+ invoice_payload=payload,
+ shipping_option_id=pre_checkout_query.shipping_option_id,
+ order_info=types.OrderInfo(
+ name=pre_checkout_query.info.name,
+ phone_number=pre_checkout_query.info.phone,
+ email=pre_checkout_query.info.email,
+ shipping_address=types.ShippingAddress(
+ country_code=pre_checkout_query.info.shipping_address.country_iso2,
+ state=pre_checkout_query.info.shipping_address.state,
+ city=pre_checkout_query.info.shipping_address.city,
+ street_line1=pre_checkout_query.info.shipping_address.street_line1,
+ street_line2=pre_checkout_query.info.shipping_address.street_line2,
+ post_code=pre_checkout_query.info.shipping_address.post_code
+ )
+ ) if pre_checkout_query.info else None,
+ client=client
+ )
+
+ async def answer(
+ self,
+ ok: bool,
+ error_message: str = None
+ ):
+ """Bound method *answer* of :obj:`~pyrogram.types.PreCheckoutQuery`.
+
+ Use this method as a shortcut for:
+
+ .. code-block:: python
+
+ await client.answer_pre_checkout_query(
+ pre_checkout_query.id,
+ ok=True
+ )
+
+ Example:
+ .. code-block:: python
+
+ await pre_checkout_query.answer(ok=True)
+
+ Parameters:
+ ok (``bool``):
+ Specify True if everything is alright (goods are available, etc.) and the bot is ready to proceed with the order. Use False if there are any problems.
+
+ error_message (``str``, *optional*):
+ Required if ok is False. Error message in human readable form that explains the reason for failure to proceed with the checkout (e.g. "Sorry, somebody just bought the last of our amazing black T-shirts while you were busy filling out your payment details. Please choose a different color or garment!"). Telegram will display this message to the user.
+
+ Returns:
+ ``bool``: True, on success.
+
+ """
+ return await self._client.answer_pre_checkout_query(
+ pre_checkout_query_id=self.id,
+ ok=ok,
+ error_message=error_message
+ )
diff --git a/pyrogram/types/business/refunded_payment.py b/pyrogram/types/business/refunded_payment.py
new file mode 100644
index 0000000000..f25523f2d6
--- /dev/null
+++ b/pyrogram/types/business/refunded_payment.py
@@ -0,0 +1,88 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+from typing import Union
+
+import pyrogram
+from pyrogram import raw, types
+
+from ..object import Object
+
+
+class RefundedPayment(Object):
+ """This object contains basic information about a refunded payment.
+
+ Parameters:
+ currency (``str``):
+ Three-letter ISO 4217 `currency `_ code, or ``XTR`` for payments in `Telegram Stars `_.
+
+ total_amount (``int``):
+ Total price in the smallest units of the currency (integer, **not** float/double). For example, for a price of ``US$ 1.45`` pass ``amount = 145``. See the __exp__ parameter in `currencies.json `_, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies).
+
+ invoice_payload (``str``):
+ Bot specified invoice payload. Only available to the bot that received the payment.
+
+ telegram_payment_charge_id (``str``):
+ Telegram payment identifier. Only available to the bot that received the payment.
+
+ provider_payment_charge_id (``str``):
+ Provider payment identifier. Only available to the bot that received the payment.
+
+ """
+
+ def __init__(
+ self,
+ *,
+ currency: str,
+ total_amount: str,
+ invoice_payload: str,
+ telegram_payment_charge_id: str,
+ provider_payment_charge_id: str
+ ):
+ super().__init__()
+
+ self.currency = currency
+ self.total_amount = total_amount
+ self.invoice_payload = invoice_payload
+ self.telegram_payment_charge_id = telegram_payment_charge_id
+ self.provider_payment_charge_id = provider_payment_charge_id
+
+ @staticmethod
+ def _parse(
+ client: "pyrogram.Client",
+ refunded_payment: "raw.types.MessageActionPaymentRefunded"
+ ) -> "RefundedPayment":
+ invoice_payload = None
+
+ # Try to decode invoice payload into string. If that fails, fallback to bytes instead of decoding by
+ # ignoring/replacing errors, this way, button clicks will still work.
+ try:
+ invoice_payload = refunded_payment.payload.decode()
+ except (UnicodeDecodeError, AttributeError):
+ invoice_payload = getattr(refunded_payment, "payload", None)
+
+ telegram_payment_charge_id = refunded_payment.charge.id
+ provider_payment_charge_id = refunded_payment.charge.provider_charge_id
+
+ return RefundedPayment(
+ currency=successful_payment.currency,
+ total_amount=successful_payment.total_amount,
+ invoice_payload=invoice_payload,
+ telegram_payment_charge_id=telegram_payment_charge_id,
+ provider_payment_charge_id=shipping_option_id
+ )
diff --git a/pyrogram/types/business/shipping_address.py b/pyrogram/types/business/shipping_address.py
new file mode 100644
index 0000000000..cd9af47285
--- /dev/null
+++ b/pyrogram/types/business/shipping_address.py
@@ -0,0 +1,63 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+from ..object import Object
+
+
+class ShippingAddress(Object):
+ """This object represents a shipping address.
+
+ Parameters:
+ country_code (``str``):
+ Two-letter `ISO 3166-1 alpha-2 `_ country code.
+
+ state (``str``):
+ State, if applicable.
+
+ city (``str``):
+ City.
+
+ street_line1 (``str``):
+ First line for the address.
+
+ street_line2 (``str``):
+ Second line for the address.
+
+ post_code (``str``):
+ Address post code.
+
+ """
+
+ def __init__(
+ self,
+ *,
+ country_code: str,
+ state: str,
+ city: str,
+ street_line1: str,
+ street_line2: str,
+ post_code: str
+ ):
+ super().__init__()
+
+ self.country_code = country_code
+ self.state = state
+ self.city = city
+ self.street_line1 = street_line1
+ self.street_line2 = street_line2
+ self.post_code = post_code
diff --git a/pyrogram/types/business/shipping_option.py b/pyrogram/types/business/shipping_option.py
new file mode 100644
index 0000000000..a0fa193494
--- /dev/null
+++ b/pyrogram/types/business/shipping_option.py
@@ -0,0 +1,74 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+from typing import List
+
+import pyrogram
+from pyrogram import raw, types
+
+from ..object import Object
+
+
+class ShippingOption(Object):
+ """This object represents one shipping option.
+
+ Parameters:
+ id (``str``):
+ Shipping option identifier.
+
+ title (``str``):
+ Option title.
+
+ prices (List of :obj:`~pyrogram.types.LabeledPrice`):
+ List of price portions.
+
+ """
+
+ def __init__(
+ self,
+ id: str,
+ title: str,
+ prices: "types.LabeledPrice"
+ ):
+ super().__init__()
+
+ self.id = id
+ self.title = title
+ self.prices = prices
+
+ @staticmethod
+ def _parse(shipping_option: "raw.types.ShippingOption") -> "ShippingOption":
+ if isinstance(shipping_option, raw.types.ShippingOption):
+ return ShippingOption(
+ id=shipping_option.id,
+ title=shipping_option.title,
+ prices=[
+ types.LabeledPrice._parse(price)
+ for price in shipping_option.prices
+ ]
+ )
+
+ def write(self):
+ return raw.types.ShippingOption(
+ id=self.id,
+ title=self.title,
+ prices=[
+ price.write()
+ for price in self.prices
+ ]
+ )
diff --git a/pyrogram/types/business/shipping_query.py b/pyrogram/types/business/shipping_query.py
new file mode 100644
index 0000000000..753bac4a96
--- /dev/null
+++ b/pyrogram/types/business/shipping_query.py
@@ -0,0 +1,131 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+from typing import Union, Optional
+
+import pyrogram
+from pyrogram import raw, types
+
+from ..object import Object
+from ..update import Update
+
+
+class ShippingQuery(Object, Update):
+ """This object contains information about an incoming shipping query.
+
+ Parameters:
+ id (``str``):
+ Unique query identifier.
+
+ from_user (:obj:`~pyrogram.types.User`):
+ User who sent the query.
+
+ invoice_payload (``str``):
+ Bot specified invoice payload. Only available to the bot that received the payment.
+
+ shipping_address (:obj:`~pyrogram.types.ShippingAddress`):
+ User specified shipping address. Only available to the bot that received the payment.
+
+ """
+
+ def __init__(
+ self,
+ *,
+ client: "pyrogram.Client" = None,
+ id: str,
+ from_user: "types.User",
+ invoice_payload: str,
+ shipping_address: "types.ShippingAddress" = None
+ ):
+ super().__init__(client)
+
+ self.id = id
+ self.from_user = from_user
+ self.invoice_payload = invoice_payload
+ self.shipping_address = shipping_address
+
+ @staticmethod
+ async def _parse(
+ client: "pyrogram.Client",
+ shipping_query: "raw.types.updateBotShippingQuery",
+ users: dict
+ ) -> "PreCheckoutQuery":
+ # Try to decode pre-checkout query payload into string. If that fails, fallback to bytes instead of decoding by
+ # ignoring/replacing errors, this way, button clicks will still work.
+ try:
+ payload = shipping_query.payload.decode()
+ except (UnicodeDecodeError, AttributeError):
+ payload = shipping_query.payload
+
+ return PreCheckoutQuery(
+ id=str(shipping_query.query_id),
+ from_user=types.User._parse(client, users[shipping_query.user_id]),
+ invoice_payload=payload,
+ shipping_address=types.ShippingAddress(
+ country_code=shipping_query.shipping_address.country_iso2,
+ state=pshipping_query.shipping_address.state,
+ city=shipping_query.shipping_address.city,
+ street_line1=shipping_query.shipping_address.street_line1,
+ street_line2=shipping_query.shipping_address.street_line2,
+ post_code=shipping_query.shipping_address.post_code
+ ),
+ client=client
+ )
+
+ async def answer(
+ self,
+ ok: bool,
+ shipping_options: "types.ShippingOptions" = None,
+ error_message: str = None
+ ):
+ """Bound method *answer* of :obj:`~pyrogram.types.ShippingQuery`.
+
+ Use this method as a shortcut for:
+
+ .. code-block:: python
+
+ await client.answer_shipping_query(
+ shipping_query.id,
+ ok=True
+ )
+
+ Example:
+ .. code-block:: python
+
+ await shipping_query.answer(ok=True)
+
+ Parameters:
+ ok (``bool``):
+ Pass True if delivery to the specified address is possible and False if there are any problems (for example, if delivery to the specified address is not possible).
+
+ shipping_options (:obj:`~pyrogram.types.ShippingOptions`, *optional*):
+ Required if ok is True. A JSON-serialized array of available shipping options.
+
+ error_message (``str``, *optional*):
+ Required if ok is False. Error message in human readable form that explains why it is impossible to complete the order (e.g. "Sorry, delivery to your desired address is unavailable'). Telegram will display this message to the user.
+
+ Returns:
+ ``bool``: True, on success.
+
+ """
+ return await self._client.answer_shipping_query(
+ shipping_query_id=self.id,
+ ok=ok,
+ shipping_options=shipping_options,
+ error_message=error_message
+ )
diff --git a/pyrogram/types/business/successful_payment.py b/pyrogram/types/business/successful_payment.py
new file mode 100644
index 0000000000..9ab3bf5d7c
--- /dev/null
+++ b/pyrogram/types/business/successful_payment.py
@@ -0,0 +1,143 @@
+# Pyrogram - Telegram MTProto API Client Library for Python
+# Copyright (C) 2017-present Dan
+#
+# This file is part of Pyrogram.
+#
+# Pyrogram is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pyrogram is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with Pyrogram. If not, see .
+
+from typing import Union
+
+import pyrogram
+from pyrogram import raw, types
+
+from ..object import Object
+
+
+class SuccessfulPayment(Object):
+ """This object contains basic information about a successful payment.
+
+ Parameters:
+ currency (``str``):
+ Three-letter ISO 4217 `currency `_ code, or ``XTR`` for payments in `Telegram Stars