From 27de5ba3b54c523536676b62882bc5618eb805e5 Mon Sep 17 00:00:00 2001 From: Pyrogram-Mod - Dev <> Date: Fri, 30 Jan 2026 00:08:05 +0100 Subject: [PATCH 01/10] Update API scheme to layer 224. --- compiler/api/source/main_api.tl | 72 ++++++++++++++++++++------------- 1 file changed, 43 insertions(+), 29 deletions(-) diff --git a/compiler/api/source/main_api.tl b/compiler/api/source/main_api.tl index a2cfdc6f..d0edf811 100644 --- a/compiler/api/source/main_api.tl +++ b/compiler/api/source/main_api.tl @@ -117,7 +117,7 @@ chatEmpty#29562865 id:long = Chat; chat#41cbf256 flags:# creator:flags.0?true kicked:flags.1?true left:flags.2?true deactivated:flags.5?true call_active:flags.23?true call_not_empty:flags.24?true noforwards:flags.25?true id:long title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel admin_rights:flags.14?ChatAdminRights default_banned_rights:flags.18?ChatBannedRights = Chat; chatForbidden#6592a1a7 id:long title:string = Chat; channel#1c32b11c flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true signatures:flags.11?true min:flags.12?true scam:flags.19?true has_link:flags.20?true has_geo:flags.21?true slowmode_enabled:flags.22?true call_active:flags.23?true call_not_empty:flags.24?true fake:flags.25?true gigagroup:flags.26?true noforwards:flags.27?true join_to_send:flags.28?true join_request:flags.29?true forum:flags.30?true flags2:# stories_hidden:flags2.1?true stories_hidden_min:flags2.2?true stories_unavailable:flags2.3?true signature_profiles:flags2.12?true autotranslation:flags2.15?true broadcast_messages_allowed:flags2.16?true monoforum:flags2.17?true forum_tabs:flags2.19?true id:long access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int restriction_reason:flags.9?Vector admin_rights:flags.14?ChatAdminRights banned_rights:flags.15?ChatBannedRights default_banned_rights:flags.18?ChatBannedRights participants_count:flags.17?int usernames:flags2.0?Vector stories_max_id:flags2.4?RecentStory color:flags2.7?PeerColor profile_color:flags2.8?PeerColor emoji_status:flags2.9?EmojiStatus level:flags2.10?int subscription_until_date:flags2.11?int bot_verification_icon:flags2.13?long send_paid_messages_stars:flags2.14?long linked_monoforum_id:flags2.18?long = Chat; -channelForbidden#17d493d5 flags:# broadcast:flags.5?true megagroup:flags.8?true id:long access_hash:long title:string until_date:flags.16?int = Chat; +channelForbidden#17d493d5 flags:# broadcast:flags.5?true megagroup:flags.8?true monoforum:flags.10?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#e4e0b29d 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 paid_reactions_available:flags2.16?true stargifts_available:flags2.19?true paid_messages_available:flags2.20?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 bot_verification:flags2.17?BotVerification stargifts_count:flags2.18?int send_paid_messages_stars:flags2.21?long main_tab:flags2.22?ProfileTab = ChatFull; @@ -203,7 +203,7 @@ messageActionPaymentRefunded#41b3e202 flags:# peer:Peer currency:string total_am 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; messageActionPrizeStars#b00c47a2 flags:# unclaimed:flags.0?true stars:long transaction_id:string boost_peer:Peer giveaway_msg_id:int = MessageAction; messageActionStarGift#ea2c31d3 flags:# name_hidden:flags.0?true saved:flags.2?true converted:flags.3?true upgraded:flags.5?true transferred:flags.6?true can_upgrade:flags.10?true refunded:flags.9?true prepaid_upgrade:flags.13?true upgrade_separate:flags.16?true auction_acquired:flags.17?true gift:StarGift message:flags.1?TextWithEntities convert_stars:flags.4?long upgrade_msg_id:flags.5?int upgrade_stars:flags.8?long from_id:flags.11?Peer peer:flags.12?Peer saved_id:flags.12?long prepaid_upgrade_hash:flags.14?string gift_msg_id:flags.15?int to_id:flags.18?Peer gift_num:flags.19?int = MessageAction; -messageActionStarGiftUnique#95728543 flags:# upgrade:flags.0?true transferred:flags.1?true saved:flags.2?true refunded:flags.5?true prepaid_upgrade:flags.11?true assigned:flags.13?true from_offer:flags.14?true gift:StarGift can_export_at:flags.3?int transfer_stars:flags.4?long from_id:flags.6?Peer peer:flags.7?Peer saved_id:flags.7?long resale_amount:flags.8?StarsAmount can_transfer_at:flags.9?int can_resell_at:flags.10?int drop_original_details_stars:flags.12?long = MessageAction; +messageActionStarGiftUnique#e6c31522 flags:# upgrade:flags.0?true transferred:flags.1?true saved:flags.2?true refunded:flags.5?true prepaid_upgrade:flags.11?true assigned:flags.13?true from_offer:flags.14?true gift:StarGift can_export_at:flags.3?int transfer_stars:flags.4?long from_id:flags.6?Peer peer:flags.7?Peer saved_id:flags.7?long resale_amount:flags.8?StarsAmount can_transfer_at:flags.9?int can_resell_at:flags.10?int drop_original_details_stars:flags.12?long can_craft_at:flags.15?int = MessageAction; messageActionPaidMessagesRefunded#ac1f1fcd count:int stars:long = MessageAction; messageActionPaidMessagesPrice#84b88578 flags:# broadcast_messages_allowed:flags.0?true stars:long = MessageAction; messageActionConferenceCall#2ffe2f7a flags:# missed:flags.0?true active:flags.1?true video:flags.4?true call_id:long duration:flags.2?int other_participants:flags.3?Vector = MessageAction; @@ -222,6 +222,8 @@ messageActionUserUpdatedPhoto#55555551 new_user_photo:UserProfilePhoto = Message messageActionTTLChange#55555552 ttl:int = MessageAction; messageActionCreatedBroadcastList#55555557 = MessageAction; messageActionLoginUnknownLocation#555555f5 title:string address:string = MessageAction; +messageActionNewCreatorPending#b07ed085 new_creator_id:long = MessageAction; +messageActionChangeCreator#e188503b new_creator_id:long = 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; @@ -478,6 +480,7 @@ updateStarGiftAuctionUserState#dc58f31e gift_id:long user_state:StarGiftAuctionU updateEmojiGameInfo#fb9c547a info:messages.EmojiGameInfo = Update; updateTranscribeAudio#88617090 flags:# final:flags.0?true transcription_id:long text:string = Update; updateBotSubscriptionExpire#a8ae3eb1 user_id:long payload:string until_date:int qts:int = Update; +updateStarGiftCraftFail#ac072444 = Update; updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State; @@ -702,24 +705,24 @@ botCommand#c27ac8c7 command:string description:string = BotCommand; botInfo#4d8a0299 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 privacy_policy_url:flags.7?string app_settings:flags.8?BotAppSettings verifier_settings:flags.9?BotVerifierSettings = BotInfo; -keyboardButton#a2fa4880 text:string = KeyboardButton; -keyboardButtonUrl#258aff05 text:string url:string = KeyboardButton; -keyboardButtonCallback#35bbdb6b flags:# requires_password:flags.0?true text:string data:bytes = KeyboardButton; -keyboardButtonRequestPhone#b16a6c29 text:string = KeyboardButton; -keyboardButtonRequestGeoLocation#fc796b3f text:string = KeyboardButton; -keyboardButtonSwitchInline#93b9fbb5 flags:# same_peer:flags.0?true text:string query:string peer_types:flags.1?Vector = KeyboardButton; -keyboardButtonGame#50f41ccf text:string = KeyboardButton; -keyboardButtonBuy#afd93fbb text:string = KeyboardButton; -keyboardButtonUrlAuth#10b78d29 flags:# text:string fwd_text:flags.0?string url:string button_id:int = KeyboardButton; -inputKeyboardButtonUrlAuth#d02e7fd4 flags:# request_write_access:flags.0?true text:string fwd_text:flags.1?string url:string bot:InputUser = KeyboardButton; -keyboardButtonRequestPoll#bbc7515d flags:# quiz:flags.0?Bool text:string = KeyboardButton; -inputKeyboardButtonUserProfile#e988037b text:string user_id:InputUser = KeyboardButton; -keyboardButtonUserProfile#308660c1 text:string user_id:long = KeyboardButton; -keyboardButtonWebView#13767230 text:string url:string = KeyboardButton; -keyboardButtonSimpleWebView#a0c0505c text:string url:string = KeyboardButton; -keyboardButtonRequestPeer#53d7bfd8 text:string button_id:int peer_type:RequestPeerType max_quantity:int = KeyboardButton; +keyboardButton#7d170cff flags:# style:flags.10?KeyboardButtonStyle text:string = KeyboardButton; +keyboardButtonUrl#d80c25ec flags:# style:flags.10?KeyboardButtonStyle text:string url:string = KeyboardButton; +keyboardButtonCallback#e62bc960 flags:# requires_password:flags.0?true style:flags.10?KeyboardButtonStyle text:string data:bytes = KeyboardButton; +keyboardButtonRequestPhone#417efd8f flags:# style:flags.10?KeyboardButtonStyle text:string = KeyboardButton; +keyboardButtonRequestGeoLocation#aa40f94d flags:# style:flags.10?KeyboardButtonStyle text:string = KeyboardButton; +keyboardButtonSwitchInline#991399fc flags:# style:flags.10?KeyboardButtonStyle same_peer:flags.0?true text:string query:string peer_types:flags.1?Vector = KeyboardButton; +keyboardButtonGame#89c590f9 flags:# style:flags.10?KeyboardButtonStyle text:string = KeyboardButton; +keyboardButtonBuy#3fa53905 flags:# style:flags.10?KeyboardButtonStyle text:string = KeyboardButton; +keyboardButtonUrlAuth#f51006f9 flags:# style:flags.10?KeyboardButtonStyle text:string fwd_text:flags.0?string url:string button_id:int = KeyboardButton; +inputKeyboardButtonUrlAuth#68013e72 flags:# style:flags.10?KeyboardButtonStyle request_write_access:flags.0?true text:string fwd_text:flags.1?string url:string bot:InputUser = KeyboardButton; +keyboardButtonRequestPoll#7a11d782 flags:# style:flags.10?KeyboardButtonStyle quiz:flags.0?Bool text:string = KeyboardButton; +inputKeyboardButtonUserProfile#7d5e07c7 flags:# style:flags.10?KeyboardButtonStyle text:string input_user:InputUser = KeyboardButton; +keyboardButtonUserProfile#c0fd5d09 flags:# style:flags.10?KeyboardButtonStyle text:string user_id:long = KeyboardButton; +keyboardButtonWebView#e846b1a0 flags:# style:flags.10?KeyboardButtonStyle text:string url:string = KeyboardButton; +keyboardButtonSimpleWebView#e15c4370 flags:# style:flags.10?KeyboardButtonStyle text:string url:string = KeyboardButton; +keyboardButtonRequestPeer#5b0f15f5 flags:# style:flags.10?KeyboardButtonStyle text:string button_id:int peer_type:RequestPeerType max_quantity:int = KeyboardButton; inputKeyboardButtonRequestPeer#c9662d05 flags:# name_requested:flags.0?true username_requested:flags.1?true photo_requested:flags.2?true text:string button_id:int peer_type:RequestPeerType max_quantity:int = KeyboardButton; -keyboardButtonCopy#75d2698e text:string copy_text:string = KeyboardButton; +keyboardButtonCopy#bcc4af10 flags:# style:flags.10?KeyboardButtonStyle text:string copy_text:string = KeyboardButton; keyboardButtonRow#77608b83 buttons:Vector = KeyboardButtonRow; @@ -1287,8 +1290,8 @@ folderPeer#e9baa668 peer:Peer folder_id:int = FolderPeer; messages.searchCounter#e844ebff flags:# inexact:flags.1?true filter:MessagesFilter count:int = messages.SearchCounter; -urlAuthResultRequest#92d33a0e flags:# request_write_access:flags.0?true bot:User domain:string = UrlAuthResult; -urlAuthResultAccepted#8f8c0e4e url:string = UrlAuthResult; +urlAuthResultRequest#32fabf1a flags:# request_write_access:flags.0?true request_phone_number:flags.1?true bot:User domain:string browser:flags.2?string platform:flags.2?string ip:flags.2?string region:flags.2?string = UrlAuthResult; +urlAuthResultAccepted#623a8fa0 flags:# url:flags.0?string = UrlAuthResult; urlAuthResultDefault#a9d6db1f = UrlAuthResult; channelLocationEmpty#bfb5ad8b = ChannelLocation; @@ -1945,7 +1948,7 @@ starsGiveawayOption#94ce852a flags:# extended:flags.0?true default:flags.1?true starsGiveawayWinnersOption#54236209 flags:# default:flags.0?true users:int per_user_stars:long = StarsGiveawayWinnersOption; starGift#313a9547 flags:# limited:flags.0?true sold_out:flags.1?true birthday:flags.2?true can_upgrade:flags.3?true require_premium:flags.7?true limited_per_user:flags.8?true peer_color_available:flags.10?true auction:flags.11?true id:long sticker:Document stars:long availability_remains:flags.0?int availability_total:flags.0?int availability_resale:flags.4?long convert_stars:long first_sale_date:flags.1?int last_sale_date:flags.1?int upgrade_stars:flags.3?long resell_min_stars:flags.4?long title:flags.5?string released_by:flags.6?Peer per_user_total:flags.8?int per_user_remains:flags.8?int locked_until_date:flags.9?int auction_slug:flags.11?string gifts_per_round:flags.11?int auction_start_date:flags.11?int upgrade_variants:flags.12?int background:flags.13?StarGiftBackground = StarGift; -starGiftUnique#569d64c9 flags:# require_premium:flags.6?true resale_ton_only:flags.7?true theme_available:flags.9?true id:long gift_id:long title:string slug:string num:int owner_id:flags.0?Peer owner_name:flags.1?string owner_address:flags.2?string attributes:Vector availability_issued:int availability_total:int gift_address:flags.3?string resell_amount:flags.4?Vector released_by:flags.5?Peer value_amount:flags.8?long value_currency:flags.8?string value_usd_amount:flags.8?long theme_peer:flags.10?Peer peer_color:flags.11?PeerColor host_id:flags.12?Peer offer_min_stars:flags.13?int = StarGift; +starGiftUnique#85f0a9cd flags:# require_premium:flags.6?true resale_ton_only:flags.7?true theme_available:flags.9?true burned:flags.14?true crafted:flags.15?true id:long gift_id:long title:string slug:string num:int owner_id:flags.0?Peer owner_name:flags.1?string owner_address:flags.2?string attributes:Vector availability_issued:int availability_total:int gift_address:flags.3?string resell_amount:flags.4?Vector released_by:flags.5?Peer value_amount:flags.8?long value_currency:flags.8?string value_usd_amount:flags.8?long theme_peer:flags.10?Peer peer_color:flags.11?PeerColor host_id:flags.12?Peer offer_min_stars:flags.13?int craft_chance_permille:flags.16?int = StarGift; payments.starGiftsNotModified#a388a368 = payments.StarGifts; payments.starGifts#2ed82995 hash:int gifts:Vector chats:Vector users:Vector = payments.StarGifts; @@ -1980,9 +1983,9 @@ botVerifierSettings#b0cd6617 flags:# can_modify_custom_description:flags.1?true botVerification#f93cd45c bot_id:long icon:long description:string = BotVerification; -starGiftAttributeModel#39d99013 name:string document:Document rarity_permille:int = StarGiftAttribute; -starGiftAttributePattern#13acff19 name:string document:Document rarity_permille:int = StarGiftAttribute; -starGiftAttributeBackdrop#d93d859c name:string backdrop_id:int center_color:int edge_color:int pattern_color:int text_color:int rarity_permille:int = StarGiftAttribute; +starGiftAttributeModel#565251e2 flags:# crafted:flags.0?true name:string document:Document rarity:StarGiftAttributeRarity = StarGiftAttribute; +starGiftAttributePattern#4e7085ea name:string document:Document rarity:StarGiftAttributeRarity = StarGiftAttribute; +starGiftAttributeBackdrop#9f2504e4 name:string backdrop_id:int center_color:int edge_color:int pattern_color:int text_color:int rarity:StarGiftAttributeRarity = StarGiftAttribute; starGiftAttributeOriginalDetails#e0bff26c flags:# sender_id:flags.0?Peer recipient_id:Peer date:int message:flags.1?TextWithEntities = StarGiftAttribute; payments.starGiftUpgradePreview#3de1dfed sample_attributes:Vector prices:Vector next_prices:Vector = payments.StarGiftUpgradePreview; @@ -1994,7 +1997,7 @@ payments.uniqueStarGift#416c56e8 gift:StarGift chats:Vector users:Vector users:Vector = messages.WebPagePreview; -savedStarGift#ead6805e flags:# name_hidden:flags.0?true unsaved:flags.5?true refunded:flags.9?true can_upgrade:flags.10?true pinned_to_top:flags.12?true upgrade_separate:flags.17?true from_id:flags.1?Peer date:int gift:StarGift message:flags.2?TextWithEntities msg_id:flags.3?int saved_id:flags.11?long convert_stars:flags.4?long upgrade_stars:flags.6?long can_export_at:flags.7?int transfer_stars:flags.8?long can_transfer_at:flags.13?int can_resell_at:flags.14?int collection_id:flags.15?Vector prepaid_upgrade_hash:flags.16?string drop_original_details_stars:flags.18?long gift_num:flags.19?int = SavedStarGift; +savedStarGift#41df43fc flags:# name_hidden:flags.0?true unsaved:flags.5?true refunded:flags.9?true can_upgrade:flags.10?true pinned_to_top:flags.12?true upgrade_separate:flags.17?true from_id:flags.1?Peer date:int gift:StarGift message:flags.2?TextWithEntities msg_id:flags.3?int saved_id:flags.11?long convert_stars:flags.4?long upgrade_stars:flags.6?long can_export_at:flags.7?int transfer_stars:flags.8?long can_transfer_at:flags.13?int can_resell_at:flags.14?int collection_id:flags.15?Vector prepaid_upgrade_hash:flags.16?string drop_original_details_stars:flags.18?long gift_num:flags.19?int can_craft_at:flags.20?int = SavedStarGift; payments.savedStarGifts#95f389b1 flags:# count:int chat_notifications_enabled:flags.1?Bool gifts:Vector next_offset:flags.0?string chats:Vector users:Vector = payments.SavedStarGifts; @@ -2139,8 +2142,16 @@ messages.emojiGameOutcome#da2ad647 seed:bytes stake_ton_amount:long ton_amount:l messages.emojiGameUnavailable#59e65335 = messages.EmojiGameInfo; messages.emojiGameDiceInfo#44e56023 flags:# game_hash:string prev_stake:long current_streak:int params:Vector plays_left:flags.0?int = messages.EmojiGameInfo; +starGiftAttributeRarity#36437737 permille:int = StarGiftAttributeRarity; +starGiftAttributeRarityEpic#78fbf3a8 = StarGiftAttributeRarity; +starGiftAttributeRarityLegendary#cef7e7a8 = StarGiftAttributeRarity; +starGiftAttributeRarityUncommon#dbce6389 = StarGiftAttributeRarity; +starGiftAttributeRarityRare#f08d516b = StarGiftAttributeRarity; + messages.messageEmpty#3f4e0648 = messages.MessageEmpty; +keyboardButtonStyle#4fdd3430 flags:# bg_primary:flags.0?true bg_danger:flags.1?true bg_success:flags.2?true icon:flags.3?long = KeyboardButtonStyle; + premiumGiftOption#79c059f7 flags:# months:int currency:string amount:long bot_url:flags.1?string store_product:flags.0?string = PremiumGiftOption; messages.webViewResult#aadf159b result:BotInlineResult users:Vector = messages.WebViewResult; @@ -2456,7 +2467,7 @@ messages.getEmojiKeywordsLanguages#4e9963b2 lang_codes:Vector = Vector = Vector; messages.requestUrlAuth#198fb446 flags:# peer:flags.1?InputPeer msg_id:flags.1?int button_id:flags.1?int url:flags.2?string = UrlAuthResult; -messages.acceptUrlAuth#b12c7125 flags:# write_allowed:flags.0?true peer:flags.1?InputPeer msg_id:flags.1?int button_id:flags.1?int url:flags.2?string = UrlAuthResult; +messages.acceptUrlAuth#b12c7125 flags:# write_allowed:flags.0?true share_phone_number:flags.3?true peer:flags.1?InputPeer msg_id:flags.1?int button_id:flags.1?int url:flags.2?string = UrlAuthResult; messages.hidePeerSettingsBar#4facb138 peer:InputPeer = Bool; messages.getScheduledHistory#f516760b peer:InputPeer hash:long = messages.Messages; messages.getScheduledMessages#bdbb0464 peer:InputPeer id:Vector = messages.Messages; @@ -2591,7 +2602,9 @@ messages.getWebViewResult#22b6c214 peer:InputPeer bot:InputUser query_id:long = messages.forwardMessage#33963bf9 peer:InputPeer id:int random_id:long = Updates; messages.getStatsURL#812c2ae6 flags:# dark:flags.0?true peer:InputPeer params:string = StatsURL; messages.getAllChats#875f74be except_ids:Vector = messages.Chats; +messages.craftStarGift#b0f9684f stargift:Vector = Updates; messages.setWebViewResult#e41cd11d query_id:long = Bool; +messages.getCraftStarGifts#fd05dd00 gift_id:long offset:string limit:int = payments.SavedStarGifts; 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; @@ -2697,6 +2710,7 @@ channels.toggleAutotranslation#167fc0a1 channel:InputChannel enabled:Bool = Upda channels.getMessageAuthor#ece2a0e6 channel:InputChannel id:int = User; channels.checkSearchPostsFlood#22567115 flags:# query:flags.0?string = SearchPostsFlood; channels.setMainProfileTab#3583fcb1 channel:InputChannel tab:ProfileTab = Bool; +channels.getFutureCreatorAfterLeave#a00918af channel:InputChannel = User; bots.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON; bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool; @@ -2777,7 +2791,7 @@ payments.getStarGiftWithdrawalUrl#d06e93a8 stargift:InputSavedStarGift password: payments.toggleChatStarGiftNotifications#60eaefa1 flags:# enabled:flags.0?true peer:InputPeer = Bool; payments.toggleStarGiftsPinnedToTop#1513e7b0 peer:InputPeer stargift:Vector = Bool; payments.canPurchaseStore#4fdc5ea7 purpose:InputStorePaymentPurpose = Bool; -payments.getResaleStarGifts#7a5fa236 flags:# sort_by_price:flags.1?true sort_by_num:flags.2?true attributes_hash:flags.0?long gift_id:long attributes:flags.3?Vector offset:string limit:int = payments.ResaleStarGifts; +payments.getResaleStarGifts#7a5fa236 flags:# sort_by_price:flags.1?true sort_by_num:flags.2?true for_craft:flags.4?true attributes_hash:flags.0?long gift_id:long attributes:flags.3?Vector offset:string limit:int = payments.ResaleStarGifts; payments.updateStarGiftPrice#edbe6ccb stargift:InputSavedStarGift resell_amount:StarsAmount = Updates; payments.createStarGiftCollection#1f4a0e87 peer:InputPeer title:string stargift:Vector = StarGiftCollection; payments.updateStarGiftCollection#4fddbee7 flags:# peer:InputPeer collection_id:int title:flags.0?string delete_stargift:flags.1?Vector add_stargift:flags.2?Vector order:flags.3?Vector = StarGiftCollection; @@ -2929,4 +2943,4 @@ smsjobs.finishJob#4f1ebf24 flags:# job_id:string error:flags.0?string = Bool; fragment.getCollectibleInfo#be1e85ba collectible:InputCollectible = fragment.CollectibleInfo; -// LAYER 221 +// LAYER 224 \ No newline at end of file From 4fb8de8b6d27b576ec498f4f9eaf4143c2ec3dcd Mon Sep 17 00:00:00 2001 From: Pyrogram-Mod - Dev <> Date: Fri, 30 Jan 2026 00:58:39 +0100 Subject: [PATCH 02/10] Implement types for Layer 224 features. Add KeyboardButtonStyle for button styling (bg colors, icons). Add StarGift and StarGiftUnique types for star gift handling. Add new MessageServiceType entries: STAR_GIFT, STAR_GIFT_UNIQUE, NEW_CREATOR_PENDING, CHANGE_CREATOR. Update KeyboardButton to support style parameter. Update Message parser to handle new service message actions. --- pyrogram/enums/message_service_type.py | 12 + pyrogram/types/bots_and_keyboards/__init__.py | 2 + .../bots_and_keyboards/keyboard_button.py | 32 ++- .../keyboard_button_style.py | 73 ++++++ pyrogram/types/messages_and_media/__init__.py | 3 +- pyrogram/types/messages_and_media/message.py | 8 + .../types/messages_and_media/star_gift.py | 248 ++++++++++++++++++ 7 files changed, 368 insertions(+), 10 deletions(-) create mode 100644 pyrogram/types/bots_and_keyboards/keyboard_button_style.py create mode 100644 pyrogram/types/messages_and_media/star_gift.py diff --git a/pyrogram/enums/message_service_type.py b/pyrogram/enums/message_service_type.py index 8a60e29e..f5d7792b 100644 --- a/pyrogram/enums/message_service_type.py +++ b/pyrogram/enums/message_service_type.py @@ -71,3 +71,15 @@ class MessageServiceType(AutoName): WEB_APP_DATA = auto() "Web app data" + + STAR_GIFT = auto() + "Star gift received" + + STAR_GIFT_UNIQUE = auto() + "Unique/collectible star gift" + + NEW_CREATOR_PENDING = auto() + "New creator transfer pending" + + CHANGE_CREATOR = auto() + "Creator changed" diff --git a/pyrogram/types/bots_and_keyboards/__init__.py b/pyrogram/types/bots_and_keyboards/__init__.py index 6f05a3b4..a9a22758 100644 --- a/pyrogram/types/bots_and_keyboards/__init__.py +++ b/pyrogram/types/bots_and_keyboards/__init__.py @@ -32,6 +32,7 @@ from .inline_keyboard_button import InlineKeyboardButton from .inline_keyboard_markup import InlineKeyboardMarkup from .keyboard_button import KeyboardButton +from .keyboard_button_style import KeyboardButtonStyle from .login_url import LoginUrl from .menu_button import MenuButton from .menu_button_commands import MenuButtonCommands @@ -50,6 +51,7 @@ "InlineKeyboardButton", "InlineKeyboardMarkup", "KeyboardButton", + "KeyboardButtonStyle", "ReplyKeyboardMarkup", "ReplyKeyboardRemove", "LoginUrl", diff --git a/pyrogram/types/bots_and_keyboards/keyboard_button.py b/pyrogram/types/bots_and_keyboards/keyboard_button.py index 5c8d4b73..e89c6b2f 100644 --- a/pyrogram/types/bots_and_keyboards/keyboard_button.py +++ b/pyrogram/types/bots_and_keyboards/keyboard_button.py @@ -40,9 +40,12 @@ class KeyboardButton(Object): web_app (:obj:`~pyrogram.types.WebAppInfo`, *optional*): If specified, the described `Web App `_ will be launched when the - button is pressed. The Web App will be able to send a β€œweb_app_data” service message. Available in private + button is pressed. The Web App will be able to send a "web_app_data" service message. Available in private chats only. + style (:obj:`~pyrogram.types.KeyboardButtonStyle`, *optional*): + Style configuration for the button (background color, icon). + """ def __init__( @@ -50,7 +53,8 @@ def __init__( text: str, request_contact: bool = None, request_location: bool = None, - web_app: "types.WebAppInfo" = None + web_app: "types.WebAppInfo" = None, + style: "types.KeyboardButtonStyle" = None ): super().__init__() @@ -58,22 +62,29 @@ def __init__( self.request_contact = request_contact self.request_location = request_location self.web_app = web_app + self.style = style @staticmethod def read(b): + style = types.KeyboardButtonStyle.read(getattr(b, "style", None)) + if isinstance(b, raw.types.KeyboardButton): + if style: + return KeyboardButton(text=b.text, style=style) return b.text if isinstance(b, raw.types.KeyboardButtonRequestPhone): return KeyboardButton( text=b.text, - request_contact=True + request_contact=True, + style=style ) if isinstance(b, raw.types.KeyboardButtonRequestGeoLocation): return KeyboardButton( text=b.text, - request_location=True + request_location=True, + style=style ) if isinstance(b, raw.types.KeyboardButtonSimpleWebView): @@ -81,15 +92,18 @@ def read(b): text=b.text, web_app=types.WebAppInfo( url=b.url - ) + ), + style=style ) def write(self): + style = self.style.write() if self.style else None + if self.request_contact: - return raw.types.KeyboardButtonRequestPhone(text=self.text) + return raw.types.KeyboardButtonRequestPhone(text=self.text, style=style) elif self.request_location: - return raw.types.KeyboardButtonRequestGeoLocation(text=self.text) + return raw.types.KeyboardButtonRequestGeoLocation(text=self.text, style=style) elif self.web_app: - return raw.types.KeyboardButtonSimpleWebView(text=self.text, url=self.web_app.url) + return raw.types.KeyboardButtonSimpleWebView(text=self.text, url=self.web_app.url, style=style) else: - return raw.types.KeyboardButton(text=self.text) + return raw.types.KeyboardButton(text=self.text, style=style) diff --git a/pyrogram/types/bots_and_keyboards/keyboard_button_style.py b/pyrogram/types/bots_and_keyboards/keyboard_button_style.py new file mode 100644 index 00000000..06066d0e --- /dev/null +++ b/pyrogram/types/bots_and_keyboards/keyboard_button_style.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 . + +from pyrogram import raw +from ..object import Object + + +class KeyboardButtonStyle(Object): + """Style configuration for keyboard buttons. + + Parameters: + bg_primary (``bool``, *optional*): + Use primary background color for the button. + + bg_danger (``bool``, *optional*): + Use danger/red background color for the button. + + bg_success (``bool``, *optional*): + Use success/green background color for the button. + + icon (``int``, *optional*): + Custom emoji ID to use as button icon. + """ + + def __init__( + self, + *, + bg_primary: bool = None, + bg_danger: bool = None, + bg_success: bool = None, + icon: int = None + ): + super().__init__() + + self.bg_primary = bg_primary + self.bg_danger = bg_danger + self.bg_success = bg_success + self.icon = icon + + @staticmethod + def read(style: "raw.types.KeyboardButtonStyle") -> "KeyboardButtonStyle": + if style is None: + return None + + return KeyboardButtonStyle( + bg_primary=style.bg_primary or None, + bg_danger=style.bg_danger or None, + bg_success=style.bg_success or None, + icon=style.icon + ) + + def write(self) -> "raw.types.KeyboardButtonStyle": + return raw.types.KeyboardButtonStyle( + bg_primary=self.bg_primary, + bg_danger=self.bg_danger, + bg_success=self.bg_success, + icon=self.icon + ) diff --git a/pyrogram/types/messages_and_media/__init__.py b/pyrogram/types/messages_and_media/__init__.py index f4da93aa..f03feb7e 100644 --- a/pyrogram/types/messages_and_media/__init__.py +++ b/pyrogram/types/messages_and_media/__init__.py @@ -42,9 +42,10 @@ from .web_app_data import WebAppData from .web_page import WebPage from .message_reactions import MessageReactions +from .star_gift import StarGift, StarGiftUnique __all__ = [ "Animation", "Audio", "Contact", "Document", "Game", "Location", "Message", "MessageEntity", "Photo", "Thumbnail", "StrippedThumbnail", "Poll", "PollOption", "Sticker", "Venue", "Video", "VideoNote", "Voice", "WebPage", "Dice", - "Reaction", "WebAppData", "MessageReactions", "Story", "Giveaway", "AlternativeVideo" + "Reaction", "WebAppData", "MessageReactions", "Story", "Giveaway", "AlternativeVideo", "StarGift", "StarGiftUnique" ] diff --git a/pyrogram/types/messages_and_media/message.py b/pyrogram/types/messages_and_media/message.py index e1e4f30a..017c3d35 100644 --- a/pyrogram/types/messages_and_media/message.py +++ b/pyrogram/types/messages_and_media/message.py @@ -573,6 +573,14 @@ async def _parse( elif isinstance(action, raw.types.MessageActionWebViewDataSentMe): web_app_data = types.WebAppData._parse(action) service_type = enums.MessageServiceType.WEB_APP_DATA + elif isinstance(action, raw.types.MessageActionStarGift): + service_type = enums.MessageServiceType.STAR_GIFT + elif isinstance(action, raw.types.MessageActionStarGiftUnique): + service_type = enums.MessageServiceType.STAR_GIFT_UNIQUE + elif isinstance(action, raw.types.MessageActionNewCreatorPending): + service_type = enums.MessageServiceType.NEW_CREATOR_PENDING + elif isinstance(action, raw.types.MessageActionChangeCreator): + service_type = enums.MessageServiceType.CHANGE_CREATOR from_user = types.User._parse(client, users.get(user_id, None)) sender_chat = types.Chat._parse(client, message, users, chats, is_chat=False) if not from_user else None diff --git a/pyrogram/types/messages_and_media/star_gift.py b/pyrogram/types/messages_and_media/star_gift.py new file mode 100644 index 00000000..ec7d140e --- /dev/null +++ b/pyrogram/types/messages_and_media/star_gift.py @@ -0,0 +1,248 @@ +# 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 Optional + +import pyrogram +from pyrogram import raw, types, utils +from ..object import Object + + +class StarGift(Object): + """A star gift. + + Parameters: + id (``int``): + Unique identifier of the gift. + + sticker (:obj:`~pyrogram.types.Sticker`): + Sticker that represents the gift. + + stars (``int``): + Price of the gift in Telegram Stars. + + convert_stars (``int``): + Amount of Stars the receiver can convert this gift to. + + limited (``bool``, *optional*): + Whether this is a limited-supply gift. + + sold_out (``bool``, *optional*): + Whether this gift sold out and cannot be bought anymore. + + birthday (``bool``, *optional*): + Whether this is a birthday-themed gift. + + can_upgrade (``bool``, *optional*): + Whether this gift can be upgraded to a collectible. + + require_premium (``bool``, *optional*): + Whether this gift can only be bought by Premium users. + + availability_remains (``int``, *optional*): + For limited-supply gifts: remaining number of gifts available. + + availability_total (``int``, *optional*): + For limited-supply gifts: total number of gifts in initial supply. + + first_sale_date (:py:obj:`~datetime.datetime`, *optional*): + For sold out gifts: when the gift was first bought. + + last_sale_date (:py:obj:`~datetime.datetime`, *optional*): + For sold out gifts: when the gift was last bought. + + upgrade_stars (``int``, *optional*): + Stars needed to upgrade this gift to a collectible. + + title (``str``, *optional*): + Title of the gift. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + id: int, + sticker: "types.Sticker" = None, + stars: int, + convert_stars: int, + limited: bool = None, + sold_out: bool = None, + birthday: bool = None, + can_upgrade: bool = None, + require_premium: bool = None, + availability_remains: int = None, + availability_total: int = None, + first_sale_date: datetime = None, + last_sale_date: datetime = None, + upgrade_stars: int = None, + title: str = None + ): + super().__init__(client) + + self.id = id + self.sticker = sticker + self.stars = stars + self.convert_stars = convert_stars + self.limited = limited + self.sold_out = sold_out + self.birthday = birthday + self.can_upgrade = can_upgrade + self.require_premium = require_premium + self.availability_remains = availability_remains + self.availability_total = availability_total + self.first_sale_date = first_sale_date + self.last_sale_date = last_sale_date + self.upgrade_stars = upgrade_stars + self.title = title + + @staticmethod + async def _parse( + client: "pyrogram.Client", + star_gift: "raw.types.StarGift" + ) -> "StarGift": + return StarGift( + client=client, + id=star_gift.id, + sticker=await types.Sticker._parse(client, star_gift.sticker, {}) if star_gift.sticker else None, + stars=star_gift.stars, + convert_stars=star_gift.convert_stars, + limited=star_gift.limited or None, + sold_out=star_gift.sold_out or None, + birthday=star_gift.birthday or None, + can_upgrade=star_gift.can_upgrade or None, + require_premium=star_gift.require_premium or None, + availability_remains=star_gift.availability_remains, + availability_total=star_gift.availability_total, + first_sale_date=utils.timestamp_to_datetime(star_gift.first_sale_date), + last_sale_date=utils.timestamp_to_datetime(star_gift.last_sale_date), + upgrade_stars=star_gift.upgrade_stars, + title=star_gift.title + ) + + +class StarGiftUnique(Object): + """A unique/collectible star gift. + + Parameters: + id (``int``): + Unique identifier of the collectible gift. + + gift_id (``int``): + Unique ID of the base gift type. + + title (``str``): + Collectible title. + + slug (``str``): + Slug for creating deep links. + + num (``int``): + Unique number among all collectibles of the same type. + + availability_issued (``int``): + Number of gifts of this type upgraded to collectible. + + availability_total (``int``): + Total number that can be upgraded. + + require_premium (``bool``, *optional*): + Whether only Premium users can buy this. + + burned (``bool``, *optional*): + Whether this gift has been burned. + + crafted (``bool``, *optional*): + Whether this gift was crafted. + + owner_id (``int``, *optional*): + User ID of the owner. + + owner_name (``str``, *optional*): + Name of the owner if ID is not available. + + owner_address (``str``, *optional*): + TON blockchain address of the owner. + + gift_address (``str``, *optional*): + TON blockchain address of the NFT. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + id: int, + gift_id: int, + title: str, + slug: str, + num: int, + availability_issued: int, + availability_total: int, + require_premium: bool = None, + burned: bool = None, + crafted: bool = None, + owner_id: int = None, + owner_name: str = None, + owner_address: str = None, + gift_address: str = None + ): + super().__init__(client) + + self.id = id + self.gift_id = gift_id + self.title = title + self.slug = slug + self.num = num + self.availability_issued = availability_issued + self.availability_total = availability_total + self.require_premium = require_premium + self.burned = burned + self.crafted = crafted + self.owner_id = owner_id + self.owner_name = owner_name + self.owner_address = owner_address + self.gift_address = gift_address + + @staticmethod + def _parse( + client: "pyrogram.Client", + star_gift: "raw.types.StarGiftUnique" + ) -> "StarGiftUnique": + owner_id = None + if star_gift.owner_id: + owner_id = utils.get_raw_peer_id(star_gift.owner_id) + + return StarGiftUnique( + client=client, + id=star_gift.id, + gift_id=star_gift.gift_id, + title=star_gift.title, + slug=star_gift.slug, + num=star_gift.num, + availability_issued=star_gift.availability_issued, + availability_total=star_gift.availability_total, + require_premium=star_gift.require_premium or None, + burned=star_gift.burned or None, + crafted=star_gift.crafted or None, + owner_id=owner_id, + owner_name=star_gift.owner_name, + owner_address=star_gift.owner_address, + gift_address=star_gift.gift_address + ) From ece8c951105452406f7e043b6f2dd79e604a739c Mon Sep 17 00:00:00 2001 From: Pyrogram-Mod - Dev <> Date: Fri, 30 Jan 2026 01:01:54 +0100 Subject: [PATCH 03/10] Add examples for Layer 224 features. Add styled_keyboards example demonstrating KeyboardButtonStyle usage with bg_primary, bg_danger, bg_success colors and custom icons. Add star_gifts example showing how to handle StarGift service messages and filter for STAR_GIFT, STAR_GIFT_UNIQUE, NEW_CREATOR_PENDING, and CHANGE_CREATOR message types. --- docs/source/start/examples/index.rst | 6 + docs/source/start/examples/star_gifts.rst | 119 ++++++++++++++++++ .../start/examples/styled_keyboards.rst | 113 +++++++++++++++++ 3 files changed, 238 insertions(+) create mode 100644 docs/source/start/examples/star_gifts.rst create mode 100644 docs/source/start/examples/styled_keyboards.rst diff --git a/docs/source/start/examples/index.rst b/docs/source/start/examples/index.rst index 70b188bb..305979e2 100644 --- a/docs/source/start/examples/index.rst +++ b/docs/source/start/examples/index.rst @@ -26,6 +26,9 @@ to give you a basic idea. :doc:`inline_queries`, "Handle inline queries (as bot) and answer with results" :doc:`use_inline_bots`, "Query an inline bot (as user) and send a result to a chat" :doc:`bot_keyboards`, "Send normal and inline keyboards using regular bots" + :doc:`styled_keyboards`, "Send keyboards with custom styles (colors, icons)" + :doc:`star_gifts`, "Handle Star Gift service messages" + :doc:`streaming_text`, "Stream typing updates for conversational bots" :doc:`raw_updates`, "Handle raw updates (old, should be avoided)" For more advanced examples, see https://github.com/ColinShark/Pyrogram-Snippets. @@ -43,4 +46,7 @@ For more advanced examples, see https://github.com/ColinShark/Pyrogram-Snippets. inline_queries use_inline_bots bot_keyboards + styled_keyboards + star_gifts + streaming_text raw_updates diff --git a/docs/source/start/examples/star_gifts.rst b/docs/source/start/examples/star_gifts.rst new file mode 100644 index 00000000..f10a84aa --- /dev/null +++ b/docs/source/start/examples/star_gifts.rst @@ -0,0 +1,119 @@ +star_gifts +========== + +This example shows how to handle Star Gift service messages. + +Star Gifts are a Telegram feature that allows users to send gifts to each other. When a gift is received, +a service message is sent with the gift details. This example demonstrates how to detect and handle these messages. + +.. code-block:: python + + from pyrogram import Client, filters + from pyrogram.enums import MessageServiceType + + app = Client("my_account") + + + @app.on_message(filters.service) + async def handle_service_messages(client, message): + """Handle service messages including star gifts.""" + + # Check if this is a star gift message + if message.service == MessageServiceType.STAR_GIFT: + print(f"Received a Star Gift!") + print(f"Message ID: {message.id}") + print(f"From: {message.from_user.first_name if message.from_user else 'Anonymous'}") + print(f"Date: {message.date}") + + # You can access the raw action for more details + # The raw action contains: gift, name_hidden, saved, converted, etc. + + elif message.service == MessageServiceType.STAR_GIFT_UNIQUE: + print(f"Received a Unique/Collectible Star Gift!") + print(f"Message ID: {message.id}") + print(f"This is a special collectible gift that can be traded as NFT") + + elif message.service == MessageServiceType.NEW_CREATOR_PENDING: + print(f"Creator transfer is pending!") + print(f"A new creator has been nominated for this channel/group") + + elif message.service == MessageServiceType.CHANGE_CREATOR: + print(f"Creator has been changed!") + print(f"The ownership of this channel/group has been transferred") + + + @app.on_message(filters.service) + async def log_all_service_types(client, message): + """Log all service message types for debugging.""" + if message.service: + print(f"Service message type: {message.service}") + + + app.run() + + +Using StarGift and StarGiftUnique Types +--------------------------------------- + +The high-level ``StarGift`` and ``StarGiftUnique`` types provide easy access to gift properties: + +.. code-block:: python + + from pyrogram.types import StarGift, StarGiftUnique + + # StarGift properties: + # - id: Unique identifier of the gift + # - sticker: Sticker representing the gift + # - stars: Price in Telegram Stars + # - convert_stars: Stars the receiver can convert to + # - limited: Whether it's a limited-supply gift + # - sold_out: Whether the gift sold out + # - birthday: Whether it's a birthday-themed gift + # - can_upgrade: Whether it can be upgraded to collectible + # - availability_remains: Remaining gifts (for limited) + # - availability_total: Total supply (for limited) + + # StarGiftUnique properties (collectible gifts): + # - id: Unique identifier + # - gift_id: Base gift type ID + # - title: Collectible title + # - slug: For creating deep links + # - num: Unique number among collectibles of same type + # - burned: Whether the gift was burned + # - crafted: Whether it was crafted + # - owner_id: User ID of owner + # - owner_address: TON blockchain address + # - gift_address: NFT address on blockchain + + +Filtering Star Gift Messages +---------------------------- + +You can create a custom filter for star gift messages: + +.. code-block:: python + + from pyrogram import Client, filters + from pyrogram.enums import MessageServiceType + + # Custom filter for star gifts + star_gift_filter = filters.create( + lambda _, __, m: m.service in ( + MessageServiceType.STAR_GIFT, + MessageServiceType.STAR_GIFT_UNIQUE + ) + ) + + app = Client("my_account") + + + @app.on_message(star_gift_filter) + async def on_star_gift(client, message): + """Handle only star gift messages.""" + if message.service == MessageServiceType.STAR_GIFT: + await message.reply("Thank you for the star gift! ⭐") + else: + await message.reply("Wow, a unique collectible gift! 🎁") + + + app.run() diff --git a/docs/source/start/examples/styled_keyboards.rst b/docs/source/start/examples/styled_keyboards.rst new file mode 100644 index 00000000..0f5aff22 --- /dev/null +++ b/docs/source/start/examples/styled_keyboards.rst @@ -0,0 +1,113 @@ +styled_keyboards +================ + +This example shows how to use the new KeyboardButtonStyle to create styled keyboard buttons with custom colors and icons. + +Available since Layer 224, keyboard buttons can now have custom styles including background colors (primary, danger, success) and custom emoji icons. + +.. code-block:: python + + from pyrogram import Client + from pyrogram.types import ( + ReplyKeyboardMarkup, + KeyboardButton, + KeyboardButtonStyle + ) + + # Create a client using your bot token + app = Client("my_bot", bot_token="123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11") + + + async def main(): + async with app: + # Example 1: Button with primary (blue) background + await app.send_message( + "me", # Edit this + "Styled keyboard with primary button", + reply_markup=ReplyKeyboardMarkup( + [[ + KeyboardButton( + text="Primary Action", + style=KeyboardButtonStyle(bg_primary=True) + ) + ]], + resize_keyboard=True + ) + ) + + # Example 2: Button with danger (red) background + await app.send_message( + "me", # Edit this + "Styled keyboard with danger button", + reply_markup=ReplyKeyboardMarkup( + [[ + KeyboardButton( + text="Delete All", + style=KeyboardButtonStyle(bg_danger=True) + ) + ]], + resize_keyboard=True + ) + ) + + # Example 3: Button with success (green) background + await app.send_message( + "me", # Edit this + "Styled keyboard with success button", + reply_markup=ReplyKeyboardMarkup( + [[ + KeyboardButton( + text="Confirm", + style=KeyboardButtonStyle(bg_success=True) + ) + ]], + resize_keyboard=True + ) + ) + + # Example 4: Button with custom emoji icon + await app.send_message( + "me", # Edit this + "Styled keyboard with icon", + reply_markup=ReplyKeyboardMarkup( + [[ + KeyboardButton( + text="Settings", + style=KeyboardButtonStyle( + icon=5368324170671202286 # Custom emoji ID + ) + ) + ]], + resize_keyboard=True + ) + ) + + # Example 5: Mixed styles in one keyboard + await app.send_message( + "me", # Edit this + "Choose an action:", + reply_markup=ReplyKeyboardMarkup( + [ + [ + KeyboardButton( + text="Save", + style=KeyboardButtonStyle(bg_success=True) + ), + KeyboardButton( + text="Cancel", + style=KeyboardButtonStyle(bg_danger=True) + ) + ], + [ + KeyboardButton( + text="More Options", + style=KeyboardButtonStyle(bg_primary=True) + ) + ] + ], + resize_keyboard=True + ) + ) + + + app.run(main()) From e2e3f40e3eee7f3de8a2afefb8b7560197dd39ea Mon Sep 17 00:00:00 2001 From: Pyrogram-Mod - Dev <> Date: Fri, 30 Jan 2026 01:44:51 +0100 Subject: [PATCH 04/10] Fix type hints and parameters for styled inline keyboards. - Corrected request_write_access type hint in LoginUrl (str -> bool) - Applied InputKeyboardButtonUserProfile input_user fix - Added style support to all inline button types - Updated documentation with comprehensive examples --- .../start/examples/styled_keyboards.rst | 174 +++++++++++++++++- .../inline_keyboard_button.py | 59 ++++-- .../types/bots_and_keyboards/login_url.py | 9 +- 3 files changed, 220 insertions(+), 22 deletions(-) diff --git a/docs/source/start/examples/styled_keyboards.rst b/docs/source/start/examples/styled_keyboards.rst index 0f5aff22..2ed6500e 100644 --- a/docs/source/start/examples/styled_keyboards.rst +++ b/docs/source/start/examples/styled_keyboards.rst @@ -3,7 +3,10 @@ styled_keyboards This example shows how to use the new KeyboardButtonStyle to create styled keyboard buttons with custom colors and icons. -Available since Layer 224, keyboard buttons can now have custom styles including background colors (primary, danger, success) and custom emoji icons. +Available since Layer 224, keyboard buttons can now have custom styles including background colors (primary, danger, success) and custom emoji icons. This works for both ReplyKeyboardMarkup (regular keyboards) and InlineKeyboardMarkup (inline buttons). + +Reply Keyboard Examples +----------------------- .. code-block:: python @@ -110,4 +113,173 @@ Available since Layer 224, keyboard buttons can now have custom styles including ) + app.run(main()) + +Inline Keyboard Examples +------------------------ + +.. code-block:: python + + from pyrogram import Client + from pyrogram.types import ( + InlineKeyboardMarkup, + InlineKeyboardButton, + KeyboardButtonStyle, + WebAppInfo, + LoginUrl + ) + + # Create a client using your bot token + app = Client("my_bot", bot_token="123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11") + + + async def main(): + async with app: + # Example 1: Callback button with primary (blue) background + await app.send_message( + "me", # Edit this + "Callback button example:", + reply_markup=InlineKeyboardMarkup( + [[ + InlineKeyboardButton( + text="Click Me", + callback_data="clicked", + style=KeyboardButtonStyle(bg_primary=True) + ) + ]] + ) + ) + + # Example 2: URL button with danger (red) style + await app.send_message( + "me", # Edit this + "URL button example:", + reply_markup=InlineKeyboardMarkup( + [[ + InlineKeyboardButton( + text="Open Website", + url="https://example.com", + style=KeyboardButtonStyle(bg_danger=True) + ) + ]] + ) + ) + + # Example 3: Switch inline query button with success (green) style + await app.send_message( + "me", # Edit this + "Inline query buttons:", + reply_markup=InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton( + text="Search in other chat", + switch_inline_query="search query", + style=KeyboardButtonStyle(bg_primary=True) + ) + ], + [ + InlineKeyboardButton( + text="Search here", + switch_inline_query_current_chat="search query", + style=KeyboardButtonStyle(bg_success=True) + ) + ] + ] + ) + ) + + # Example 4: User profile button with custom emoji icon + await app.send_message( + "me", # Edit this + "User profile button:", + reply_markup=InlineKeyboardMarkup( + [[ + InlineKeyboardButton( + text="View Profile", + user_id=123456789, # Replace with actual user ID + style=KeyboardButtonStyle( + icon=5368324170671202286 # Custom emoji ID + ) + ) + ]] + ) + ) + + # Example 5: WebApp button with primary style + await app.send_message( + "me", # Edit this + "WebApp button:", + reply_markup=InlineKeyboardMarkup( + [[ + InlineKeyboardButton( + text="Open WebApp", + web_app=WebAppInfo(url="https://example.com/webapp"), + style=KeyboardButtonStyle(bg_primary=True) + ) + ]] + ) + ) + + # Example 6: Login URL button with style + await app.send_message( + "me", # Edit this + "Login button:", + reply_markup=InlineKeyboardMarkup( + [[ + InlineKeyboardButton( + text="Login with Telegram", + login_url=LoginUrl( + url="https://example.com/login", + forward_text="Login to Example", + request_write_access=True + ), + style=KeyboardButtonStyle(bg_success=True) + ) + ]] + ) + ) + + # Example 7: All button types in one keyboard + await app.send_message( + "me", # Edit this + "Complete keyboard with all styled button types:", + reply_markup=InlineKeyboardMarkup( + [ + # Row 1: Callback buttons + [ + InlineKeyboardButton( + text="βœ… Confirm", + callback_data="confirm", + style=KeyboardButtonStyle(bg_success=True) + ), + InlineKeyboardButton( + text="❌ Cancel", + callback_data="cancel", + style=KeyboardButtonStyle(bg_danger=True) + ) + ], + # Row 2: URL button + [ + InlineKeyboardButton( + text="🌐 Visit Website", + url="https://example.com", + style=KeyboardButtonStyle(bg_primary=True) + ) + ], + # Row 3: Inline query buttons + [ + InlineKeyboardButton( + text="πŸ” Search", + switch_inline_query_current_chat="", + style=KeyboardButtonStyle( + icon=5368324170671202286 # Search emoji + ) + ) + ] + ] + ) + ) + + app.run(main()) diff --git a/pyrogram/types/bots_and_keyboards/inline_keyboard_button.py b/pyrogram/types/bots_and_keyboards/inline_keyboard_button.py index a1d8a7ad..fa40a896 100644 --- a/pyrogram/types/bots_and_keyboards/inline_keyboard_button.py +++ b/pyrogram/types/bots_and_keyboards/inline_keyboard_button.py @@ -69,6 +69,9 @@ class InlineKeyboardButton(Object): callback_game (:obj:`~pyrogram.types.CallbackGame`, *optional*): Description of the game that will be launched when the user presses the button. **NOTE**: This type of button **must** always be the first button in the first row. + + style (:obj:`~pyrogram.types.KeyboardButtonStyle`, *optional*): + Style configuration for the button (background color, icon). """ def __init__( @@ -81,7 +84,8 @@ def __init__( user_id: int = None, switch_inline_query: str = None, switch_inline_query_current_chat: str = None, - callback_game: "types.CallbackGame" = None + callback_game: "types.CallbackGame" = None, + style: "types.KeyboardButtonStyle" = None ): super().__init__() @@ -94,10 +98,13 @@ def __init__( self.switch_inline_query = switch_inline_query self.switch_inline_query_current_chat = switch_inline_query_current_chat self.callback_game = callback_game + self.style = style # self.pay = pay @staticmethod def read(b: "raw.base.KeyboardButton"): + style = types.KeyboardButtonStyle.read(getattr(b, "style", None)) + if isinstance(b, raw.types.KeyboardButtonCallback): # Try decode data to keep it as string, but if fails, fallback to bytes so we don't lose any information, # instead of decoding by ignoring/replacing errors. @@ -108,43 +115,50 @@ def read(b: "raw.base.KeyboardButton"): return InlineKeyboardButton( text=b.text, - callback_data=data + callback_data=data, + style=style ) if isinstance(b, raw.types.KeyboardButtonUrl): return InlineKeyboardButton( text=b.text, - url=b.url + url=b.url, + style=style ) if isinstance(b, raw.types.KeyboardButtonUrlAuth): return InlineKeyboardButton( text=b.text, - login_url=types.LoginUrl.read(b) + login_url=types.LoginUrl.read(b), + style=style ) if isinstance(b, raw.types.KeyboardButtonUserProfile): return InlineKeyboardButton( text=b.text, - user_id=b.user_id + user_id=b.user_id, + style=style ) if isinstance(b, raw.types.KeyboardButtonSwitchInline): if b.same_peer: return InlineKeyboardButton( text=b.text, - switch_inline_query_current_chat=b.query + switch_inline_query_current_chat=b.query, + style=style ) else: return InlineKeyboardButton( text=b.text, - switch_inline_query=b.query + switch_inline_query=b.query, + style=style ) if isinstance(b, raw.types.KeyboardButtonGame): return InlineKeyboardButton( text=b.text, - callback_game=types.CallbackGame() + callback_game=types.CallbackGame(), + style=style ) if isinstance(b, raw.types.KeyboardButtonWebView): @@ -152,57 +166,68 @@ def read(b: "raw.base.KeyboardButton"): text=b.text, web_app=types.WebAppInfo( url=b.url - ) + ), + style=style ) async def write(self, client: "pyrogram.Client"): + style = self.style.write() if self.style else None + if self.callback_data is not None: # Telegram only wants bytes, but we are allowed to pass strings too, for convenience. data = bytes(self.callback_data, "utf-8") if isinstance(self.callback_data, str) else self.callback_data return raw.types.KeyboardButtonCallback( text=self.text, - data=data + data=data, + style=style ) if self.url is not None: return raw.types.KeyboardButtonUrl( text=self.text, - url=self.url + url=self.url, + style=style ) if self.login_url is not None: return self.login_url.write( text=self.text, - bot=await client.resolve_peer(self.login_url.bot_username or "self") + bot=await client.resolve_peer(self.login_url.bot_username or "self"), + style=style ) if self.user_id is not None: return raw.types.InputKeyboardButtonUserProfile( text=self.text, - user_id=await client.resolve_peer(self.user_id) + input_user=await client.resolve_peer(self.user_id), + style=style ) if self.switch_inline_query is not None: return raw.types.KeyboardButtonSwitchInline( text=self.text, - query=self.switch_inline_query + query=self.switch_inline_query, + style=style ) if self.switch_inline_query_current_chat is not None: return raw.types.KeyboardButtonSwitchInline( text=self.text, query=self.switch_inline_query_current_chat, - same_peer=True + same_peer=True, + style=style ) if self.callback_game is not None: return raw.types.KeyboardButtonGame( - text=self.text + text=self.text, + style=style ) if self.web_app is not None: return raw.types.KeyboardButtonWebView( text=self.text, - url=self.web_app.url + url=self.web_app.url, + style=style ) diff --git a/pyrogram/types/bots_and_keyboards/login_url.py b/pyrogram/types/bots_and_keyboards/login_url.py index a0af5a1a..2cec2055 100644 --- a/pyrogram/types/bots_and_keyboards/login_url.py +++ b/pyrogram/types/bots_and_keyboards/login_url.py @@ -49,7 +49,7 @@ class LoginUrl(Object): See `Linking your domain to the bot `_ for more details. - request_write_access (``str``, *optional*): + request_write_access (``bool``, *optional*): Pass True to request the permission for your bot to send messages to the user. button_id (``int``): @@ -61,7 +61,7 @@ def __init__( url: str, forward_text: str = None, bot_username: str = None, - request_write_access: str = None, + request_write_access: bool = None, button_id: int = None ): super().__init__() @@ -80,11 +80,12 @@ def read(b: "raw.types.KeyboardButtonUrlAuth") -> "LoginUrl": button_id=b.button_id ) - def write(self, text: str, bot: "raw.types.InputUser"): + def write(self, text: str, bot: "raw.types.InputUser", style: "raw.types.KeyboardButtonStyle" = None): return raw.types.InputKeyboardButtonUrlAuth( text=text, url=self.url, bot=bot, fwd_text=self.forward_text, - request_write_access=self.request_write_access + request_write_access=self.request_write_access, + style=style ) From b073b04ef45d3f8d4b39b732249d3c8e2e05147a Mon Sep 17 00:00:00 2001 From: Pyrogram-Mod - Dev <> Date: Fri, 30 Jan 2026 09:48:53 +0100 Subject: [PATCH 05/10] feat: stories, stars, business, boosts, todo and saved messages --- .gitignore | 1 + compiler/docs/compiler.py | 178 ++++++++- docs/source/api/decorators.rst | 6 + docs/source/api/enums/BoostType.rst | 6 + .../api/enums/BusinessAwayMessageSchedule.rst | 6 + docs/source/api/enums/PrivacyKeyType.rst | 6 + docs/source/api/enums/StoryPrivacyType.rst | 6 + docs/source/api/enums/index.rst | 4 + docs/source/api/handlers.rst | 6 + docs/source/index.rst | 13 + docs/source/topics/boosts-and-giveaways.rst | 117 ++++++ docs/source/topics/business-features.rst | 89 +++++ docs/source/topics/conference-calls.rst | 48 +++ docs/source/topics/keyboard-styles.rst | 64 +++ docs/source/topics/miscellaneous.rst | 61 +++ docs/source/topics/monoforum.rst | 53 +++ docs/source/topics/privacy-settings.rst | 48 +++ docs/source/topics/saved-messages.rst | 77 ++++ docs/source/topics/star-gifts.rst | 79 ++++ docs/source/topics/stories.rst | 111 ++++++ docs/source/topics/suggested-posts.rst | 54 +++ docs/source/topics/telegram-stars.rst | 93 +++++ docs/source/topics/todo-lists.rst | 87 +++++ pyrogram/connection/transport/tcp/tcp.py | 3 +- pyrogram/dispatcher.py | 52 ++- pyrogram/enums/__init__.py | 8 + pyrogram/enums/boost_type.py | 35 ++ .../enums/business_away_message_schedule.py | 32 ++ pyrogram/enums/message_media_type.py | 9 + pyrogram/enums/message_service_type.py | 51 +++ pyrogram/enums/privacy_key_type.py | 68 ++++ pyrogram/enums/story_privacy_type.py | 35 ++ pyrogram/filters.py | 48 +++ pyrogram/handlers/__init__.py | 21 + .../handlers/business_connection_handler.py | 49 +++ pyrogram/handlers/chat_boost_handler.py | 49 +++ pyrogram/handlers/story_handler.py | 49 +++ pyrogram/methods/__init__.py | 10 + pyrogram/methods/boosts/__init__.py | 33 ++ pyrogram/methods/boosts/apply_boost.py | 63 +++ .../methods/boosts/get_boost_level_options.py | 44 +++ pyrogram/methods/boosts/get_boost_status.py | 56 +++ pyrogram/methods/boosts/get_boosts_list.py | 89 +++++ pyrogram/methods/boosts/get_my_boosts.py | 34 ++ pyrogram/methods/business/__init__.py | 46 +++ .../business/check_quick_reply_shortcut.py | 43 +++ .../business/create_business_chat_link.py | 43 +++ .../business/delete_business_chat_link.py | 42 ++ .../business/delete_quick_reply_messages.py | 49 +++ .../business/delete_quick_reply_shortcut.py | 42 ++ .../business/edit_business_chat_link.py | 48 +++ .../business/edit_quick_reply_shortcut.py | 47 +++ .../business/get_bot_business_connection.py | 42 ++ .../business/get_business_chat_links.py | 37 ++ .../methods/business/get_quick_replies.py | 37 ++ .../business/get_quick_reply_messages.py | 49 +++ .../business/send_quick_reply_messages.py | 54 +++ pyrogram/methods/decorators/__init__.py | 8 +- .../decorators/on_business_connection.py | 44 +++ pyrogram/methods/decorators/on_chat_boost.py | 44 +++ pyrogram/methods/decorators/on_story.py | 61 +++ pyrogram/methods/messages/__init__.py | 6 + pyrogram/methods/messages/download_media.py | 2 +- .../messages/get_pinned_saved_dialogs.py | 35 ++ .../methods/messages/get_saved_dialogs.py | 60 +++ .../messages/get_saved_reaction_tags.py | 49 +++ pyrogram/methods/messages/pin_saved_dialog.py | 50 +++ .../messages/reorder_pinned_saved_dialogs.py | 52 +++ .../messages/update_saved_reaction_tag.py | 48 +++ pyrogram/methods/payments/__init__.py | 35 ++ .../methods/payments/get_saved_star_gifts.py | 59 +++ .../payments/get_star_gift_auction_state.py | 45 +++ pyrogram/methods/payments/get_star_gifts.py | 40 ++ pyrogram/methods/payments/get_stars_status.py | 63 +++ .../payments/get_stars_transactions.py | 109 ++++++ .../methods/payments/get_unique_star_gift.py | 40 ++ pyrogram/methods/stories/__init__.py | 39 ++ pyrogram/methods/stories/delete_stories.py | 66 ++++ pyrogram/methods/stories/edit_story.py | 100 +++++ pyrogram/methods/stories/get_all_stories.py | 73 ++++ pyrogram/methods/stories/get_peer_stories.py | 62 +++ pyrogram/methods/stories/get_stories.py | 78 ++++ .../stories/get_story_public_forwards.py | 59 +++ .../stories/get_story_reactions_list.py | 69 ++++ .../methods/stories/get_story_views_list.py | 79 ++++ pyrogram/methods/stories/read_stories.py | 61 +++ pyrogram/methods/stories/send_story.py | 256 ++++++++++++ .../methods/stories/send_story_reaction.py | 57 +++ pyrogram/methods/todo/__init__.py | 24 ++ .../methods/todo/toggle_todo_completed.py | 58 +++ pyrogram/methods/utilities/idle.py | 2 +- pyrogram/methods/utilities/run.py | 7 +- pyrogram/sync.py | 6 +- pyrogram/types/__init__.py | 7 + pyrogram/types/boosts/__init__.py | 36 ++ pyrogram/types/boosts/boost.py | 123 ++++++ pyrogram/types/boosts/boost_status.py | 107 +++++ pyrogram/types/boosts/boosts_list.py | 78 ++++ pyrogram/types/boosts/giveaway_results.py | 156 ++++++++ pyrogram/types/boosts/my_boost.py | 79 ++++ pyrogram/types/boosts/my_boosts.py | 58 +++ pyrogram/types/boosts/prepaid_giveaway.py | 76 ++++ pyrogram/types/business/__init__.py | 42 ++ .../types/business/business_bot_recipients.py | 91 +++++ pyrogram/types/business/business_chat_link.py | 132 +++++++ .../types/business/business_connection.py | 88 +++++ pyrogram/types/business/business_hours.py | 110 ++++++ pyrogram/types/business/business_info.py | 93 +++++ pyrogram/types/business/business_intro.py | 71 ++++ pyrogram/types/business/business_location.py | 69 ++++ pyrogram/types/business/business_message.py | 208 ++++++++++ pyrogram/types/business/quick_reply.py | 151 ++++++++ pyrogram/types/calls/__init__.py | 27 ++ pyrogram/types/calls/conference_call.py | 117 ++++++ pyrogram/types/calls/group_call.py | 143 +++++++ .../types/calls/group_call_participant.py | 159 ++++++++ pyrogram/types/input_media/__init__.py | 3 +- .../types/input_media/input_media_todo.py | 52 +++ pyrogram/types/messages_and_media/__init__.py | 14 +- pyrogram/types/messages_and_media/dice.py | 15 +- .../disallowed_gifts_settings.py | 79 ++++ .../types/messages_and_media/fact_check.py | 90 +++++ pyrogram/types/messages_and_media/message.py | 112 ++++++ .../messages_and_media/message_effect.py | 98 +++++ .../types/messages_and_media/paid_media.py | 101 +++++ .../messages_and_media/star_gift_attribute.py | 251 ++++++++++++ .../messages_and_media/suggested_post.py | 86 +++++ pyrogram/types/messages_and_media/video.py | 26 +- pyrogram/types/messages_and_media/web_page.py | 19 +- pyrogram/types/payments/__init__.py | 47 +++ pyrogram/types/payments/auction_bid_level.py | 65 ++++ .../types/payments/paid_reaction_privacy.py | 71 ++++ .../types/payments/star_gift_auction_round.py | 58 +++ .../types/payments/star_gift_auction_state.py | 175 +++++++++ .../payments/star_gift_auction_user_state.py | 110 ++++++ pyrogram/types/payments/stars_amount.py | 90 +++++ pyrogram/types/payments/stars_gift_option.py | 76 ++++ .../types/payments/stars_revenue_status.py | 77 ++++ pyrogram/types/payments/stars_status.py | 107 +++++ pyrogram/types/payments/stars_subscription.py | 163 ++++++++ pyrogram/types/payments/stars_topup_option.py | 76 ++++ pyrogram/types/payments/stars_transaction.py | 214 ++++++++++ pyrogram/types/saved_messages/__init__.py | 23 ++ pyrogram/types/saved_messages/saved_dialog.py | 91 +++++ .../types/saved_messages/saved_dialogs.py | 68 ++++ .../saved_messages/saved_reaction_tag.py | 62 +++ pyrogram/types/stats/__init__.py | 22 ++ pyrogram/types/stats/public_forward.py | 72 ++++ pyrogram/types/stats/public_forwards.py | 70 ++++ pyrogram/types/stories/__init__.py | 43 +++ pyrogram/types/stories/media_area.py | 233 +++++++++++ pyrogram/types/stories/peer_stories.py | 86 +++++ .../types/stories/stories_stealth_mode.py | 65 ++++ .../types/stories/story_forward_header.py | 78 ++++ pyrogram/types/stories/story_item.py | 364 ++++++++++++++++++ pyrogram/types/stories/story_reaction.py | 90 +++++ .../types/stories/story_reactions_list.py | 70 ++++ pyrogram/types/stories/story_view.py | 112 ++++++ pyrogram/types/stories/story_views.py | 89 +++++ pyrogram/types/stories/story_views_list.py | 88 +++++ pyrogram/types/todo/__init__.py | 27 ++ pyrogram/types/todo/todo_completion.py | 70 ++++ pyrogram/types/todo/todo_item.py | 86 +++++ pyrogram/types/todo/todo_list.py | 124 ++++++ pyrogram/types/user_and_chats/__init__.py | 13 +- pyrogram/types/user_and_chats/birthday.py | 62 +++ .../types/user_and_chats/bot_verification.py | 62 +++ .../user_and_chats/bot_verifier_settings.py | 62 +++ pyrogram/types/user_and_chats/chat.py | 265 ++++++++++++- pyrogram/types/user_and_chats/wallpaper.py | 183 +++++++++ pyrogram/utils.py | 2 +- 171 files changed, 11723 insertions(+), 31 deletions(-) create mode 100644 docs/source/api/enums/BoostType.rst create mode 100644 docs/source/api/enums/BusinessAwayMessageSchedule.rst create mode 100644 docs/source/api/enums/PrivacyKeyType.rst create mode 100644 docs/source/api/enums/StoryPrivacyType.rst create mode 100644 docs/source/topics/boosts-and-giveaways.rst create mode 100644 docs/source/topics/business-features.rst create mode 100644 docs/source/topics/conference-calls.rst create mode 100644 docs/source/topics/keyboard-styles.rst create mode 100644 docs/source/topics/miscellaneous.rst create mode 100644 docs/source/topics/monoforum.rst create mode 100644 docs/source/topics/privacy-settings.rst create mode 100644 docs/source/topics/saved-messages.rst create mode 100644 docs/source/topics/star-gifts.rst create mode 100644 docs/source/topics/stories.rst create mode 100644 docs/source/topics/suggested-posts.rst create mode 100644 docs/source/topics/telegram-stars.rst create mode 100644 docs/source/topics/todo-lists.rst create mode 100644 pyrogram/enums/boost_type.py create mode 100644 pyrogram/enums/business_away_message_schedule.py create mode 100644 pyrogram/enums/privacy_key_type.py create mode 100644 pyrogram/enums/story_privacy_type.py create mode 100644 pyrogram/handlers/business_connection_handler.py create mode 100644 pyrogram/handlers/chat_boost_handler.py create mode 100644 pyrogram/handlers/story_handler.py create mode 100644 pyrogram/methods/boosts/__init__.py create mode 100644 pyrogram/methods/boosts/apply_boost.py create mode 100644 pyrogram/methods/boosts/get_boost_level_options.py create mode 100644 pyrogram/methods/boosts/get_boost_status.py create mode 100644 pyrogram/methods/boosts/get_boosts_list.py create mode 100644 pyrogram/methods/boosts/get_my_boosts.py create mode 100644 pyrogram/methods/business/__init__.py create mode 100644 pyrogram/methods/business/check_quick_reply_shortcut.py create mode 100644 pyrogram/methods/business/create_business_chat_link.py create mode 100644 pyrogram/methods/business/delete_business_chat_link.py create mode 100644 pyrogram/methods/business/delete_quick_reply_messages.py create mode 100644 pyrogram/methods/business/delete_quick_reply_shortcut.py create mode 100644 pyrogram/methods/business/edit_business_chat_link.py create mode 100644 pyrogram/methods/business/edit_quick_reply_shortcut.py create mode 100644 pyrogram/methods/business/get_bot_business_connection.py create mode 100644 pyrogram/methods/business/get_business_chat_links.py create mode 100644 pyrogram/methods/business/get_quick_replies.py create mode 100644 pyrogram/methods/business/get_quick_reply_messages.py create mode 100644 pyrogram/methods/business/send_quick_reply_messages.py create mode 100644 pyrogram/methods/decorators/on_business_connection.py create mode 100644 pyrogram/methods/decorators/on_chat_boost.py create mode 100644 pyrogram/methods/decorators/on_story.py create mode 100644 pyrogram/methods/messages/get_pinned_saved_dialogs.py create mode 100644 pyrogram/methods/messages/get_saved_dialogs.py create mode 100644 pyrogram/methods/messages/get_saved_reaction_tags.py create mode 100644 pyrogram/methods/messages/pin_saved_dialog.py create mode 100644 pyrogram/methods/messages/reorder_pinned_saved_dialogs.py create mode 100644 pyrogram/methods/messages/update_saved_reaction_tag.py create mode 100644 pyrogram/methods/payments/__init__.py create mode 100644 pyrogram/methods/payments/get_saved_star_gifts.py create mode 100644 pyrogram/methods/payments/get_star_gift_auction_state.py create mode 100644 pyrogram/methods/payments/get_star_gifts.py create mode 100644 pyrogram/methods/payments/get_stars_status.py create mode 100644 pyrogram/methods/payments/get_stars_transactions.py create mode 100644 pyrogram/methods/payments/get_unique_star_gift.py create mode 100644 pyrogram/methods/stories/__init__.py create mode 100644 pyrogram/methods/stories/delete_stories.py create mode 100644 pyrogram/methods/stories/edit_story.py create mode 100644 pyrogram/methods/stories/get_all_stories.py create mode 100644 pyrogram/methods/stories/get_peer_stories.py create mode 100644 pyrogram/methods/stories/get_stories.py create mode 100644 pyrogram/methods/stories/get_story_public_forwards.py create mode 100644 pyrogram/methods/stories/get_story_reactions_list.py create mode 100644 pyrogram/methods/stories/get_story_views_list.py create mode 100644 pyrogram/methods/stories/read_stories.py create mode 100644 pyrogram/methods/stories/send_story.py create mode 100644 pyrogram/methods/stories/send_story_reaction.py create mode 100644 pyrogram/methods/todo/__init__.py create mode 100644 pyrogram/methods/todo/toggle_todo_completed.py create mode 100644 pyrogram/types/boosts/__init__.py create mode 100644 pyrogram/types/boosts/boost.py create mode 100644 pyrogram/types/boosts/boost_status.py create mode 100644 pyrogram/types/boosts/boosts_list.py create mode 100644 pyrogram/types/boosts/giveaway_results.py create mode 100644 pyrogram/types/boosts/my_boost.py create mode 100644 pyrogram/types/boosts/my_boosts.py create mode 100644 pyrogram/types/boosts/prepaid_giveaway.py create mode 100644 pyrogram/types/business/__init__.py create mode 100644 pyrogram/types/business/business_bot_recipients.py create mode 100644 pyrogram/types/business/business_chat_link.py create mode 100644 pyrogram/types/business/business_connection.py create mode 100644 pyrogram/types/business/business_hours.py create mode 100644 pyrogram/types/business/business_info.py create mode 100644 pyrogram/types/business/business_intro.py create mode 100644 pyrogram/types/business/business_location.py create mode 100644 pyrogram/types/business/business_message.py create mode 100644 pyrogram/types/business/quick_reply.py create mode 100644 pyrogram/types/calls/__init__.py create mode 100644 pyrogram/types/calls/conference_call.py create mode 100644 pyrogram/types/calls/group_call.py create mode 100644 pyrogram/types/calls/group_call_participant.py create mode 100644 pyrogram/types/input_media/input_media_todo.py create mode 100644 pyrogram/types/messages_and_media/disallowed_gifts_settings.py create mode 100644 pyrogram/types/messages_and_media/fact_check.py create mode 100644 pyrogram/types/messages_and_media/message_effect.py create mode 100644 pyrogram/types/messages_and_media/paid_media.py create mode 100644 pyrogram/types/messages_and_media/star_gift_attribute.py create mode 100644 pyrogram/types/messages_and_media/suggested_post.py create mode 100644 pyrogram/types/payments/__init__.py create mode 100644 pyrogram/types/payments/auction_bid_level.py create mode 100644 pyrogram/types/payments/paid_reaction_privacy.py create mode 100644 pyrogram/types/payments/star_gift_auction_round.py create mode 100644 pyrogram/types/payments/star_gift_auction_state.py create mode 100644 pyrogram/types/payments/star_gift_auction_user_state.py create mode 100644 pyrogram/types/payments/stars_amount.py create mode 100644 pyrogram/types/payments/stars_gift_option.py create mode 100644 pyrogram/types/payments/stars_revenue_status.py create mode 100644 pyrogram/types/payments/stars_status.py create mode 100644 pyrogram/types/payments/stars_subscription.py create mode 100644 pyrogram/types/payments/stars_topup_option.py create mode 100644 pyrogram/types/payments/stars_transaction.py create mode 100644 pyrogram/types/saved_messages/__init__.py create mode 100644 pyrogram/types/saved_messages/saved_dialog.py create mode 100644 pyrogram/types/saved_messages/saved_dialogs.py create mode 100644 pyrogram/types/saved_messages/saved_reaction_tag.py create mode 100644 pyrogram/types/stats/__init__.py create mode 100644 pyrogram/types/stats/public_forward.py create mode 100644 pyrogram/types/stats/public_forwards.py create mode 100644 pyrogram/types/stories/__init__.py create mode 100644 pyrogram/types/stories/media_area.py create mode 100644 pyrogram/types/stories/peer_stories.py create mode 100644 pyrogram/types/stories/stories_stealth_mode.py create mode 100644 pyrogram/types/stories/story_forward_header.py create mode 100644 pyrogram/types/stories/story_item.py create mode 100644 pyrogram/types/stories/story_reaction.py create mode 100644 pyrogram/types/stories/story_reactions_list.py create mode 100644 pyrogram/types/stories/story_view.py create mode 100644 pyrogram/types/stories/story_views.py create mode 100644 pyrogram/types/stories/story_views_list.py create mode 100644 pyrogram/types/todo/__init__.py create mode 100644 pyrogram/types/todo/todo_completion.py create mode 100644 pyrogram/types/todo/todo_item.py create mode 100644 pyrogram/types/todo/todo_list.py create mode 100644 pyrogram/types/user_and_chats/birthday.py create mode 100644 pyrogram/types/user_and_chats/bot_verification.py create mode 100644 pyrogram/types/user_and_chats/bot_verifier_settings.py create mode 100644 pyrogram/types/user_and_chats/wallpaper.py diff --git a/.gitignore b/.gitignore index 82adb3ee..980af071 100644 --- a/.gitignore +++ b/.gitignore @@ -130,3 +130,4 @@ venv.bak/ docs_build.sh .buildinfo json_docs.py +*.session-journal diff --git a/compiler/docs/compiler.py b/compiler/docs/compiler.py index f87f1f07..a4d24937 100644 --- a/compiler/docs/compiler.py +++ b/compiler/docs/compiler.py @@ -222,6 +222,24 @@ def get_title_list(s: str) -> list: export_session_string set_parse_mode """, + decorators=""" + Decorators + on_message + on_edited_message + on_callback_query + on_inline_query + on_chosen_inline_result + on_poll + on_user_status + on_deleted_messages + on_chat_member_updated + on_raw_update + on_disconnect + on_chat_join_request + on_story + on_chat_boost + on_business_connection + """, messages=""" Messages send_message @@ -242,6 +260,7 @@ def get_title_list(s: str) -> list: send_contact send_cached_media send_reaction + send_paid_reaction edit_message_text edit_message_caption edit_message_media @@ -272,6 +291,13 @@ def get_title_list(s: str) -> list: get_discussion_replies get_discussion_replies_count get_custom_emoji_stickers + toggle_todo_completed + get_saved_dialogs + get_pinned_saved_dialogs + reorder_pinned_saved_dialogs + pin_saved_dialog + get_saved_reaction_tags + update_saved_reaction_tag """, chats=""" Chats @@ -387,6 +413,7 @@ def get_title_list(s: str) -> list: set_chat_menu_button get_chat_menu_button answer_web_app_query + send_streaming_text """, authorization=""" Authorization @@ -411,6 +438,50 @@ def get_title_list(s: str) -> list: invoke resolve_peer save_file + """, + stories=""" + Stories + send_story + get_stories + edit_story + delete_stories + read_stories + get_story_public_forwards + get_story_views_list + get_story_reactions_list + send_story_reaction + """, + payments=""" + Payments + get_stars_status + get_stars_transactions + get_star_gifts + get_unique_star_gift + get_saved_star_gifts + get_star_gift_auction_state + """, + boosts=""" + Boosts + get_boost_status + get_boosts_list + apply_boost + get_boost_level_options + get_my_boosts + """, + business=""" + Business + get_business_chat_links + create_business_chat_link + edit_business_chat_link + delete_business_chat_link + get_bot_business_connection + get_quick_replies + get_quick_reply_messages + send_quick_reply_messages + edit_quick_reply_shortcut + delete_quick_reply_shortcut + check_quick_reply_shortcut + delete_quick_reply_messages """ ) @@ -469,6 +540,9 @@ def get_title_list(s: str) -> list: Dialog Restriction EmojiStatus + Birthday + BotVerification + BotVerifierSettings """, messages_media=""" Messages & Media @@ -500,6 +574,15 @@ def get_title_list(s: str) -> list: WebAppData MessageReactions ChatReactions + PaidMedia + TodoList + TodoItem + TodoCompletion + FactCheck + SuggestedPost + MessageEffect + PublicForward + PublicForwards """, bot_keyboards=""" Bot keyboards @@ -574,6 +657,74 @@ def get_title_list(s: str) -> list: Authorization SentCode TermsOfService + """, + stories=""" + Stories + StoryItem + StoryViews + StoryView + StoryViewsList + PeerStories + StoriesStealthMode + MediaArea + MediaAreaCoordinates + StoryForwardHeader + StoryReaction + StoryReactionsList + """, + payments=""" + Payments + StarsAmount + StarsTransaction + StarsTransactionPeer + StarsSubscription + StarsSubscriptionPricing + StarsStatus + StarsRevenueStatus + StarsTopupOption + StarsGiftOption + PaidReactionPrivacy + """, + star_gifts=""" + Star Gifts + StarGift + StarGiftUnique + StarGiftAuctionState + StarGiftAuctionUserState + StarGiftAuctionRound + AuctionBidLevel + DisallowedGiftsSettings + """, + business=""" + Business + BusinessInfo + BusinessWorkHours + BusinessWeeklyOpen + BusinessLocation + BusinessIntro + BusinessGreetingMessage + BusinessAwayMessage + BusinessRecipients + BusinessConnection + QuickReply + BusinessChatLink + BusinessBotRecipients + """, + boosts=""" + Boosts + Boost + BoostStatus + BoostsList + GiveawayResults + PrepaidGiveaway + MyBoost + MyBoosts + """, + saved_messages=""" + Saved Messages + SavedDialog + SavedDialogs + SavedReactionTag """ ) @@ -685,6 +836,31 @@ def get_title_list(s: str) -> list: ChatJoinRequest ChatJoinRequest.approve ChatJoinRequest.decline + """, + story_item=""" + StoryItem + StoryItem.delete + StoryItem.react + StoryItem.get_views + StoryItem.get_reactions + StoryItem.get_public_forwards + """, + quick_reply=""" + QuickReply + QuickReply.get_messages + QuickReply.send_messages + QuickReply.edit + QuickReply.delete + """, + business_chat_link=""" + BusinessChatLink + BusinessChatLink.edit + BusinessChatLink.delete + """, + saved_dialog=""" + SavedDialog + SavedDialog.pin + SavedDialog.unpin """ ) @@ -744,4 +920,4 @@ def start(): DESTINATION = "../../docs/source/telegram" PYROGRAM_API_DEST = "../../docs/source/api" - start() \ No newline at end of file + start() diff --git a/docs/source/api/decorators.rst b/docs/source/api/decorators.rst index ba4fbab3..d0d08142 100644 --- a/docs/source/api/decorators.rst +++ b/docs/source/api/decorators.rst @@ -43,6 +43,9 @@ Index - :meth:`~Client.on_poll` - :meth:`~Client.on_disconnect` - :meth:`~Client.on_raw_update` + - :meth:`~Client.on_story` + - :meth:`~Client.on_chat_boost` + - :meth:`~Client.on_business_connection` ----- @@ -62,3 +65,6 @@ Details .. autodecorator:: pyrogram.Client.on_poll() .. autodecorator:: pyrogram.Client.on_disconnect() .. autodecorator:: pyrogram.Client.on_raw_update() +.. autodecorator:: pyrogram.Client.on_story() +.. autodecorator:: pyrogram.Client.on_chat_boost() +.. autodecorator:: pyrogram.Client.on_business_connection() diff --git a/docs/source/api/enums/BoostType.rst b/docs/source/api/enums/BoostType.rst new file mode 100644 index 00000000..08c75d37 --- /dev/null +++ b/docs/source/api/enums/BoostType.rst @@ -0,0 +1,6 @@ +BoostType +========= + +.. currentmodule:: pyrogram.enums + +.. autoclass:: BoostType diff --git a/docs/source/api/enums/BusinessAwayMessageSchedule.rst b/docs/source/api/enums/BusinessAwayMessageSchedule.rst new file mode 100644 index 00000000..d418a8d6 --- /dev/null +++ b/docs/source/api/enums/BusinessAwayMessageSchedule.rst @@ -0,0 +1,6 @@ +BusinessAwayMessageSchedule +=========================== + +.. currentmodule:: pyrogram.enums + +.. autoclass:: BusinessAwayMessageSchedule diff --git a/docs/source/api/enums/PrivacyKeyType.rst b/docs/source/api/enums/PrivacyKeyType.rst new file mode 100644 index 00000000..73fe840e --- /dev/null +++ b/docs/source/api/enums/PrivacyKeyType.rst @@ -0,0 +1,6 @@ +PrivacyKeyType +============== + +.. currentmodule:: pyrogram.enums + +.. autoclass:: PrivacyKeyType diff --git a/docs/source/api/enums/StoryPrivacyType.rst b/docs/source/api/enums/StoryPrivacyType.rst new file mode 100644 index 00000000..7df91efb --- /dev/null +++ b/docs/source/api/enums/StoryPrivacyType.rst @@ -0,0 +1,6 @@ +StoryPrivacyType +================ + +.. currentmodule:: pyrogram.enums + +.. autoclass:: StoryPrivacyType diff --git a/docs/source/api/enums/index.rst b/docs/source/api/enums/index.rst index bd9f8b1d..67e5c18e 100644 --- a/docs/source/api/enums/index.rst +++ b/docs/source/api/enums/index.rst @@ -27,6 +27,10 @@ to apply only a valid value among the expected ones. SentCodeType NextCodeType UserStatus + BoostType + BusinessAwayMessageSchedule + PrivacyKeyType + StoryPrivacyType .. toctree:: :hidden: diff --git a/docs/source/api/handlers.rst b/docs/source/api/handlers.rst index 68034efd..6480e3b6 100644 --- a/docs/source/api/handlers.rst +++ b/docs/source/api/handlers.rst @@ -42,6 +42,9 @@ Index - :class:`PollHandler` - :class:`DisconnectHandler` - :class:`RawUpdateHandler` + - :class:`StoryHandler` + - :class:`ChatBoostHandler` + - :class:`BusinessConnectionHandler` ----- @@ -60,3 +63,6 @@ Details .. autoclass:: PollHandler() .. autoclass:: DisconnectHandler() .. autoclass:: RawUpdateHandler() +.. autoclass:: StoryHandler() +.. autoclass:: ChatBoostHandler() +.. autoclass:: BusinessConnectionHandler() diff --git a/docs/source/index.rst b/docs/source/index.rst index 0d4f077a..b1aa049c 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -149,6 +149,19 @@ Meta topics/test-servers topics/advanced-usage topics/voice-calls + topics/stories + topics/telegram-stars + topics/star-gifts + topics/business-features + topics/boosts-and-giveaways + topics/todo-lists + topics/saved-messages + topics/conference-calls + topics/monoforum + topics/suggested-posts + topics/privacy-settings + topics/keyboard-styles + topics/miscellaneous .. toctree:: :hidden: diff --git a/docs/source/topics/boosts-and-giveaways.rst b/docs/source/topics/boosts-and-giveaways.rst new file mode 100644 index 00000000..bc49fd1c --- /dev/null +++ b/docs/source/topics/boosts-and-giveaways.rst @@ -0,0 +1,117 @@ +Boosts and Giveaways +==================== + +Telegram channels support boosts and giveaways to grow and engage with audiences. + +Channel Boosts +-------------- + +Get boost status for a channel: + +.. code-block:: python + + from pyrogram import Client + + app = Client("my_account") + + + async def main(): + boost_status = await app.get_boost_status("channel_username") + + print(f"Total boosts: {boost_status.level}") + print(f"Current level: {boost_status.current_level_boosts}") + print(f"Boosts to next level: {boost_status.boosts}") + + + with app: + app.loop.run_until_complete(main()) + +Applying Boosts +--------------- + +Apply a boost to a channel: + +.. code-block:: python + + from pyrogram import Client + + app = Client("my_account") + + + async def main(): + await app.apply_boost("channel_username") + print("Boost applied successfully!") + + + with app: + app.loop.run_until_complete(main()) + +Getting Boosts List +------------------- + +Retrieve the list of boosts for a channel: + +.. code-block:: python + + from pyrogram import Client + + app = Client("my_account") + + + async def main(): + boosts = await app.get_boosts_list("channel_username") + + for boost in boosts: + print(f"Boost from: {boost.user_id}") + print(f"Date: {boost.date}") + + + with app: + app.loop.run_until_complete(main()) + +Handling Boost Updates +----------------------- + +Handle boost updates with a handler: + +.. code-block:: python + + from pyrogram import Client + from pyrogram.handlers import ChatBoostHandler + + app = Client("my_bot_token") + + + async def boost_handler(client, boost): + print(f"New boost in {boost.chat.title}") + print(f"Boost count: {boost.boost.boosts}") + + + app.add_handler(ChatBoostHandler(boost_handler)) + app.run() + +Giveaway Messages +----------------- + +Handle giveaway-related messages: + +.. code-block:: python + + from pyrogram import Client + + app = Client("my_account") + + + @app.on_message() + async def giveaway_handler(client, message): + if message.giveaway: + print(f"Giveaway for {message.giveaway.quantity} prizes") + print(f"Ends: {message.giveaway.until_date}") + print(f"Channels: {[c.title for c in message.giveaway.channels]}") + + if message.giveaway_results: + print(f"Winners: {message.giveaway_results.winners_count}") + print(f"Unclaimed: {message.giveaway_results.unclaimed_count}") + + + app.run() diff --git a/docs/source/topics/business-features.rst b/docs/source/topics/business-features.rst new file mode 100644 index 00000000..86bf39b0 --- /dev/null +++ b/docs/source/topics/business-features.rst @@ -0,0 +1,89 @@ +Business Features +================= + +Telegram Business accounts offer special features for business automation. + +Handling Business Messages +--------------------------- + +Receive and handle messages from business chats: + +.. code-block:: python + + from pyrogram import Client + + app = Client("my_bot_token") + + + @app.on_message() + async def business_message_handler(client, message): + print(f"Business message: {message.text}") + + + app.run() + +Business Connections +-------------------- + +Handle business connection updates: + +.. code-block:: python + + from pyrogram import Client + from pyrogram.handlers import BusinessConnectionHandler + + app = Client("my_bot_token") + + + async def connection_handler(client, connection): + print(f"Connected to business: {connection.user.username}") + print(f"Connection ID: {connection.id}") + + + app.add_handler(BusinessConnectionHandler(connection_handler)) + app.run() + +Quick Replies +------------- + +Business accounts can use quick reply shortcuts. Messages with quick replies have the +:attr:`~pyrogram.types.Message.quick_reply_shortcut_id` attribute set. + +.. code-block:: python + + from pyrogram import Client + + app = Client("my_account") + + + @app.on_message() + async def handle_quick_reply(client, message): + if message.quick_reply_shortcut_id: + print(f"Sent using quick reply: {message.quick_reply_shortcut_id}") + + + app.run() + +Business Information +-------------------- + +Access business account information including work hours, location, and greeting messages: + +.. code-block:: python + + from pyrogram import Client + + app = Client("my_account") + + + async def main(): + chat = await app.get_chat("business_username") + + if chat.business_info: + print(f"Work hours: {chat.business_info.business_hours}") + print(f"Location: {chat.business_info.location}") + print(f"Intro: {chat.business_info.intro}") + + + with app: + app.loop.run_until_complete(main()) diff --git a/docs/source/topics/conference-calls.rst b/docs/source/topics/conference-calls.rst new file mode 100644 index 00000000..53a8817c --- /dev/null +++ b/docs/source/topics/conference-calls.rst @@ -0,0 +1,48 @@ +Conference Calls +================ + +Telegram Group Calls have been enhanced with conference features. + +Checking Conference Support +--------------------------- + +Check if a group call supports conference mode: + +.. code-block:: python + + from pyrogram import Client + + app = Client("my_account") + + + async def main(): + chat = await app.get_chat("group_username") + call = chat.call + + if call and call.is_conference: + print("This is a conference call") + print(f"Title: {call.title}") + + + with app: + app.loop.run_until_complete(main()) + +Handling Call Events +-------------------- + +Handle service messages related to conference calls: + +.. code-block:: python + + from pyrogram import Client, enums + + app = Client("my_account") + + + @app.on_message() + async def call_handler(client, message): + if message.service == enums.MessageServiceType.CONFERENCE_CALL: + print(f"Conference call action in {message.chat.title}") + + + app.run() diff --git a/docs/source/topics/keyboard-styles.rst b/docs/source/topics/keyboard-styles.rst new file mode 100644 index 00000000..8ec0aec4 --- /dev/null +++ b/docs/source/topics/keyboard-styles.rst @@ -0,0 +1,64 @@ +Keyboard Button Styles +====================== + +Pyrogram now supports styled inline and reply keyboard buttons. + +Button Styles +------------- + +Buttons can have different visual styles using the :class:`~pyrogram.enums.KeyboardButtonStyle` enum: + +- **PRIMARY**: Default blue style. +- **DANGER**: Red style for destructive actions. +- **SUCCESS**: Green style for positive actions. + +.. code-block:: python + + from pyrogram import Client, types, enums + + app = Client("my_account") + + async def main(): + await app.send_message( + "chat_id", + "Choose an action:", + reply_markup=types.InlineKeyboardMarkup([ + [ + types.InlineKeyboardButton( + "Delete Record", + callback_data="delete", + style=enums.KeyboardButtonStyle.DANGER + ), + types.InlineKeyboardButton( + "Save Changes", + callback_data="save", + style=enums.KeyboardButtonStyle.SUCCESS + ) + ] + ]) + ) + + app.run(main()) + +Button Icons +------------ + +You can also attach custom icons to buttons: + +.. code-block:: python + + from pyrogram import Client, types + + async def main(): + await app.send_message( + "chat_id", + "Settings:", + reply_markup=types.ReplyKeyboardMarkup([ + [ + types.KeyboardButton( + "Notifications", + icon=123456789 # Custom Emoji ID + ) + ] + ]) + ) diff --git a/docs/source/topics/miscellaneous.rst b/docs/source/topics/miscellaneous.rst new file mode 100644 index 00000000..fb69ca62 --- /dev/null +++ b/docs/source/topics/miscellaneous.rst @@ -0,0 +1,61 @@ +Miscellaneous Updates +===================== + +This section covers various smaller features and improvements introduced in recent layers. + +Birthday Feature +---------------- + +Users can now set and share their birthdays. + +.. code-block:: python + + from pyrogram import Client + + async def main(): + user = await app.get_chat("me") + if user.birthday: + print(f"Birthday: {user.birthday.day}/{user.birthday.month}/{user.birthday.year}") + +Paid Reactions +-------------- + +Support for Telegram Stars-based reactions: + +.. code-block:: python + + from pyrogram import Client + + async def main(): + # Send a paid reaction (1 Star) + await app.send_paid_reaction("chat_id", message_id=123, count=1) + +Audio Transcription +------------------- + +Handle audio transcription updates: + +.. code-block:: python + + from pyrogram import Client + + @app.on_raw_update() + async def handle_transcription(client, update, users, chats): + from pyrogram.raw.types import UpdateTranscribeAudio + + if isinstance(update, UpdateTranscribeAudio): + print(f"Transcription: {update.text}") + +Emoji Game Results +------------------ + +Dice messages now include the specific game outcome: + +.. code-block:: python + + @app.on_message() + async def dice_handler(client, message): + if message.dice: + print(f"Value: {message.dice.value}") + if message.dice.game_outcome: + print(f"Outcome: {message.dice.game_outcome}") diff --git a/docs/source/topics/monoforum.rst b/docs/source/topics/monoforum.rst new file mode 100644 index 00000000..6620a4ac --- /dev/null +++ b/docs/source/topics/monoforum.rst @@ -0,0 +1,53 @@ +Monoforum +========= + +Monoforum is a feature that allows channels to have a single-thread discussion area, similar to a simplified forum. + +Checking Monoforum Status +------------------------- + +Check if a channel has monoforum enabled: + +.. code-block:: python + + from pyrogram import Client + + app = Client("my_account") + + + async def main(): + chat = await app.get_chat("channel_username") + + if chat.monoforum: + print("This channel is a Monoforum") + if chat.linked_monoforum_id: + print(f"Linked Monoforum ID: {chat.linked_monoforum_id}") + + + with app: + app.loop.run_until_complete(main()) + +Handling Monoforum Updates +-------------------------- + +Handle monoforum read history updates: + +.. code-block:: python + + from pyrogram import Client + + app = Client("my_account") + + + @app.on_raw_update() + async def monoforum_update_handler(client, update, users, chats): + from pyrogram.raw.types import UpdateReadMonoForumInbox, UpdateReadMonoForumOutbox + + if isinstance(update, UpdateReadMonoForumInbox): + print(f"Monoforum inbox read up to {update.read_max_id}") + + if isinstance(update, UpdateReadMonoForumOutbox): + print(f"Monoforum outbox read up to {update.read_max_id}") + + + app.run() diff --git a/docs/source/topics/privacy-settings.rst b/docs/source/topics/privacy-settings.rst new file mode 100644 index 00000000..3c252094 --- /dev/null +++ b/docs/source/topics/privacy-settings.rst @@ -0,0 +1,48 @@ +Privacy Settings +================ + +Telegram has introduced new privacy keys and values to give users more control over their information. + +New Privacy Keys +---------------- + +You can now manage privacy for the following categories: + +- **About**: Control who can see your bio/about text. +- **Birthday**: Control who can see your birthday. +- **Star Gifts**: Control who can see your received star gifts on your profile. +- **Paid Messages**: Control who can send you paid messages. +- **Saved Music**: Control who can see your saved music. + +.. code-block:: python + + from pyrogram import Client, enums + + app = Client("my_account") + + async def main(): + # Get privacy for birthday + privacy = await app.get_privacy(enums.PrivacyKeyType.BIRTHDAY) + print(f"Birthday privacy: {privacy}") + + app.run(main()) + +New Privacy Rules +----------------- + +New values are available to define more granular permissions: + +- **Allow Close Friends**: Only users in your close friends list. +- **Allow Premium**: Only Telegram Premium users. +- **Allow Bots / Disallow Bots**: Specifically allow or disallow all bots. + +.. code-block:: python + + from pyrogram import Client, enums, raw + + async def main(): + # Allow only premium users to send you messages + await app.set_privacy( + enums.PrivacyKeyType.CHAT_INVITE, + [raw.types.InputPrivacyValueAllowPremium()] + ) diff --git a/docs/source/topics/saved-messages.rst b/docs/source/topics/saved-messages.rst new file mode 100644 index 00000000..f4a69842 --- /dev/null +++ b/docs/source/topics/saved-messages.rst @@ -0,0 +1,77 @@ +Saved Messages Improvements +=========================== + +Telegram has enhanced Saved Messages with peer-based organization and reaction tags. + +Organizing by Peer +------------------ + +Retrieve Saved Messages dialogs organized by the original peer: + +.. code-block:: python + + from pyrogram import Client + + app = Client("my_account") + + + async def main(): + saved_dialogs = await app.get_saved_dialogs() + + for dialog in saved_dialogs.dialogs: + print(f"Saved from: {dialog.peer.title or dialog.peer.first_name}") + print(f"Last message: {dialog.top_message.text}") + + + with app: + app.loop.run_until_complete(main()) + +Reaction Tags +------------- + +Manage reaction tags used to categorize your saved messages: + +.. code-block:: python + + from pyrogram import Client + + app = Client("my_account") + + + async def main(): + tags = await app.get_saved_reaction_tags() + + for tag in tags: + print(f"Tag: {tag.reaction} (used in {tag.count} messages)") + if tag.title: + print(f"Custom name: {tag.title}") + + # Set a custom name for a reaction tag + await app.update_saved_reaction_tag("❀️", title="Favorites") + + + with app: + app.loop.run_until_complete(main()) + +Pinning Saved Dialogs +--------------------- + +Pin important peer-folders in your Saved Messages: + +.. code-block:: python + + from pyrogram import Client + + app = Client("my_account") + + + async def main(): + # Pin a user folder in saved messages + await app.pin_saved_dialog("username") + + # Get only pinned folders + pinned = await app.get_pinned_saved_dialogs() + + + with app: + app.loop.run_until_complete(main()) diff --git a/docs/source/topics/star-gifts.rst b/docs/source/topics/star-gifts.rst new file mode 100644 index 00000000..41201c46 --- /dev/null +++ b/docs/source/topics/star-gifts.rst @@ -0,0 +1,79 @@ +Star Gifts +========== + +Telegram Star Gifts allow users to send unique stickers and collectibles using Telegram Stars. + +Handling Star Gifts +------------------- + +Receive and parse star gift service messages: + +.. code-block:: python + + from pyrogram import Client, enums + + app = Client("my_account") + + + @app.on_message() + async def star_gift_handler(client, message): + if message.service == enums.MessageServiceType.STAR_GIFT: + gift = message.star_gift + print(f"Received gift: {gift.title}") + print(f"Price: {gift.stars} stars") + print(f"Convert value: {gift.convert_stars} stars") + + if message.service == enums.MessageServiceType.STAR_GIFT_UNIQUE: + unique_gift = message.star_gift_unique + print(f"Received unique gift: {unique_gift.title} #{unique_gift.num}") + print(f"Slug: {unique_gift.slug}") + + + app.run() + +Getting Available Gifts +----------------------- + +Get a list of gifts available for purchase: + +.. code-block:: python + + from pyrogram import Client + + app = Client("my_account") + + + async def main(): + gifts = await app.get_star_gifts() + + for gift in gifts: + print(f"Gift: {gift.title} ({gift.stars} Stars)") + if gift.limited: + print(f"Limited: {gift.availability_remains}/{gift.availability_total}") + + + with app: + app.loop.run_until_complete(main()) + +Managing Saved Gifts +-------------------- + +Retrieve and manage gifts saved on your profile: + +.. code-block:: python + + from pyrogram import Client + + app = Client("my_account") + + + async def main(): + # Get your own saved gifts + saved_gifts = await app.get_saved_star_gifts("me") + + for gift in saved_gifts: + print(f"Saved unique gift: {gift.title} #{gift.num}") + + + with app: + app.loop.run_until_complete(main()) diff --git a/docs/source/topics/stories.rst b/docs/source/topics/stories.rst new file mode 100644 index 00000000..7684025e --- /dev/null +++ b/docs/source/topics/stories.rst @@ -0,0 +1,111 @@ +Stories +======= + +Pyrogram supports Telegram Stories, allowing you to create, view, and interact with stories. + +Receiving Stories +----------------- + +You can handle story updates using the :class:`~pyrogram.handlers.StoryHandler`: + +.. code-block:: python + + from pyrogram import Client + + app = Client("my_account") + + + @app.on_story() + async def story_handler(client, story): + print(f"New story from {story.chat.title}: {story.caption}") + + + app.run() + +Sending Stories +--------------- + +Send a photo or video story: + +.. code-block:: python + + from pyrogram import Client + + app = Client("my_account") + + + async def main(): + await app.send_story( + "me", + "photo.jpg", + caption="Check out my new story!" + ) + + + with app: + app.loop.run_until_complete(main()) + +Getting Stories +--------------- + +Retrieve stories from a chat: + +.. code-block:: python + + from pyrogram import Client + + app = Client("my_account") + + + async def main(): + stories = await app.get_stories("username") + + for story in stories: + print(f"Story ID: {story.id}") + print(f"Caption: {story.caption}") + + + with app: + app.loop.run_until_complete(main()) + +Deleting Stories +---------------- + +Delete your own stories: + +.. code-block:: python + + from pyrogram import Client + + app = Client("my_account") + + + async def main(): + await app.delete_stories("me", [story_id1, story_id2]) + + + with app: + app.loop.run_until_complete(main()) + +Story Privacy +------------- + +Control who can see your stories: + +.. code-block:: python + + from pyrogram import Client, enums + + app = Client("my_account") + + + async def main(): + await app.send_story( + "me", + "photo.jpg", + privacy=enums.StoryPrivacyType.CONTACTS + ) + + + with app: + app.loop.run_until_complete(main()) diff --git a/docs/source/topics/suggested-posts.rst b/docs/source/topics/suggested-posts.rst new file mode 100644 index 00000000..b235c224 --- /dev/null +++ b/docs/source/topics/suggested-posts.rst @@ -0,0 +1,54 @@ +Suggested Posts +=============== + +Channels can have suggested posts that administrators can approve, edit, or refund. + +Handling Suggested Posts +------------------------ + +Suggested posts appear in the :attr:`~pyrogram.types.Message.suggested_post` attribute: + +.. code-block:: python + + from pyrogram import Client + + app = Client("my_account") + + + @app.on_message() + async def suggested_post_handler(client, message): + if message.suggested_post: + print(f"New suggested post in {message.chat.title}") + print(f"Suggested by: {message.suggested_post.user.first_name}") + + if message.suggested_post.stars: + print(f"Price: {message.suggested_post.stars.amount} Stars") + + + app.run() + +Service Messages +---------------- + +Handle suggested post lifecycle events: + +.. code-block:: python + + from pyrogram import Client, enums + + app = Client("my_account") + + + @app.on_message() + async def suggested_post_service_handler(client, message): + if message.service == enums.MessageServiceType.SUGGESTED_POST_APPROVAL: + print("A suggested post was approved!") + + if message.service == enums.MessageServiceType.SUGGESTED_POST_SUCCESS: + print("A suggested post was successfully published!") + + if message.service == enums.MessageServiceType.SUGGESTED_POST_REFUND: + print("A suggested post was refunded.") + + + app.run() diff --git a/docs/source/topics/telegram-stars.rst b/docs/source/topics/telegram-stars.rst new file mode 100644 index 00000000..d1d65ab1 --- /dev/null +++ b/docs/source/topics/telegram-stars.rst @@ -0,0 +1,93 @@ +Telegram Stars +============== + +Telegram Stars is a virtual currency system for paid content and monetization. + +Checking Stars Balance +---------------------- + +Get your current Stars balance: + +.. code-block:: python + + from pyrogram import Client + + app = Client("my_account") + + + async def main(): + status = await app.get_stars_status() + print(f"Balance: {status.balance.amount} stars") + + + with app: + app.loop.run_until_complete(main()) + +Getting Transactions +-------------------- + +View Stars transaction history: + +.. code-block:: python + + from pyrogram import Client + + app = Client("my_account") + + + async def main(): + transactions = await app.get_stars_transactions() + + for transaction in transactions: + print(f"Amount: {transaction.stars.amount}") + print(f"Date: {transaction.date}") + + + with app: + app.loop.run_until_complete(main()) + +Handling Paid Media +------------------- + +Messages with paid media can be accessed through the :attr:`~pyrogram.types.Message.paid_media` attribute: + +.. code-block:: python + + from pyrogram import Client + + app = Client("my_account") + + + @app.on_message() + async def handle_paid_media(client, message): + if message.paid_media: + print(f"Paid media costs {message.paid_media.stars_amount} stars") + + for media in message.paid_media.extended_media: + if isinstance(media, dict) and media.get("type") == "preview": + print("Preview only - purchase to see full media") + else: + print(f"Media: {media}") + + + app.run() + +Star Gifts +---------- + +Handle star gift messages: + +.. code-block:: python + + from pyrogram import Client, enums + + app = Client("my_account") + + + @app.on_message() + async def handle_star_gift(client, message): + if message.service == enums.MessageServiceType.STAR_GIFT: + print(f"Received a star gift in chat {message.chat.id}") + + + app.run() diff --git a/docs/source/topics/todo-lists.rst b/docs/source/topics/todo-lists.rst new file mode 100644 index 00000000..4dc521c6 --- /dev/null +++ b/docs/source/topics/todo-lists.rst @@ -0,0 +1,87 @@ +To-Do Lists +=========== + +Telegram supports collaborative to-do lists in chats. + +Handling To-Do Messages +------------------------ + +Receive and display to-do lists in messages: + +.. code-block:: python + + from pyrogram import Client + + app = Client("my_account") + + + @app.on_message() + async def todo_handler(client, message): + if message.todo: + print(f"To-Do List: {message.todo.title}") + print(f"Total items: {message.todo.total_count}") + + for item in message.todo.items: + status = "βœ“" if item.is_checked else "β—‹" + print(f"{status} {item.text}") + + if message.todo.completions: + print(f"Completed: {message.todo.completed_count}") + + + app.run() + +Sending To-Do Lists +------------------- + +Send a to-do list to a chat: + +.. code-block:: python + + from pyrogram import Client + from pyrogram.types import InputMediaTodo + + app = Client("my_account") + + + async def main(): + await app.send_media_group( + "chat_id", + media=[ + InputMediaTodo( + title="Shopping List", + items=[ + "Milk", + "Bread", + "Eggs" + ] + ) + ] + ) + + + with app: + app.loop.run_until_complete(main()) + +To-Do Service Messages +----------------------- + +Handle to-do completion and task addition service messages: + +.. code-block:: python + + from pyrogram import Client, enums + + app = Client("my_account") + + + @app.on_message() + async def todo_service_handler(client, message): + if message.service == enums.MessageServiceType.TODO_COMPLETIONS: + print("Tasks completed!") + + if message.service == enums.MessageServiceType.TODO_APPEND_TASKS: + print("New tasks added!") + + + app.run() diff --git a/pyrogram/connection/transport/tcp/tcp.py b/pyrogram/connection/transport/tcp/tcp.py index 89131236..8d34816c 100644 --- a/pyrogram/connection/transport/tcp/tcp.py +++ b/pyrogram/connection/transport/tcp/tcp.py @@ -53,7 +53,6 @@ def __init__(self, ipv6: bool, proxy: Proxy) -> None: self.writer: Optional[asyncio.StreamWriter] = None self.lock = asyncio.Lock() - self.loop = asyncio.get_event_loop() async def _connect_via_proxy( self, @@ -91,7 +90,7 @@ async def _connect_via_proxy( ) sock.settimeout(TCP.TIMEOUT) - await self.loop.sock_connect( + await asyncio.get_running_loop().sock_connect( sock=sock, address=destination ) diff --git a/pyrogram/dispatcher.py b/pyrogram/dispatcher.py index e9174b69..66799e12 100644 --- a/pyrogram/dispatcher.py +++ b/pyrogram/dispatcher.py @@ -26,7 +26,8 @@ from pyrogram.handlers import ( CallbackQueryHandler, MessageHandler, EditedMessageHandler, DeletedMessagesHandler, UserStatusHandler, RawUpdateHandler, InlineQueryHandler, PollHandler, - ChosenInlineResultHandler, ChatMemberUpdatedHandler, ChatJoinRequestHandler + ChosenInlineResultHandler, ChatMemberUpdatedHandler, ChatJoinRequestHandler, + StoryHandler, ChatBoostHandler, BusinessConnectionHandler ) from pyrogram.raw.types import ( UpdateNewMessage, UpdateNewChannelMessage, UpdateNewScheduledMessage, @@ -35,23 +36,36 @@ UpdateBotCallbackQuery, UpdateInlineBotCallbackQuery, UpdateUserStatus, UpdateBotInlineQuery, UpdateMessagePoll, UpdateBotInlineSend, UpdateChatParticipant, UpdateChannelParticipant, - UpdateBotChatInviteRequester + UpdateBotChatInviteRequester, UpdateStory, UpdateBotNewBusinessMessage, + UpdateBotEditBusinessMessage, UpdateBotDeleteBusinessMessage, + UpdateBotChatBoost, UpdateBotBusinessConnect, UpdateBusinessBotCallbackQuery, + UpdateReadMonoForumInbox, UpdateReadMonoForumOutbox, UpdateMonoForumNoPaidException, + UpdateTranscribeAudio, UpdateEmojiGameInfo, UpdateStarGiftAuctionState, + UpdateStarGiftAuctionUserState, UpdateStarGiftCraftFail, UpdateQuickReplies, + UpdateNewQuickReply, UpdateDeleteQuickReply, UpdateQuickReplyMessage, + UpdateDeleteQuickReplyMessages, UpdateSavedDialogPinned, UpdatePinnedSavedDialogs, + UpdateSavedReactionTags, UpdateReadStories, UpdateStoryID, UpdateStoriesStealthMode, + UpdateSentStoryReaction, UpdateNewStoryReaction, UpdateStarsBalance, + UpdateStarsRevenueStatus, UpdateBotPurchasedPaidMedia, UpdatePaidReactionPrivacy ) log = logging.getLogger(__name__) class Dispatcher: - NEW_MESSAGE_UPDATES = (UpdateNewMessage, UpdateNewChannelMessage, UpdateNewScheduledMessage) - EDIT_MESSAGE_UPDATES = (UpdateEditMessage, UpdateEditChannelMessage) - DELETE_MESSAGES_UPDATES = (UpdateDeleteMessages, UpdateDeleteChannelMessages) - CALLBACK_QUERY_UPDATES = (UpdateBotCallbackQuery, UpdateInlineBotCallbackQuery) + NEW_MESSAGE_UPDATES = (UpdateNewMessage, UpdateNewChannelMessage, UpdateNewScheduledMessage, UpdateBotNewBusinessMessage) + EDIT_MESSAGE_UPDATES = (UpdateEditMessage, UpdateEditChannelMessage, UpdateBotEditBusinessMessage) + DELETE_MESSAGES_UPDATES = (UpdateDeleteMessages, UpdateDeleteChannelMessages, UpdateBotDeleteBusinessMessage) + CALLBACK_QUERY_UPDATES = (UpdateBotCallbackQuery, UpdateInlineBotCallbackQuery, UpdateBusinessBotCallbackQuery) CHAT_MEMBER_UPDATES = (UpdateChatParticipant, UpdateChannelParticipant) USER_STATUS_UPDATES = (UpdateUserStatus,) BOT_INLINE_QUERY_UPDATES = (UpdateBotInlineQuery,) POLL_UPDATES = (UpdateMessagePoll,) CHOSEN_INLINE_RESULT_UPDATES = (UpdateBotInlineSend,) CHAT_JOIN_REQUEST_UPDATES = (UpdateBotChatInviteRequester,) + STORY_UPDATES = (UpdateStory,) + CHAT_BOOST_UPDATES = (UpdateBotChatBoost,) + BUSINESS_CONNECTION_UPDATES = (UpdateBotBusinessConnect,) def __init__(self, client: "pyrogram.Client"): self.client = client @@ -127,6 +141,27 @@ async def chat_join_request_parser(update, users, chats): ChatJoinRequestHandler ) + async def story_parser(update, users, chats): + from pyrogram.raw.types import StoryItem + if isinstance(update.story, StoryItem): + return ( + await pyrogram.types.StoryItem._parse(self.client, update.story, users, chats, update.peer), + StoryHandler + ) + return (None, type(None)) + + async def chat_boost_parser(update, users, chats): + return ( + await pyrogram.types.ChatBoostUpdated._parse(self.client, update, users, chats), + ChatBoostHandler + ) + + async def business_connection_parser(update, users, chats): + return ( + await pyrogram.types.BusinessConnection._parse(self.client, update.connection), + BusinessConnectionHandler + ) + self.update_parsers = { Dispatcher.NEW_MESSAGE_UPDATES: message_parser, Dispatcher.EDIT_MESSAGE_UPDATES: edited_message_parser, @@ -137,7 +172,10 @@ async def chat_join_request_parser(update, users, chats): Dispatcher.POLL_UPDATES: poll_parser, Dispatcher.CHOSEN_INLINE_RESULT_UPDATES: chosen_inline_result_parser, Dispatcher.CHAT_MEMBER_UPDATES: chat_member_updated_parser, - Dispatcher.CHAT_JOIN_REQUEST_UPDATES: chat_join_request_parser + Dispatcher.CHAT_JOIN_REQUEST_UPDATES: chat_join_request_parser, + Dispatcher.STORY_UPDATES: story_parser, + Dispatcher.CHAT_BOOST_UPDATES: chat_boost_parser, + Dispatcher.BUSINESS_CONNECTION_UPDATES: business_connection_parser } self.update_parsers = {key: value for key_tuple, value in self.update_parsers.items() for key in key_tuple} diff --git a/pyrogram/enums/__init__.py b/pyrogram/enums/__init__.py index d19c7044..d5d445af 100644 --- a/pyrogram/enums/__init__.py +++ b/pyrogram/enums/__init__.py @@ -16,6 +16,8 @@ # You should have received a copy of the GNU Lesser General Public License # along with Pyrogram. If not, see . +from .boost_type import BoostType +from .business_away_message_schedule import BusinessAwayMessageSchedule from .chat_action import ChatAction from .chat_event_action import ChatEventAction from .chat_member_status import ChatMemberStatus @@ -28,10 +30,14 @@ from .next_code_type import NextCodeType from .parse_mode import ParseMode from .poll_type import PollType +from .privacy_key_type import PrivacyKeyType from .sent_code_type import SentCodeType +from .story_privacy_type import StoryPrivacyType from .user_status import UserStatus __all__ = [ + 'BoostType', + 'BusinessAwayMessageSchedule', 'ChatAction', 'ChatEventAction', 'ChatMemberStatus', @@ -44,6 +50,8 @@ 'NextCodeType', 'ParseMode', 'PollType', + 'PrivacyKeyType', 'SentCodeType', + 'StoryPrivacyType', 'UserStatus' ] diff --git a/pyrogram/enums/boost_type.py b/pyrogram/enums/boost_type.py new file mode 100644 index 00000000..a55db8db --- /dev/null +++ b/pyrogram/enums/boost_type.py @@ -0,0 +1,35 @@ +# 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 .auto_name import AutoName + + +class BoostType(AutoName): + """Boost type enumeration.""" + + PREMIUM = "premium" + """Boost from a Premium subscription.""" + + GIFT = "gift" + """Boost from a gift.""" + + GIVEAWAY = "giveaway" + """Boost from a giveaway.""" + + STARS = "stars" + """Boost from Telegram Stars.""" diff --git a/pyrogram/enums/business_away_message_schedule.py b/pyrogram/enums/business_away_message_schedule.py new file mode 100644 index 00000000..98fc9fd2 --- /dev/null +++ b/pyrogram/enums/business_away_message_schedule.py @@ -0,0 +1,32 @@ +# 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 .auto_name import AutoName + + +class BusinessAwayMessageSchedule(AutoName): + """Business away message schedule type enumeration.""" + + ALWAYS = "always" + """Away message is always active.""" + + OUTSIDE_WORK_HOURS = "outside_work_hours" + """Away message is active outside work hours.""" + + CUSTOM = "custom" + """Away message follows custom schedule.""" diff --git a/pyrogram/enums/message_media_type.py b/pyrogram/enums/message_media_type.py index 42575e72..07767c4c 100644 --- a/pyrogram/enums/message_media_type.py +++ b/pyrogram/enums/message_media_type.py @@ -74,3 +74,12 @@ class MessageMediaType(AutoName): GIVEAWAY = auto() "Giveaway media" + + GIVEAWAY_RESULTS = auto() + "Giveaway results media" + + PAID_MEDIA = auto() + "Paid media" + + TODO = auto() + "To-do list media" diff --git a/pyrogram/enums/message_service_type.py b/pyrogram/enums/message_service_type.py index f5d7792b..59332dbb 100644 --- a/pyrogram/enums/message_service_type.py +++ b/pyrogram/enums/message_service_type.py @@ -83,3 +83,54 @@ class MessageServiceType(AutoName): CHANGE_CREATOR = auto() "Creator changed" + + GIFT_CODE = auto() + "Gift code" + + GIVEAWAY_LAUNCH = auto() + "Giveaway launched" + + GIVEAWAY_RESULTS = auto() + "Giveaway results" + + BOOST_APPLY = auto() + "Boost apply" + + TODO_COMPLETIONS = auto() + "Todo completions" + + TODO_APPEND_TASKS = auto() + "Todo append tasks" + + CONFERENCE_CALL = auto() + "Conference call" + + GIFT_STARS = auto() + "Gift stars" + + PRIZE_STARS = auto() + "Prize stars" + + STAR_GIFT_PURCHASE_OFFER = auto() + "Star gift purchase offer" + + STAR_GIFT_PURCHASE_OFFER_DECLINED = auto() + "Star gift purchase offer declined" + + SUGGESTED_POST_APPROVAL = auto() + "Suggested post approval" + + SUGGESTED_POST_SUCCESS = auto() + "Suggested post success" + + SUGGESTED_POST_REFUND = auto() + "Suggested post refund" + + PAID_MESSAGES_REFUNDED = auto() + "Paid messages refunded" + + PAID_MESSAGES_PRICE = auto() + "Paid messages price" + + SUGGEST_BIRTHDAY = auto() + "Suggest birthday" diff --git a/pyrogram/enums/privacy_key_type.py b/pyrogram/enums/privacy_key_type.py new file mode 100644 index 00000000..64b36170 --- /dev/null +++ b/pyrogram/enums/privacy_key_type.py @@ -0,0 +1,68 @@ +# 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 .auto_name import AutoName + + +class PrivacyKeyType(AutoName): + """Privacy key type enumeration.""" + + STATUS_TIMESTAMP = "status_timestamp" + """Privacy for last seen timestamp.""" + + CHAT_INVITE = "chat_invite" + """Privacy for group/channel invitations.""" + + PHONE_CALL = "phone_call" + """Privacy for voice/video calls.""" + + PHONE_P2P = "phone_p2p" + """Privacy for P2P calls.""" + + FORWARDS = "forwards" + """Privacy for forwarded messages.""" + + PROFILE_PHOTO = "profile_photo" + """Privacy for profile photo.""" + + PHONE_NUMBER = "phone_number" + """Privacy for phone number.""" + + ADDED_BY_PHONE = "added_by_phone" + """Privacy for being added by phone number.""" + + VOICE_MESSAGES = "voice_messages" + """Privacy for voice messages.""" + + ABOUT = "about" + """Privacy for bio/about.""" + + BIRTHDAY = "birthday" + """Privacy for birthday.""" + + GIFTS = "gifts" + """Privacy for star gifts.""" + + STARREF_LINKS = "starref_links" + """Privacy for star referral links.""" + + NO_PAID_MESSAGES = "no_paid_messages" + """Privacy for receiving paid messages.""" + + SAVED_MUSIC = "saved_music" + """Privacy for who can see your saved music.""" diff --git a/pyrogram/enums/story_privacy_type.py b/pyrogram/enums/story_privacy_type.py new file mode 100644 index 00000000..6e453240 --- /dev/null +++ b/pyrogram/enums/story_privacy_type.py @@ -0,0 +1,35 @@ +# 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 .auto_name import AutoName + + +class StoryPrivacyType(AutoName): + """Story privacy type enumeration.""" + + PUBLIC = "public" + """Story visible to everyone.""" + + CONTACTS = "contacts" + """Story visible to contacts only.""" + + CLOSE_FRIENDS = "close_friends" + """Story visible to close friends only.""" + + SELECTED = "selected" + """Story visible to selected users only.""" diff --git a/pyrogram/filters.py b/pyrogram/filters.py index 6504d78d..21d19e30 100644 --- a/pyrogram/filters.py +++ b/pyrogram/filters.py @@ -334,6 +334,54 @@ async def giveaway_filter(_, __, m: Message): """Filter messages that contain :obj:`~pyrogram.types.Giveaway` objects.""" +# endregion + + +# region story_filter +async def story_filter(_, __, m: Message): + return bool(m.story) + + +story = create(story_filter) +"""Filter messages that contain :obj:`~pyrogram.types.StoryItem` objects.""" + + +# endregion + + +# region paid_media_filter +async def paid_media_filter(_, __, m: Message): + return bool(m.paid_media) + + +paid_media = create(paid_media_filter) +"""Filter messages that contain :obj:`~pyrogram.types.PaidMedia` objects.""" + + +# endregion + + +# region todo_filter +async def todo_filter(_, __, m: Message): + return bool(m.todo) + + +todo = create(todo_filter) +"""Filter messages that contain :obj:`~pyrogram.types.TodoList` objects.""" + + +# endregion + + +# region business_filter +async def business_filter(_, __, m: Message): + return bool(getattr(m, "via_business_bot_id", None) or getattr(m, "quick_reply_shortcut_id", None)) + + +business = create(business_filter) +"""Filter messages related to Telegram Business (sent via business bot or quick reply).""" + + # endregion # region video_filter diff --git a/pyrogram/handlers/__init__.py b/pyrogram/handlers/__init__.py index 1c762958..c6eec281 100644 --- a/pyrogram/handlers/__init__.py +++ b/pyrogram/handlers/__init__.py @@ -27,4 +27,25 @@ from .message_handler import MessageHandler from .poll_handler import PollHandler from .raw_update_handler import RawUpdateHandler +from .story_handler import StoryHandler from .user_status_handler import UserStatusHandler +from .chat_boost_handler import ChatBoostHandler +from .business_connection_handler import BusinessConnectionHandler + +__all__ = [ + "CallbackQueryHandler", + "ChatJoinRequestHandler", + "ChatMemberUpdatedHandler", + "ChosenInlineResultHandler", + "DeletedMessagesHandler", + "DisconnectHandler", + "EditedMessageHandler", + "InlineQueryHandler", + "MessageHandler", + "PollHandler", + "RawUpdateHandler", + "StoryHandler", + "UserStatusHandler", + "ChatBoostHandler", + "BusinessConnectionHandler" +] diff --git a/pyrogram/handlers/business_connection_handler.py b/pyrogram/handlers/business_connection_handler.py new file mode 100644 index 00000000..065b79a7 --- /dev/null +++ b/pyrogram/handlers/business_connection_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 BusinessConnectionHandler(Handler): + """The BusinessConnection handler class. Used to handle business connection updates. + 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_business_connection` decorator. + + Parameters: + callback (``Callable``): + Pass a function that will be called when a new BusinessConnection arrives. It takes *(client, business_connection)* + 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 updates 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 handler. + + business_connection (:obj:`~pyrogram.types.BusinessConnection`): + The received business connection. + """ + + def __init__(self, callback: Callable, filters=None): + super().__init__(callback, filters) diff --git a/pyrogram/handlers/chat_boost_handler.py b/pyrogram/handlers/chat_boost_handler.py new file mode 100644 index 00000000..b2b70736 --- /dev/null +++ b/pyrogram/handlers/chat_boost_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 ChatBoostHandler(Handler): + """The ChatBoost handler class. Used to handle new chat boosts. + 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_chat_boost` decorator. + + Parameters: + callback (``Callable``): + Pass a function that will be called when a new ChatBoost arrives. It takes *(client, chat_boost)* + 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 updates 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 handler. + + chat_boost (:obj:`~pyrogram.types.ChatBoostUpdated`): + The received chat boost update. + """ + + 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 00000000..75892039 --- /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 or updated 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 Story 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.StoryItem`): + 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 ea71f6b1..04025a7a 100644 --- a/pyrogram/methods/__init__.py +++ b/pyrogram/methods/__init__.py @@ -18,13 +18,18 @@ from .advanced import Advanced from .auth import Auth +from .boosts import Boosts from .bots import Bots +from .business import Business from .chats import Chats from .contacts import Contacts from .decorators import Decorators from .invite_links import InviteLinks from .messages import Messages from .password import Password +from .payments import Payments +from .stories import Stories +from .todo import Todo from .users import Users from .utilities import Utilities @@ -32,7 +37,9 @@ class Methods( Advanced, Auth, + Boosts, Bots, + Business, Contacts, Password, Chats, @@ -41,5 +48,8 @@ class Methods( Decorators, Utilities, InviteLinks, + Payments, + Stories, + Todo, ): pass diff --git a/pyrogram/methods/boosts/__init__.py b/pyrogram/methods/boosts/__init__.py new file mode 100644 index 00000000..d1663ce0 --- /dev/null +++ b/pyrogram/methods/boosts/__init__.py @@ -0,0 +1,33 @@ +# 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_boost_status import GetBoostStatus +from .get_boosts_list import GetBoostsList +from .apply_boost import ApplyBoost +from .get_boost_level_options import GetBoostLevelOptions +from .get_my_boosts import GetMyBoosts + + +class Boosts( + GetBoostStatus, + GetBoostsList, + ApplyBoost, + GetBoostLevelOptions, + GetMyBoosts +): + pass diff --git a/pyrogram/methods/boosts/apply_boost.py b/pyrogram/methods/boosts/apply_boost.py new file mode 100644 index 00000000..8ed764d6 --- /dev/null +++ b/pyrogram/methods/boosts/apply_boost.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, List, Optional + +import pyrogram +from pyrogram import raw, types + + +class ApplyBoost: + async def apply_boost( + self: "pyrogram.Client", + chat_id: Union[int, str], + slots: List[int] = None + ) -> "types.BoostStatus": + """Apply a boost to a channel. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target channel. + You can also use channel public link in form of *t.me/* (str). + + slots (List of ``int``, *optional*): + Which of your boost slots to reassign to this channel. + If not provided, the first available slot will be used. + + Returns: + :obj:`~pyrogram.types.BoostStatus`: The updated boost status. + + Example: + .. code-block:: python + + # Apply a boost to a channel + status = await app.apply_boost(chat_id) + print(f"New level: {status.level}") + """ + peer = await self.resolve_peer(chat_id) + + r = await self.invoke( + raw.functions.premium.ApplyBoost( + peer=peer, + slots=slots + ) + ) + + return types.BoostStatus._parse(self, r) diff --git a/pyrogram/methods/boosts/get_boost_level_options.py b/pyrogram/methods/boosts/get_boost_level_options.py new file mode 100644 index 00000000..19644fe7 --- /dev/null +++ b/pyrogram/methods/boosts/get_boost_level_options.py @@ -0,0 +1,44 @@ +# 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 GetBoostLevelOptions: + async def get_boost_level_options( + self: "pyrogram.Client", + chat_id: Union[int, str] + ) -> "types.BoostLevelOptions": + """Get the boost level options for a channel. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target channel. + + Returns: + :obj:`~pyrogram.types.BoostLevelOptions`: The boost level options. + """ + rpc = raw.functions.payments.GetBoostLevelOptions( + peer=await self.resolve_peer(chat_id) + ) + r = await self.invoke(rpc) + + return types.BoostLevelOptions._parse(self, r) diff --git a/pyrogram/methods/boosts/get_boost_status.py b/pyrogram/methods/boosts/get_boost_status.py new file mode 100644 index 00000000..f5827614 --- /dev/null +++ b/pyrogram/methods/boosts/get_boost_status.py @@ -0,0 +1,56 @@ +# 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 GetBoostStatus: + async def get_boost_status( + self: "pyrogram.Client", + chat_id: Union[int, str] + ) -> "types.BoostStatus": + """Get boost status for a channel. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target channel. + You can also use channel public link in form of *t.me/* (str). + + Returns: + :obj:`~pyrogram.types.BoostStatus`: On success, the boost status is returned. + + Example: + .. code-block:: python + + status = await app.get_boost_status(chat_id) + print(f"Level: {status.level}, Boosts: {status.boosts}") + """ + peer = await self.resolve_peer(chat_id) + + r = await self.invoke( + raw.functions.premium.GetBoostsStatus( + peer=peer + ) + ) + + return types.BoostStatus._parse(self, r) diff --git a/pyrogram/methods/boosts/get_boosts_list.py b/pyrogram/methods/boosts/get_boosts_list.py new file mode 100644 index 00000000..b82518dd --- /dev/null +++ b/pyrogram/methods/boosts/get_boosts_list.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, List, AsyncGenerator, Optional + +import pyrogram +from pyrogram import raw, types + + +class GetBoostsList: + async def get_boosts_list( + self: "pyrogram.Client", + chat_id: Union[int, str], + limit: int = 0, + offset: str = "" + ) -> AsyncGenerator["types.Boost", None]: + """Get the list of boosts for a channel. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target channel. + You can also use channel public link in form of *t.me/* (str). + + limit (``int``, *optional*): + Limits the number of boosts to be retrieved. + By default, no limit is applied and all boosts are returned. + + offset (``str``, *optional*): + Offset for pagination. + + Returns: + ``Generator``: A generator yielding :obj:`~pyrogram.types.Boost` objects. + + Example: + .. code-block:: python + + async for boost in app.get_boosts_list(chat_id): + print(boost.user) + """ + peer = await self.resolve_peer(chat_id) + current = 0 + total = abs(limit) or (1 << 31) - 1 + limit = min(100, total) + + while True: + r: raw.types.premium.BoostsList = await self.invoke( + raw.functions.premium.GetBoostsList( + peer=peer, + offset=offset, + limit=limit + ) + ) + + users = {u.id: u for u in r.users} + + boosts = types.Boost._parse_list(self, r.boosts, users) + + if not boosts: + return + + for boost in boosts: + yield boost + + current += 1 + + if current >= total: + return + + offset = r.next_offset + + if not offset: + return diff --git a/pyrogram/methods/boosts/get_my_boosts.py b/pyrogram/methods/boosts/get_my_boosts.py new file mode 100644 index 00000000..1a9c2a86 --- /dev/null +++ b/pyrogram/methods/boosts/get_my_boosts.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 . + +import pyrogram +from pyrogram import raw, types + +class GetMyBoosts: + async def get_my_boosts( + self: "pyrogram.Client" + ) -> "types.MyBoosts": + """Get the list of your own boosts. + + .. include:: /_includes/usable-by/users.rst + + Returns: + :obj:`~pyrogram.types.MyBoosts`: The list of your boosts. + """ + r = await self.invoke(raw.functions.premium.GetMyBoosts()) + return types.MyBoosts._parse(self, r) diff --git a/pyrogram/methods/business/__init__.py b/pyrogram/methods/business/__init__.py new file mode 100644 index 00000000..f31140ba --- /dev/null +++ b/pyrogram/methods/business/__init__.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 . + +from .get_business_chat_links import GetBusinessChatLinks +from .create_business_chat_link import CreateBusinessChatLink +from .edit_business_chat_link import EditBusinessChatLink +from .delete_business_chat_link import DeleteBusinessChatLink +from .get_bot_business_connection import GetBotBusinessConnection +from .get_quick_replies import GetQuickReplies +from .get_quick_reply_messages import GetQuickReplyMessages +from .edit_quick_reply_shortcut import EditQuickReplyShortcut +from .delete_quick_reply_shortcut import DeleteQuickReplyShortcut +from .send_quick_reply_messages import SendQuickReplyMessages +from .check_quick_reply_shortcut import CheckQuickReplyShortcut +from .delete_quick_reply_messages import DeleteQuickReplyMessages + +class Business( + GetBusinessChatLinks, + CreateBusinessChatLink, + EditBusinessChatLink, + DeleteBusinessChatLink, + GetBotBusinessConnection, + GetQuickReplies, + GetQuickReplyMessages, + EditQuickReplyShortcut, + DeleteQuickReplyShortcut, + SendQuickReplyMessages, + CheckQuickReplyShortcut, + DeleteQuickReplyMessages +): + pass diff --git a/pyrogram/methods/business/check_quick_reply_shortcut.py b/pyrogram/methods/business/check_quick_reply_shortcut.py new file mode 100644 index 00000000..7be7513e --- /dev/null +++ b/pyrogram/methods/business/check_quick_reply_shortcut.py @@ -0,0 +1,43 @@ +# 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 CheckQuickReplyShortcut: + async def check_quick_reply_shortcut( + self: "pyrogram.Client", + shortcut: str + ) -> bool: + """Check if a quick reply shortcut name is available. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + shortcut (``str``): + The shortcut name to check. + + Returns: + ``bool``: True if available. + """ + return await self.invoke( + raw.functions.messages.CheckQuickReplyShortcut( + shortcut=shortcut + ) + ) diff --git a/pyrogram/methods/business/create_business_chat_link.py b/pyrogram/methods/business/create_business_chat_link.py new file mode 100644 index 00000000..ec72c018 --- /dev/null +++ b/pyrogram/methods/business/create_business_chat_link.py @@ -0,0 +1,43 @@ +# 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 CreateBusinessChatLink: + async def create_business_chat_link( + self: "pyrogram.Client", + link: "raw.types.InputBusinessChatLink" + ) -> "types.BusinessChatLink": + """Create a new business chat link. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + link (:obj:`~raw.types.InputBusinessChatLink`): + The business chat link to create. + + Returns: + :obj:`~pyrogram.types.BusinessChatLink`: On success, the created business chat link is returned. + """ + rpc = raw.functions.account.CreateBusinessChatLink( + link=link + ) + r = await self.invoke(rpc) + + return types.BusinessChatLink._parse(self, r) diff --git a/pyrogram/methods/business/delete_business_chat_link.py b/pyrogram/methods/business/delete_business_chat_link.py new file mode 100644 index 00000000..1b45596f --- /dev/null +++ b/pyrogram/methods/business/delete_business_chat_link.py @@ -0,0 +1,42 @@ +# 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 DeleteBusinessChatLink: + async def delete_business_chat_link( + self: "pyrogram.Client", + slug: str + ) -> bool: + """Delete a business chat link. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + slug (``str``): + The slug of the link to delete. + + Returns: + ``bool``: True on success. + """ + return await self.invoke( + raw.functions.account.DeleteBusinessChatLink( + slug=slug + ) + ) diff --git a/pyrogram/methods/business/delete_quick_reply_messages.py b/pyrogram/methods/business/delete_quick_reply_messages.py new file mode 100644 index 00000000..c7df40b8 --- /dev/null +++ b/pyrogram/methods/business/delete_quick_reply_messages.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 List +import pyrogram +from pyrogram import raw, types + + +class DeleteQuickReplyMessages: + async def delete_quick_reply_messages( + self: "pyrogram.Client", + shortcut_id: int, + message_ids: List[int] + ) -> "types.Updates": + """Delete messages from a quick reply shortcut. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + shortcut_id (``int``): + The shortcut ID. + + message_ids (List of ``int``): + List of message IDs within the shortcut to delete. + + Returns: + :obj:`~pyrogram.types.Updates`: On success, the updates object is returned. + """ + return await self.invoke( + raw.functions.messages.DeleteQuickReplyMessages( + shortcut_id=shortcut_id, + id=message_ids + ) + ) diff --git a/pyrogram/methods/business/delete_quick_reply_shortcut.py b/pyrogram/methods/business/delete_quick_reply_shortcut.py new file mode 100644 index 00000000..ad620642 --- /dev/null +++ b/pyrogram/methods/business/delete_quick_reply_shortcut.py @@ -0,0 +1,42 @@ +# 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 DeleteQuickReplyShortcut: + async def delete_quick_reply_shortcut( + self: "pyrogram.Client", + shortcut_id: int + ) -> bool: + """Delete a quick reply shortcut. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + shortcut_id (``int``): + The shortcut ID to delete. + + Returns: + ``bool``: True on success. + """ + return await self.invoke( + raw.functions.messages.DeleteQuickReplyShortcut( + shortcut_id=shortcut_id + ) + ) diff --git a/pyrogram/methods/business/edit_business_chat_link.py b/pyrogram/methods/business/edit_business_chat_link.py new file mode 100644 index 00000000..e630b383 --- /dev/null +++ b/pyrogram/methods/business/edit_business_chat_link.py @@ -0,0 +1,48 @@ +# 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 EditBusinessChatLink: + async def edit_business_chat_link( + self: "pyrogram.Client", + slug: str, + link: "raw.types.InputBusinessChatLink" + ) -> "types.BusinessChatLink": + """Edit an existing business chat link. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + slug (``str``): + The slug of the link to edit. + + link (:obj:`~raw.types.InputBusinessChatLink`): + The new business chat link details. + + Returns: + :obj:`~pyrogram.types.BusinessChatLink`: On success, the edited business chat link is returned. + """ + rpc = raw.functions.account.EditBusinessChatLink( + slug=slug, + link=link + ) + r = await self.invoke(rpc) + + return types.BusinessChatLink._parse(self, r) diff --git a/pyrogram/methods/business/edit_quick_reply_shortcut.py b/pyrogram/methods/business/edit_quick_reply_shortcut.py new file mode 100644 index 00000000..6bbd3151 --- /dev/null +++ b/pyrogram/methods/business/edit_quick_reply_shortcut.py @@ -0,0 +1,47 @@ +# 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 EditQuickReplyShortcut: + async def edit_quick_reply_shortcut( + self: "pyrogram.Client", + shortcut_id: int, + shortcut: str + ) -> bool: + """Edit the name of a quick reply shortcut. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + shortcut_id (``int``): + The shortcut ID to edit. + + shortcut (``str``): + The new name for the shortcut. + + Returns: + ``bool``: True on success. + """ + return await self.invoke( + raw.functions.messages.EditQuickReplyShortcut( + shortcut_id=shortcut_id, + shortcut=shortcut + ) + ) diff --git a/pyrogram/methods/business/get_bot_business_connection.py b/pyrogram/methods/business/get_bot_business_connection.py new file mode 100644 index 00000000..1ce2d94d --- /dev/null +++ b/pyrogram/methods/business/get_bot_business_connection.py @@ -0,0 +1,42 @@ +# 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 GetBotBusinessConnection: + async def get_bot_business_connection( + self: "pyrogram.Client", + connection_id: str + ) -> "types.Updates": + """Get information about a bot's business connection. + + .. include:: /_includes/usable-by/bots.rst + + Parameters: + connection_id (``str``): + The connection ID. + + Returns: + :obj:`~pyrogram.types.Updates`: On success, the updates object is returned. + """ + return await self.invoke( + raw.functions.account.GetBotBusinessConnection( + connection_id=connection_id + ) + ) diff --git a/pyrogram/methods/business/get_business_chat_links.py b/pyrogram/methods/business/get_business_chat_links.py new file mode 100644 index 00000000..a0847542 --- /dev/null +++ b/pyrogram/methods/business/get_business_chat_links.py @@ -0,0 +1,37 @@ +# 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 GetBusinessChatLinks: + async def get_business_chat_links( + self: "pyrogram.Client" + ) -> List["types.BusinessChatLink"]: + """Get business chat links. + + .. include:: /_includes/usable-by/users.rst + + Returns: + List of :obj:`~pyrogram.types.BusinessChatLink`: On success, a list of business chat links is returned. + """ + rpc = raw.functions.account.GetBusinessChatLinks() + r = await self.invoke(rpc) + + return types.List([types.BusinessChatLink._parse(self, link) for link in r.links]) diff --git a/pyrogram/methods/business/get_quick_replies.py b/pyrogram/methods/business/get_quick_replies.py new file mode 100644 index 00000000..63dd8f51 --- /dev/null +++ b/pyrogram/methods/business/get_quick_replies.py @@ -0,0 +1,37 @@ +# 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 GetQuickReplies: + async def get_quick_replies( + self: "pyrogram.Client" + ) -> List["types.QuickReply"]: + """Get the list of quick reply shortcuts. + + .. include:: /_includes/usable-by/users.rst + + Returns: + List of :obj:`~pyrogram.types.QuickReply`: On success, a list of quick reply shortcuts is returned. + """ + rpc = raw.functions.messages.GetQuickReplies(hash=0) + r = await self.invoke(rpc) + + return types.List([types.QuickReply._parse(self, qr) for qr in r.quick_replies]) diff --git a/pyrogram/methods/business/get_quick_reply_messages.py b/pyrogram/methods/business/get_quick_reply_messages.py new file mode 100644 index 00000000..7132bda8 --- /dev/null +++ b/pyrogram/methods/business/get_quick_reply_messages.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 List +import pyrogram +from pyrogram import raw, types + +class GetQuickReplyMessages: + async def get_quick_reply_messages( + self: "pyrogram.Client", + shortcut_id: int + ) -> List["types.Message"]: + """Get messages associated with a quick reply shortcut. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + shortcut_id (``int``): + The shortcut ID. + + Returns: + List of :obj:`~pyrogram.types.Message`: On success, a list of messages is returned. + """ + rpc = raw.functions.messages.GetQuickReplyMessages( + shortcut_id=shortcut_id, + id=[], + hash=0 + ) + r = await self.invoke(rpc) + + return types.List([ + await types.Message._parse(self, m, {u.id: u for u in r.users}, {c.id: c for c in r.chats}) + for m in r.messages + ]) diff --git a/pyrogram/methods/business/send_quick_reply_messages.py b/pyrogram/methods/business/send_quick_reply_messages.py new file mode 100644 index 00000000..5b0584b9 --- /dev/null +++ b/pyrogram/methods/business/send_quick_reply_messages.py @@ -0,0 +1,54 @@ +# 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, Union +import pyrogram +from pyrogram import raw, types + +class SendQuickReplyMessages: + async def send_quick_reply_messages( + self: "pyrogram.Client", + chat_id: Union[int, str], + shortcut_id: int, + message_ids: List[int] + ) -> "types.Updates": + """Send messages from a quick reply shortcut. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + + shortcut_id (``int``): + The shortcut ID. + + message_ids (List of ``int``): + List of message IDs within the shortcut to send. + + Returns: + :obj:`~pyrogram.types.Updates`: The resulting updates. + """ + return await self.invoke( + raw.functions.messages.SendQuickReplyMessages( + peer=await self.resolve_peer(chat_id), + shortcut_id=shortcut_id, + id=message_ids, + random_id=[self.rnd_id() for _ in message_ids] + ) + ) diff --git a/pyrogram/methods/decorators/__init__.py b/pyrogram/methods/decorators/__init__.py index 1fc61185..833fd2ff 100644 --- a/pyrogram/methods/decorators/__init__.py +++ b/pyrogram/methods/decorators/__init__.py @@ -27,7 +27,10 @@ from .on_message import OnMessage from .on_poll import OnPoll from .on_raw_update import OnRawUpdate +from .on_story import OnStory from .on_user_status import OnUserStatus +from .on_chat_boost import OnChatBoost +from .on_business_connection import OnBusinessConnection class Decorators( @@ -42,6 +45,9 @@ class Decorators( OnPoll, OnChosenInlineResult, OnChatMemberUpdated, - OnChatJoinRequest + OnChatJoinRequest, + OnStory, + OnChatBoost, + OnBusinessConnection ): pass diff --git a/pyrogram/methods/decorators/on_business_connection.py b/pyrogram/methods/decorators/on_business_connection.py new file mode 100644 index 00000000..6b5c6e51 --- /dev/null +++ b/pyrogram/methods/decorators/on_business_connection.py @@ -0,0 +1,44 @@ +# 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.handlers import BusinessConnectionHandler + + +class OnBusinessConnection: + def on_business_connection( + self, + filters=None, + group: int = 0 + ) -> callable: + """Decorator for handling business connection updates. + + Parameters: + filters (:obj:`~pyrogram.filters`, *optional*): + Pass one or more filters to speculate on which updates to handle. + + group (``int``, *optional*): + The group identifier, defaults to 0. + """ + def decorator(func: callable) -> callable: + if isinstance(self, pyrogram.Client): + self.add_handler(BusinessConnectionHandler(func, filters), group) + + return func + + return decorator diff --git a/pyrogram/methods/decorators/on_chat_boost.py b/pyrogram/methods/decorators/on_chat_boost.py new file mode 100644 index 00000000..ec1fecb8 --- /dev/null +++ b/pyrogram/methods/decorators/on_chat_boost.py @@ -0,0 +1,44 @@ +# 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.handlers import ChatBoostHandler + + +class OnChatBoost: + def on_chat_boost( + self, + filters=None, + group: int = 0 + ) -> callable: + """Decorator for handling chat boost updates. + + Parameters: + filters (:obj:`~pyrogram.filters`, *optional*): + Pass one or more filters to speculate on which updates to handle. + + group (``int``, *optional*): + The group identifier, defaults to 0. + """ + def decorator(func: callable) -> callable: + if isinstance(self, pyrogram.Client): + self.add_handler(ChatBoostHandler(func, filters), group) + + 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 00000000..a26ba8a0 --- /dev/null +++ b/pyrogram/methods/decorators/on_story.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 Callable + +import pyrogram +from pyrogram.filters import Filter + + +class OnStory: + def on_story( + self=None, + filters=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`. + + 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 5fc0133b..32f194ad 100644 --- a/pyrogram/methods/messages/__init__.py +++ b/pyrogram/methods/messages/__init__.py @@ -37,6 +37,9 @@ from .get_discussion_replies_count import GetDiscussionRepliesCount from .get_media_group import GetMediaGroup from .get_messages import GetMessages +from .get_saved_dialogs import GetSavedDialogs +from .pin_saved_dialog import PinSavedDialog +from .get_saved_reaction_tags import GetSavedReactionTags from .read_chat_history import ReadChatHistory from .retract_vote import RetractVote from .search_global import SearchGlobal @@ -76,6 +79,9 @@ class Messages( ForwardMessages, GetMediaGroup, GetMessages, + GetSavedDialogs, + PinSavedDialog, + GetSavedReactionTags, SendAudio, SendChatAction, SendContact, diff --git a/pyrogram/methods/messages/download_media.py b/pyrogram/methods/messages/download_media.py index db9a9a14..46383985 100644 --- a/pyrogram/methods/messages/download_media.py +++ b/pyrogram/methods/messages/download_media.py @@ -187,4 +187,4 @@ async def progress(current, total): if block: return await downloader else: - asyncio.get_event_loop().create_task(downloader) + asyncio.get_running_loop().create_task(downloader) diff --git a/pyrogram/methods/messages/get_pinned_saved_dialogs.py b/pyrogram/methods/messages/get_pinned_saved_dialogs.py new file mode 100644 index 00000000..15975e7f --- /dev/null +++ b/pyrogram/methods/messages/get_pinned_saved_dialogs.py @@ -0,0 +1,35 @@ +# 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 GetPinnedSavedDialogs: + async def get_pinned_saved_dialogs( + self: "pyrogram.Client" + ) -> "types.SavedDialogs": + """Get the list of pinned saved messages dialogs. + + .. include:: /_includes/usable-by/users.rst + + Returns: + :obj:`~pyrogram.types.SavedDialogs`: On success, the pinned saved dialogs are returned. + """ + r = await self.invoke(raw.functions.messages.GetPinnedSavedDialogs()) + return await types.SavedDialogs._parse(self, r) diff --git a/pyrogram/methods/messages/get_saved_dialogs.py b/pyrogram/methods/messages/get_saved_dialogs.py new file mode 100644 index 00000000..7cd6eccf --- /dev/null +++ b/pyrogram/methods/messages/get_saved_dialogs.py @@ -0,0 +1,60 @@ +# 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 GetSavedDialogs: + async def get_saved_dialogs( + self: "pyrogram.Client", + offset_date: int = 0, + offset_id: int = 0, + offset_peer: Union[int, str] = None, + limit: int = 100 + ) -> "types.SavedDialogs": + """Get the list of saved messages dialogs. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + offset_date (``int``, *optional*): + Offset date for pagination. + + offset_id (``int``, *optional*): + Offset message ID for pagination. + + offset_peer (``int`` | ``str``, *optional*): + Offset peer for pagination. + + limit (``int``, *optional*): + Limit of results. + + Returns: + :obj:`~pyrogram.types.SavedDialogs`: On success, the saved dialogs are returned. + """ + rpc = raw.functions.messages.GetSavedDialogs( + offset_date=offset_date, + offset_id=offset_id, + offset_peer=await self.resolve_peer(offset_peer or raw.types.InputPeerEmpty()), + limit=limit, + hash=0 + ) + r = await self.invoke(rpc) + + return await types.SavedDialogs._parse(self, r) diff --git a/pyrogram/methods/messages/get_saved_reaction_tags.py b/pyrogram/methods/messages/get_saved_reaction_tags.py new file mode 100644 index 00000000..2bf8bc9b --- /dev/null +++ b/pyrogram/methods/messages/get_saved_reaction_tags.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 List, Union +import pyrogram +from pyrogram import raw, types + +class GetSavedReactionTags: + async def get_saved_reaction_tags( + self: "pyrogram.Client", + peer: Union[int, str] = None + ) -> List["types.SavedReactionTag"]: + """Get reaction tags used in saved messages. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + peer (``int`` | ``str``, *optional*): + Filter by a specific chat. + + Returns: + List of :obj:`~pyrogram.types.SavedReactionTag`: On success, a list of reaction tags is returned. + """ + r = await self.invoke( + raw.functions.messages.GetSavedReactionTags( + peer=await self.resolve_peer(peer) if peer else None, + hash=0 + ) + ) + + return types.List([ + types.SavedReactionTag._parse(self, tag) + for tag in r.tags + ]) diff --git a/pyrogram/methods/messages/pin_saved_dialog.py b/pyrogram/methods/messages/pin_saved_dialog.py new file mode 100644 index 00000000..ede9d08a --- /dev/null +++ b/pyrogram/methods/messages/pin_saved_dialog.py @@ -0,0 +1,50 @@ +# 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 PinSavedDialog: + async def pin_saved_dialog( + self: "pyrogram.Client", + peer: Union[int, str], + pinned: bool = True + ) -> bool: + """Pin or unpin a saved messages dialog. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + peer (``int`` | ``str``): + Unique identifier (int) or username (str) of the chat. + + pinned (``bool``, *optional*): + Whether to pin or unpin the dialog. Defaults to True (pin). + + Returns: + ``bool``: True on success. + """ + return await self.invoke( + raw.functions.messages.ToggleSavedDialogPin( + peer=raw.types.InputDialogPeer( + peer=await self.resolve_peer(peer) + ), + pinned=pinned + ) + ) diff --git a/pyrogram/methods/messages/reorder_pinned_saved_dialogs.py b/pyrogram/methods/messages/reorder_pinned_saved_dialogs.py new file mode 100644 index 00000000..dc1b98ce --- /dev/null +++ b/pyrogram/methods/messages/reorder_pinned_saved_dialogs.py @@ -0,0 +1,52 @@ +# 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, Union +import pyrogram +from pyrogram import raw + + +class ReorderPinnedSavedDialogs: + async def reorder_pinned_saved_dialogs( + self: "pyrogram.Client", + order: List[Union[int, str]], + force: bool = None + ) -> bool: + """Reorder pinned saved messages dialogs. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + order (List of ``int`` | ``str``): + The new order of pinned saved dialogs. + + force (``bool``, *optional*): + Whether to force the reorder. + + Returns: + ``bool``: True on success. + """ + return await self.invoke( + raw.functions.messages.ReorderPinnedSavedDialogs( + order=[ + raw.types.InputDialogPeer(peer=await self.resolve_peer(p)) + for p in order + ], + force=force + ) + ) diff --git a/pyrogram/methods/messages/update_saved_reaction_tag.py b/pyrogram/methods/messages/update_saved_reaction_tag.py new file mode 100644 index 00000000..8c6af109 --- /dev/null +++ b/pyrogram/methods/messages/update_saved_reaction_tag.py @@ -0,0 +1,48 @@ +# 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 UpdateSavedReactionTag: + async def update_saved_reaction_tag( + self: "pyrogram.Client", + reaction: "types.Reaction", + title: str = None + ) -> bool: + """Update a saved messages reaction tag. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + reaction (:obj:`~pyrogram.types.Reaction`): + The reaction to update. + + title (``str``, *optional*): + The new title for the tag. + + Returns: + ``bool``: True on success. + """ + return await self.invoke( + raw.functions.messages.UpdateSavedReactionTag( + reaction=await reaction.write(self), + title=title + ) + ) diff --git a/pyrogram/methods/payments/__init__.py b/pyrogram/methods/payments/__init__.py new file mode 100644 index 00000000..1ef5d099 --- /dev/null +++ b/pyrogram/methods/payments/__init__.py @@ -0,0 +1,35 @@ +# 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_stars_status import GetStarsStatus +from .get_stars_transactions import GetStarsTransactions +from .get_star_gifts import GetStarGifts +from .get_unique_star_gift import GetUniqueStarGift +from .get_saved_star_gifts import GetSavedStarGifts +from .get_star_gift_auction_state import GetStarGiftAuctionState + + +class Payments( + GetStarsStatus, + GetStarsTransactions, + GetStarGifts, + GetUniqueStarGift, + GetSavedStarGifts, + GetStarGiftAuctionState +): + pass diff --git a/pyrogram/methods/payments/get_saved_star_gifts.py b/pyrogram/methods/payments/get_saved_star_gifts.py new file mode 100644 index 00000000..cc9e4877 --- /dev/null +++ b/pyrogram/methods/payments/get_saved_star_gifts.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 . + +from typing import List, Union +import pyrogram +from pyrogram import raw, types + +class GetSavedStarGifts: + async def get_saved_star_gifts( + self: "pyrogram.Client", + peer: Union[int, str] = "me", + offset: str = None, + limit: int = 100 + ) -> List["types.StarGiftUnique"]: + """Get the list of saved star gifts for a peer. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + peer (``int`` | ``str``, *optional*): + Unique identifier (int) or username (str) of the target chat. + Defaults to "me" (your own saved gifts). + + offset (``str``, *optional*): + Offset for pagination. + + limit (``int``, *optional*): + Limit of results. + + Returns: + List of :obj:`~pyrogram.types.StarGiftUnique`: On success, a list of unique star gifts is returned. + """ + r = await self.invoke( + raw.functions.payments.GetSavedStarGifts( + peer=await self.resolve_peer(peer), + offset=offset or "", + limit=limit + ) + ) + + return types.List([ + types.StarGiftUnique._parse(self, gift) + for gift in r.gifts + ]) diff --git a/pyrogram/methods/payments/get_star_gift_auction_state.py b/pyrogram/methods/payments/get_star_gift_auction_state.py new file mode 100644 index 00000000..679dc0b2 --- /dev/null +++ b/pyrogram/methods/payments/get_star_gift_auction_state.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, types + + +class GetStarGiftAuctionState: + async def get_star_gift_auction_state( + self: "pyrogram.Client", + gift_id: int + ) -> "types.StarGiftAuctionState": + """Get the auction state of a unique star gift. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + gift_id (``int``): + The unique ID of the gift. + + Returns: + :obj:`~pyrogram.types.StarGiftAuctionState`: On success, the auction state is returned. + """ + r = await self.invoke( + raw.functions.payments.GetStarGiftAuctionState( + auction=raw.types.InputStarGiftAuction(gift_id=gift_id), + version=0 + ) + ) + return types.StarGiftAuctionState._parse(self, r) diff --git a/pyrogram/methods/payments/get_star_gifts.py b/pyrogram/methods/payments/get_star_gifts.py new file mode 100644 index 00000000..eff8823e --- /dev/null +++ b/pyrogram/methods/payments/get_star_gifts.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 GetStarGifts: + async def get_star_gifts( + self: "pyrogram.Client" + ) -> List["types.StarGift"]: + """Get the list of available star gifts. + + .. include:: /_includes/usable-by/users.rst + + Returns: + List of :obj:`~pyrogram.types.StarGift`: On success, a list of star gifts is returned. + """ + r = await self.invoke(raw.functions.payments.GetStarGifts(hash=0)) + + return types.List([ + await types.StarGift._parse(self, gift) + for gift in r.gifts + ]) diff --git a/pyrogram/methods/payments/get_stars_status.py b/pyrogram/methods/payments/get_stars_status.py new file mode 100644 index 00000000..f659154c --- /dev/null +++ b/pyrogram/methods/payments/get_stars_status.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 raw, types + + +class GetStarsStatus: + async def get_stars_status( + self: "pyrogram.Client", + peer: Union[int, str] = None + ) -> "types.StarsStatus": + """Get the current Stars balance and subscription status. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + peer (``int`` | ``str``, *optional*): + Unique identifier (int) or username (str) of the peer to get status for. + If not provided, gets your own Stars status. + + Returns: + :obj:`~pyrogram.types.StarsStatus`: The Stars status. + + Example: + .. code-block:: python + + # Get your own Stars status + status = await app.get_stars_status() + print(f"Balance: {status.balance}") + + # Get Stars status for a bot + status = await app.get_stars_status("@mybot") + """ + if peer: + input_peer = await self.resolve_peer(peer) + else: + input_peer = raw.types.InputPeerSelf() + + r = await self.invoke( + raw.functions.payments.GetStarsStatus( + peer=input_peer + ) + ) + + return types.StarsStatus._parse(self, r) diff --git a/pyrogram/methods/payments/get_stars_transactions.py b/pyrogram/methods/payments/get_stars_transactions.py new file mode 100644 index 00000000..04a0d1a5 --- /dev/null +++ b/pyrogram/methods/payments/get_stars_transactions.py @@ -0,0 +1,109 @@ +# 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 AsyncGenerator, Optional, Union + +import pyrogram +from pyrogram import raw, types + + +class GetStarsTransactions: + async def get_stars_transactions( + self: "pyrogram.Client", + peer: Union[int, str] = None, + inbound: bool = None, + outbound: bool = None, + limit: int = 0, + offset: str = "" + ) -> AsyncGenerator["types.StarsTransaction", None]: + """Get Stars transaction history. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + peer (``int`` | ``str``, *optional*): + Unique identifier (int) or username (str) of the peer. + If not provided, gets your own transaction history. + + inbound (``bool``, *optional*): + If True, only get incoming transactions. + + outbound (``bool``, *optional*): + If True, only get outgoing transactions. + + limit (``int``, *optional*): + Limits the number of transactions to be retrieved. + By default, no limit is applied and all transactions are returned. + + offset (``str``, *optional*): + Pagination offset. + + Returns: + ``Generator``: A generator yielding :obj:`~pyrogram.types.StarsTransaction` objects. + + Example: + .. code-block:: python + + # Get all transactions + async for transaction in app.get_stars_transactions(): + print(transaction) + + # Get only incoming transactions + async for transaction in app.get_stars_transactions(inbound=True): + print(f"Received {transaction.amount}") + """ + if peer: + input_peer = await self.resolve_peer(peer) + else: + input_peer = raw.types.InputPeerSelf() + + current = 0 + total = abs(limit) or (1 << 31) - 1 + limit = min(100, total) + + while True: + r = await self.invoke( + raw.functions.payments.GetStarsTransactions( + peer=input_peer, + inbound=inbound, + outbound=outbound, + offset=offset, + limit=limit + ) + ) + + users = {u.id: u for u in r.users} + chats = {c.id: c for c in r.chats} + + transactions = r.history + + if not transactions: + return + + for transaction in transactions: + yield types.StarsTransaction._parse(self, transaction, users, chats) + + current += 1 + + if current >= total: + return + + offset = r.next_offset + + if not offset: + return diff --git a/pyrogram/methods/payments/get_unique_star_gift.py b/pyrogram/methods/payments/get_unique_star_gift.py new file mode 100644 index 00000000..0a2ea41d --- /dev/null +++ b/pyrogram/methods/payments/get_unique_star_gift.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, types + + +class GetUniqueStarGift: + async def get_unique_star_gift( + self: "pyrogram.Client", + slug: str + ) -> "types.StarGiftUnique": + """Get information about a unique star gift by its slug. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + slug (``str``): + The slug of the unique gift. + + Returns: + :obj:`~pyrogram.types.StarGiftUnique`: On success, the unique star gift is returned. + """ + r = await self.invoke(raw.functions.payments.GetUniqueStarGift(slug=slug)) + return types.StarGiftUnique._parse(self, r.gift) diff --git a/pyrogram/methods/stories/__init__.py b/pyrogram/methods/stories/__init__.py new file mode 100644 index 00000000..4f3cba90 --- /dev/null +++ b/pyrogram/methods/stories/__init__.py @@ -0,0 +1,39 @@ +# 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 +from .get_peer_stories import GetPeerStories +from .get_all_stories import GetAllStories +from .read_stories import ReadStories +from .delete_stories import DeleteStories +from .send_story import SendStory +from .edit_story import EditStory +from .get_story_public_forwards import GetStoryPublicForwards + + +class Stories( + GetStories, + GetPeerStories, + GetAllStories, + ReadStories, + DeleteStories, + SendStory, + EditStory, + GetStoryPublicForwards +): + pass diff --git a/pyrogram/methods/stories/delete_stories.py b/pyrogram/methods/stories/delete_stories.py new file mode 100644 index 00000000..45c18dfb --- /dev/null +++ b/pyrogram/methods/stories/delete_stories.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 List, Union + +import pyrogram +from pyrogram import raw + + +class DeleteStories: + async def delete_stories( + self: "pyrogram.Client", + chat_id: Union[int, str], + story_ids: Union[int, List[int]] + ) -> List[int]: + """Delete one or more stories. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your own stories you can use "me" or "self". + + story_ids (``int`` | List of ``int``): + Story ID or list of story IDs to delete. + + Returns: + List of ``int``: List of deleted story IDs. + + Example: + .. code-block:: python + + # Delete a single story + deleted = await app.delete_stories("me", 12345) + + # Delete multiple stories + deleted = await app.delete_stories("me", [12345, 12346]) + """ + peer = await self.resolve_peer(chat_id) + + story_ids = [story_ids] if not isinstance(story_ids, list) else story_ids + + r = await self.invoke( + raw.functions.stories.DeleteStories( + peer=peer, + id=story_ids + ) + ) + + return list(r) diff --git a/pyrogram/methods/stories/edit_story.py b/pyrogram/methods/stories/edit_story.py new file mode 100644 index 00000000..7a0d8db9 --- /dev/null +++ b/pyrogram/methods/stories/edit_story.py @@ -0,0 +1,100 @@ +# 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, Union + +import pyrogram +from pyrogram import raw, types, utils + + +class EditStory: + async def edit_story( + self: "pyrogram.Client", + chat_id: Union[int, str], + story_id: int, + caption: str = None, + parse_mode: "pyrogram.enums.ParseMode" = None, + caption_entities: List["types.MessageEntity"] = None, + privacy_rules: List["raw.base.InputPrivacyRule"] = None + ) -> "types.StoryItem": + """Edit a story. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your own stories you can use "me" or "self". + + story_id (``int``): + The ID of the story to edit. + + caption (``str``, *optional*): + New caption for the story, 0-2048 characters. + + parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*): + By default, texts are parsed using HTML style. + + caption_entities (List of :obj:`~pyrogram.types.MessageEntity`, *optional*): + List of special entities that appear in the new caption. + + privacy_rules (List of :obj:`~pyrogram.raw.base.InputPrivacyRule`, *optional*): + New privacy rules for the story. + + Returns: + :obj:`~pyrogram.types.StoryItem`: On success, the edited story is returned. + + Example: + .. code-block:: python + + # Edit story caption + await app.edit_story("me", 12345, caption="Updated caption!") + """ + peer = await self.resolve_peer(chat_id) + + # Parse caption + message = None + entities = None + if caption is not None: + parsed = await utils.parse_text_entities( + self, caption, parse_mode, caption_entities + ) + message = parsed.get("message") + entities = parsed.get("entities") + + r = await self.invoke( + raw.functions.stories.EditStory( + peer=peer, + id=story_id, + caption=message, + entities=entities, + privacy_rules=privacy_rules + ) + ) + + users = {u.id: u for u in r.users} + chats = {c.id: c for c in r.chats} + + for update in r.updates: + if isinstance(update, raw.types.UpdateStory): + if isinstance(update.story, raw.types.StoryItem): + return await types.StoryItem._parse( + self, update.story, users, chats, update.peer + ) + + return None diff --git a/pyrogram/methods/stories/get_all_stories.py b/pyrogram/methods/stories/get_all_stories.py new file mode 100644 index 00000000..ed316085 --- /dev/null +++ b/pyrogram/methods/stories/get_all_stories.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 . + +from typing import List, Optional + +import pyrogram +from pyrogram import raw, types + + +class GetAllStories: + async def get_all_stories( + self: "pyrogram.Client", + hidden: bool = False, + next_offset: str = None + ) -> List["types.PeerStories"]: + """Get all active stories from followed users and channels. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + hidden (``bool``, *optional*): + If True, get stories from hidden (archived) peers instead. + + next_offset (``str``, *optional*): + Offset for pagination, returned from previous call. + + Returns: + List of :obj:`~pyrogram.types.PeerStories`: On success, a list of peer stories is returned. + + Example: + .. code-block:: python + + # Get all stories from followed users + all_stories = await app.get_all_stories() + + for peer_stories in all_stories: + print(f"{peer_stories.peer} has {len(peer_stories.stories)} stories") + """ + r = await self.invoke( + raw.functions.stories.GetAllStories( + hidden=hidden, + next=next_offset is not None, + state=next_offset or "" + ) + ) + + if isinstance(r, raw.types.stories.AllStoriesNotModified): + return types.List([]) + + users = {u.id: u for u in r.users} + chats = {c.id: c for c in r.chats} + + peer_stories_list = [] + for peer_stories in r.peer_stories: + parsed = await types.PeerStories._parse(self, peer_stories, users, chats) + peer_stories_list.append(parsed) + + return types.List(peer_stories_list) diff --git a/pyrogram/methods/stories/get_peer_stories.py b/pyrogram/methods/stories/get_peer_stories.py new file mode 100644 index 00000000..443d0fa6 --- /dev/null +++ b/pyrogram/methods/stories/get_peer_stories.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, types, utils + + +class GetPeerStories: + async def get_peer_stories( + self: "pyrogram.Client", + chat_id: Union[int, str] + ) -> "types.PeerStories": + """Get all active stories of a peer. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your own stories you can use "me" or "self". + + Returns: + :obj:`~pyrogram.types.PeerStories`: On success, the peer's stories are returned. + + Example: + .. code-block:: python + + # Get all stories of a user + stories = await app.get_peer_stories("username") + + for story in stories.stories: + print(story.id) + """ + peer = await self.resolve_peer(chat_id) + + r = await self.invoke( + raw.functions.stories.GetPeerStories( + peer=peer + ) + ) + + users = {u.id: u for u in r.users} + chats = {c.id: c for c in r.chats} + + return await types.PeerStories._parse(self, r.stories, users, chats) diff --git a/pyrogram/methods/stories/get_stories.py b/pyrogram/methods/stories/get_stories.py new file mode 100644 index 00000000..7c62cc38 --- /dev/null +++ b/pyrogram/methods/stories/get_stories.py @@ -0,0 +1,78 @@ +# 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, Union + +import pyrogram +from pyrogram import raw, types, utils + + +class GetStories: + async def get_stories( + self: "pyrogram.Client", + chat_id: Union[int, str], + story_ids: Union[int, List[int]] + ) -> Union["types.StoryItem", List["types.StoryItem"]]: + """Get one or more stories by their IDs. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your own stories you can use "me" or "self". + + story_ids (``int`` | List of ``int``): + Story ID or list of story IDs to get. + + Returns: + :obj:`~pyrogram.types.StoryItem` | List of :obj:`~pyrogram.types.StoryItem`: On success, + if a single story ID was passed, the story is returned, otherwise a list of stories is returned. + + Example: + .. code-block:: python + + # Get a single story + story = await app.get_stories(chat_id, 12345) + + # Get multiple stories + stories = await app.get_stories(chat_id, [12345, 12346, 12347]) + """ + peer = await self.resolve_peer(chat_id) + + is_single = not isinstance(story_ids, list) + story_ids = [story_ids] if is_single else story_ids + + r = await self.invoke( + raw.functions.stories.GetStoriesByID( + peer=peer, + id=story_ids + ) + ) + + users = {u.id: u for u in r.users} + chats = {c.id: c for c in r.chats} + + stories = [] + for story in r.stories: + if isinstance(story, raw.types.StoryItem): + stories.append( + await types.StoryItem._parse(self, story, users, chats, peer) + ) + + return stories[0] if is_single and stories else types.List(stories) diff --git a/pyrogram/methods/stories/get_story_public_forwards.py b/pyrogram/methods/stories/get_story_public_forwards.py new file mode 100644 index 00000000..99b330bc --- /dev/null +++ b/pyrogram/methods/stories/get_story_public_forwards.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 . + +from typing import Union +import pyrogram +from pyrogram import raw, types + +class GetStoryPublicForwards: + async def get_story_public_forwards( + self: "pyrogram.Client", + chat_id: Union[int, str], + story_id: int, + offset: str = None, + limit: int = 20 + ) -> "types.PublicForwards": + """Get public forwards of a story. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + + story_id (``int``): + The story ID. + + offset (``str``, *optional*): + Offset for pagination. + + limit (``int``, *optional*): + Limit of results. + + Returns: + :obj:`~pyrogram.types.PublicForwards`: The public forwards. + """ + rpc = raw.functions.stats.GetStoryPublicForwards( + peer=await self.resolve_peer(chat_id), + id=story_id, + offset=offset or "", + limit=limit + ) + r = await self.invoke(rpc) + + return await types.PublicForwards._parse(self, r) diff --git a/pyrogram/methods/stories/get_story_reactions_list.py b/pyrogram/methods/stories/get_story_reactions_list.py new file mode 100644 index 00000000..47bbb398 --- /dev/null +++ b/pyrogram/methods/stories/get_story_reactions_list.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 . + +from typing import Union +import pyrogram +from pyrogram import raw, types + +class GetStoryReactionsList: + async def get_story_reactions_list( + self: "pyrogram.Client", + chat_id: Union[int, str], + story_id: int, + reaction: "types.Reaction" = None, + forwards_first: bool = None, + offset: str = None, + limit: int = 20 + ) -> "types.StoryReactionsList": + """Get the list of reactions on a story. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + + story_id (``int``): + The story ID. + + reaction (:obj:`~pyrogram.types.Reaction`, *optional*): + Filter by a specific reaction. + + forwards_first (``bool``, *optional*): + Pass True to get forwards first. + + offset (``str``, *optional*): + Offset for pagination. + + limit (``int``, *optional*): + Limit of results. + + Returns: + :obj:`~pyrogram.types.StoryReactionsList`: The list of story reactions. + """ + rpc = raw.functions.stories.GetStoryReactionsList( + peer=await self.resolve_peer(chat_id), + id=story_id, + reaction=await reaction.write(self) if reaction else None, + forwards_first=forwards_first, + offset=offset or "", + limit=limit + ) + r = await self.invoke(rpc) + + return types.StoryReactionsList._parse(self, r) diff --git a/pyrogram/methods/stories/get_story_views_list.py b/pyrogram/methods/stories/get_story_views_list.py new file mode 100644 index 00000000..aa0d593a --- /dev/null +++ b/pyrogram/methods/stories/get_story_views_list.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 typing import Union +import pyrogram +from pyrogram import raw, types + +class GetStoryViewsList: + async def get_story_views_list( + self: "pyrogram.Client", + chat_id: Union[int, str], + story_id: int, + query: str = None, + just_contacts: bool = None, + reactions_first: bool = None, + forwards_first: bool = None, + offset: str = None, + limit: int = 20 + ) -> "types.StoryViewsList": + """Get the list of viewers of a story. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + + story_id (``int``): + The story ID. + + query (``str``, *optional*): + Search query to filter viewers by name. + + just_contacts (``bool``, *optional*): + Pass True to only get contacts. + + reactions_first (``bool``, *optional*): + Pass True to get viewers who reacted first. + + forwards_first (``bool``, *optional*): + Pass True to get viewers who forwarded first. + + offset (``str``, *optional*): + Offset for pagination. + + limit (``int``, *optional*): + Limit of results. + + Returns: + :obj:`~pyrogram.types.StoryViewsList`: The list of story views. + """ + rpc = raw.functions.stories.GetStoryViewsList( + peer=await self.resolve_peer(chat_id), + id=story_id, + q=query or "", + just_contacts=just_contacts, + reactions_first=reactions_first, + forwards_first=forwards_first, + offset=offset or "", + limit=limit + ) + r = await self.invoke(rpc) + + return types.StoryViewsList._parse(self, r) diff --git a/pyrogram/methods/stories/read_stories.py b/pyrogram/methods/stories/read_stories.py new file mode 100644 index 00000000..af8107e5 --- /dev/null +++ b/pyrogram/methods/stories/read_stories.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 List, Union + +import pyrogram +from pyrogram import raw + + +class ReadStories: + async def read_stories( + self: "pyrogram.Client", + chat_id: Union[int, str], + max_id: int + ) -> List[int]: + """Mark stories as read up to a certain ID. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + + max_id (``int``): + The ID of the last story to mark as read. + All stories with ID <= max_id will be marked as read. + + Returns: + List of ``int``: List of story IDs that were marked as read. + + Example: + .. code-block:: python + + # Mark all stories up to ID 12345 as read + read_ids = await app.read_stories("username", 12345) + """ + peer = await self.resolve_peer(chat_id) + + r = await self.invoke( + raw.functions.stories.ReadStories( + peer=peer, + max_id=max_id + ) + ) + + return list(r) diff --git a/pyrogram/methods/stories/send_story.py b/pyrogram/methods/stories/send_story.py new file mode 100644 index 00000000..800a388f --- /dev/null +++ b/pyrogram/methods/stories/send_story.py @@ -0,0 +1,256 @@ +# 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 typing import BinaryIO, List, Union + +import pyrogram +from pyrogram import raw, types, utils +from pyrogram.file_id import FileType + + +class SendStory: + async def send_story( + self: "pyrogram.Client", + chat_id: Union[int, str], + media: Union[str, BinaryIO], + caption: str = None, + period: int = 86400, + pinned: bool = None, + protect_content: bool = None, + parse_mode: "pyrogram.enums.ParseMode" = None, + caption_entities: List["types.MessageEntity"] = None, + privacy_rules: List["raw.base.InputPrivacyRule"] = None + ) -> "types.StoryItem": + """Send a new story. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + For your own stories you can use "me" or "self". + + media (``str`` | ``BinaryIO``): + Photo or video to send as story. + Pass a file_id as string to send a file that exists on the Telegram servers, + pass an HTTP URL as string for Telegram to get a file from the Internet, + pass a file path as string to upload a new file from the local filesystem, + or pass a binary file-like object to upload from memory. + + caption (``str``, *optional*): + Story caption, 0-2048 characters. + + period (``int``, *optional*): + Period after which the story will expire, in seconds. + Default is 86400 (24 hours). Allowed values: 6*3600 (6 hours), + 12*3600 (12 hours), 86400 (24 hours), 2*86400 (48 hours). + + pinned (``bool``, *optional*): + Whether to pin the story to the profile. + + protect_content (``bool``, *optional*): + Protects the story from forwarding and saving. + + parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*): + By default, texts are parsed using HTML style. + Pass "markdown" or "md" to enable Markdown-style parsing. + + caption_entities (List of :obj:`~pyrogram.types.MessageEntity`, *optional*): + List of special entities that appear in the caption. + + privacy_rules (List of :obj:`~pyrogram.raw.base.InputPrivacyRule`, *optional*): + Privacy rules for the story. If not provided, defaults to public. + + Returns: + :obj:`~pyrogram.types.StoryItem`: On success, the sent story is returned. + + Example: + .. code-block:: python + + # Send a photo story + await app.send_story("me", "photo.jpg", caption="My story!") + + # Send a video story + await app.send_story("me", "video.mp4") + + # Send a story with limited visibility + from pyrogram import raw + await app.send_story( + "me", + "photo.jpg", + privacy_rules=[raw.types.InputPrivacyValueAllowCloseFriends()] + ) + """ + peer = await self.resolve_peer(chat_id) + + # Parse caption + message, entities = (await utils.parse_text_entities( + self, caption, parse_mode, caption_entities + )).values() + + # Determine if it's a photo or video + if isinstance(media, str): + if os.path.isfile(media): + # Local file + file_name = os.path.basename(media) + mime_type = self.guess_mime_type(media) or "application/octet-stream" + + if mime_type.startswith("video/"): + # Upload as video + media_obj = await self.invoke( + raw.functions.messages.UploadMedia( + peer=peer, + media=raw.types.InputMediaUploadedDocument( + file=await self.save_file(media), + mime_type=mime_type, + attributes=[ + raw.types.DocumentAttributeVideo( + supports_streaming=True, + duration=0, + w=0, + h=0 + ), + raw.types.DocumentAttributeFilename(file_name=file_name) + ] + ) + ) + ) + input_media = raw.types.InputMediaDocument( + id=raw.types.InputDocument( + id=media_obj.document.id, + access_hash=media_obj.document.access_hash, + file_reference=media_obj.document.file_reference + ) + ) + else: + # Upload as photo + media_obj = await self.invoke( + raw.functions.messages.UploadMedia( + peer=peer, + media=raw.types.InputMediaUploadedPhoto( + file=await self.save_file(media) + ) + ) + ) + input_media = raw.types.InputMediaPhoto( + id=raw.types.InputPhoto( + id=media_obj.photo.id, + access_hash=media_obj.photo.access_hash, + file_reference=media_obj.photo.file_reference + ) + ) + elif re.match("^https?://", media): + # URL + if any(ext in media.lower() for ext in [".mp4", ".mov", ".avi", ".webm"]): + input_media = raw.types.InputMediaDocumentExternal(url=media) + else: + input_media = raw.types.InputMediaPhotoExternal(url=media) + else: + # file_id + decoded = utils.decode_file_id(media) + if decoded.file_type == FileType.PHOTO: + input_media = raw.types.InputMediaPhoto( + id=raw.types.InputPhoto( + id=decoded.media_id, + access_hash=decoded.access_hash, + file_reference=decoded.file_reference + ) + ) + else: + input_media = raw.types.InputMediaDocument( + id=raw.types.InputDocument( + id=decoded.media_id, + access_hash=decoded.access_hash, + file_reference=decoded.file_reference + ) + ) + else: + # BinaryIO - determine type from content + mime_type = getattr(media, "mime_type", None) or "image/jpeg" + if mime_type.startswith("video/"): + media_obj = await self.invoke( + raw.functions.messages.UploadMedia( + peer=peer, + media=raw.types.InputMediaUploadedDocument( + file=await self.save_file(media), + mime_type=mime_type, + attributes=[ + raw.types.DocumentAttributeVideo( + supports_streaming=True, + duration=0, + w=0, + h=0 + ) + ] + ) + ) + ) + input_media = raw.types.InputMediaDocument( + id=raw.types.InputDocument( + id=media_obj.document.id, + access_hash=media_obj.document.access_hash, + file_reference=media_obj.document.file_reference + ) + ) + else: + media_obj = await self.invoke( + raw.functions.messages.UploadMedia( + peer=peer, + media=raw.types.InputMediaUploadedPhoto( + file=await self.save_file(media) + ) + ) + ) + input_media = raw.types.InputMediaPhoto( + id=raw.types.InputPhoto( + id=media_obj.photo.id, + access_hash=media_obj.photo.access_hash, + file_reference=media_obj.photo.file_reference + ) + ) + + # Default privacy rules (public) + if privacy_rules is None: + privacy_rules = [raw.types.InputPrivacyValueAllowAll()] + + r = await self.invoke( + raw.functions.stories.SendStory( + peer=peer, + media=input_media, + caption=message, + entities=entities, + privacy_rules=privacy_rules, + period=period, + pinned=pinned, + noforwards=protect_content + ) + ) + + users = {u.id: u for u in r.users} + chats = {c.id: c for c in r.chats} + + for update in r.updates: + if isinstance(update, raw.types.UpdateStory): + if isinstance(update.story, raw.types.StoryItem): + return await types.StoryItem._parse( + self, update.story, users, chats, update.peer + ) + + return None diff --git a/pyrogram/methods/stories/send_story_reaction.py b/pyrogram/methods/stories/send_story_reaction.py new file mode 100644 index 00000000..6b6c2038 --- /dev/null +++ b/pyrogram/methods/stories/send_story_reaction.py @@ -0,0 +1,57 @@ +# 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 SendStoryReaction: + async def send_story_reaction( + self: "pyrogram.Client", + chat_id: Union[int, str], + story_id: int, + reaction: "types.Reaction" = None, + add_to_recent: bool = None + ) -> bool: + """Send a reaction to a story. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + + story_id (``int``): + The story ID. + + reaction (:obj:`~pyrogram.types.Reaction`, *optional*): + The reaction to send. If not provided, it will remove the existing reaction. + + add_to_recent (``bool``, *optional*): + Whether to add the reaction to the recent reactions list. + + Returns: + ``bool``: True on success. + """ + rpc = raw.functions.stories.SendReaction( + peer=await self.resolve_peer(chat_id), + story_id=story_id, + reaction=await reaction.write(self) if reaction else raw.types.ReactionEmpty(), + add_to_recent=add_to_recent + ) + return await self.invoke(rpc) diff --git a/pyrogram/methods/todo/__init__.py b/pyrogram/methods/todo/__init__.py new file mode 100644 index 00000000..265da68a --- /dev/null +++ b/pyrogram/methods/todo/__init__.py @@ -0,0 +1,24 @@ +# 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 .toggle_todo_completed import ToggleTodoCompleted + +class Todo( + ToggleTodoCompleted +): + pass diff --git a/pyrogram/methods/todo/toggle_todo_completed.py b/pyrogram/methods/todo/toggle_todo_completed.py new file mode 100644 index 00000000..e43a4a20 --- /dev/null +++ b/pyrogram/methods/todo/toggle_todo_completed.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, Union +import pyrogram +from pyrogram import raw, types + +class ToggleTodoCompleted: + async def toggle_todo_completed( + self: "pyrogram.Client", + chat_id: Union[int, str], + message_id: int, + completed: List[int] = None, + incompleted: List[int] = None + ) -> "types.Updates": + """Toggle to-do items as completed or incompleted. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + + message_id (``int``): + Identifier of the message containing the to-do list. + + completed (List of ``int``, *optional*): + List of item indices to mark as completed. + + incompleted (List of ``int``, *optional*): + List of item indices to mark as incompleted. + + Returns: + :obj:`~pyrogram.types.Updates`: On success, the updates object is returned. + """ + return await self.invoke( + raw.functions.messages.ToggleTodoCompleted( + peer=await self.resolve_peer(chat_id), + msg_id=message_id, + completed=completed or [], + incompleted=incompleted or [] + ) + ) diff --git a/pyrogram/methods/utilities/idle.py b/pyrogram/methods/utilities/idle.py index 732cb032..00db22a2 100644 --- a/pyrogram/methods/utilities/idle.py +++ b/pyrogram/methods/utilities/idle.py @@ -73,7 +73,7 @@ async def main(): def signal_handler(signum, __): logging.info(f"Stop signal received ({signals[signum]}). Exiting...") - asyncio.get_event_loop().run_in_executor(None, task.cancel) + task.cancel() for s in (SIGINT, SIGTERM, SIGABRT): signal_fn(s, signal_handler) diff --git a/pyrogram/methods/utilities/run.py b/pyrogram/methods/utilities/run.py index ec1bece3..00598cc5 100644 --- a/pyrogram/methods/utilities/run.py +++ b/pyrogram/methods/utilities/run.py @@ -70,7 +70,12 @@ async def main(): app.run(main()) """ - loop = asyncio.get_event_loop() + try: + loop = asyncio.get_running_loop() + except RuntimeError: + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + run = loop.run_until_complete if coroutine is not None: diff --git a/pyrogram/sync.py b/pyrogram/sync.py index 94c82a3d..54c53610 100644 --- a/pyrogram/sync.py +++ b/pyrogram/sync.py @@ -52,11 +52,7 @@ async def anext(agen): def async_to_sync_wrap(*args, **kwargs): coroutine = function(*args, **kwargs) - try: - loop = asyncio.get_event_loop() - except RuntimeError: - loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) + loop = _ensure_event_loop() if threading.current_thread() is threading.main_thread() or not main_loop.is_running(): if loop.is_running(): diff --git a/pyrogram/types/__init__.py b/pyrogram/types/__init__.py index e0859bba..fd130c1a 100644 --- a/pyrogram/types/__init__.py +++ b/pyrogram/types/__init__.py @@ -17,12 +17,19 @@ # along with Pyrogram. If not, see . from .authorization import * +from .boosts import * from .bots_and_keyboards import * +from .business import * +from .calls import * from .inline_mode import * from .input_media import * from .input_message_content import * from .list import List from .messages_and_media import * from .object import Object +from .payments import * +from .stats import * +from .stories import * +from .todo import * from .update import * from .user_and_chats import * diff --git a/pyrogram/types/boosts/__init__.py b/pyrogram/types/boosts/__init__.py new file mode 100644 index 00000000..6c15114c --- /dev/null +++ b/pyrogram/types/boosts/__init__.py @@ -0,0 +1,36 @@ +# 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 .boost import Boost +from .boost_status import BoostStatus +from .boosts_list import BoostsList +from .giveaway_results import GiveawayResults +from .prepaid_giveaway import PrepaidGiveaway +from .my_boost import MyBoost +from .my_boosts import MyBoosts + + +__all__ = [ + "Boost", + "BoostStatus", + "BoostsList", + "GiveawayResults", + "PrepaidGiveaway", + "MyBoost", + "MyBoosts" +] diff --git a/pyrogram/types/boosts/boost.py b/pyrogram/types/boosts/boost.py new file mode 100644 index 00000000..407b0bbe --- /dev/null +++ b/pyrogram/types/boosts/boost.py @@ -0,0 +1,123 @@ +# 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 Optional + +import pyrogram +from pyrogram import raw, types, utils +from ..object import Object + + +class Boost(Object): + """Represents a channel boost. + + Parameters: + id (``str``): + Unique identifier of the boost. + + user (:obj:`~pyrogram.types.User`, *optional*): + The user who boosted (if not from a giveaway). + + date (:py:obj:`~datetime.datetime`): + When the boost was applied. + + expires (:py:obj:`~datetime.datetime`): + When the boost expires. + + giveaway_message_id (``int``, *optional*): + For giveaway boosts: the giveaway message ID. + + stars (``int``, *optional*): + For Telegram Stars boosts: amount of stars used. + + multiplier (``int``, *optional*): + Boost multiplier (for Telegram Premium users). + + is_gift (``bool``, *optional*): + Whether this boost was a gift. + + is_giveaway (``bool``, *optional*): + Whether this boost is from a giveaway. + + is_unclaimed (``bool``, *optional*): + Whether this giveaway boost was unclaimed. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + id: str, + user: "types.User" = None, + date: datetime = None, + expires: datetime = None, + giveaway_message_id: int = None, + stars: int = None, + multiplier: int = None, + is_gift: bool = None, + is_giveaway: bool = None, + is_unclaimed: bool = None + ): + super().__init__(client) + + self.id = id + self.user = user + self.date = date + self.expires = expires + self.giveaway_message_id = giveaway_message_id + self.stars = stars + self.multiplier = multiplier + self.is_gift = is_gift + self.is_giveaway = is_giveaway + self.is_unclaimed = is_unclaimed + + @staticmethod + def _parse( + client: "pyrogram.Client", + boost: "raw.types.Boost", + users: dict + ) -> "Boost": + user = None + if boost.user_id and boost.user_id in users: + user = types.User._parse(client, users[boost.user_id]) + + return Boost( + client=client, + id=boost.id, + user=user, + date=utils.timestamp_to_datetime(boost.date), + expires=utils.timestamp_to_datetime(boost.expires), + giveaway_message_id=boost.giveaway_msg_id, + stars=boost.stars, + multiplier=boost.multiplier, + is_gift=boost.gift or None, + is_giveaway=boost.giveaway or None, + is_unclaimed=boost.unclaimed or None + ) + + @staticmethod + def _parse_list( + client: "pyrogram.Client", + boosts: list, + users: dict + ) -> list: + return types.List([ + Boost._parse(client, b, users) + for b in boosts + ]) diff --git a/pyrogram/types/boosts/boost_status.py b/pyrogram/types/boosts/boost_status.py new file mode 100644 index 00000000..f1646509 --- /dev/null +++ b/pyrogram/types/boosts/boost_status.py @@ -0,0 +1,107 @@ +# 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, List + +import pyrogram +from pyrogram import raw, types +from ..object import Object + + +class BoostStatus(Object): + """Represents a channel's boost status. + + Parameters: + level (``int``): + Current boost level of the channel. + + current_level_boosts (``int``): + Number of boosts at current level. + + boosts (``int``): + Total number of boosts. + + next_level_boosts (``int``, *optional*): + Boosts needed for next level. + + premium_audience_percent (``float``, *optional*): + Percentage of premium audience. + + prepaid_giveaways (List of :obj:`~pyrogram.types.PrepaidGiveaway`, *optional*): + List of prepaid giveaways. + + my_boost (``bool``, *optional*): + Whether the current user has boosted. + + my_boost_slots (List of ``int``, *optional*): + Boost slots used by current user. + + boost_url (``str``, *optional*): + URL for boosting the channel. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + level: int = None, + current_level_boosts: int = None, + boosts: int = None, + next_level_boosts: int = None, + premium_audience_percent: float = None, + prepaid_giveaways: List["types.PrepaidGiveaway"] = None, + my_boost: bool = None, + my_boost_slots: List[int] = None, + boost_url: str = None + ): + super().__init__(client) + + self.level = level + self.current_level_boosts = current_level_boosts + self.boosts = boosts + self.next_level_boosts = next_level_boosts + self.premium_audience_percent = premium_audience_percent + self.prepaid_giveaways = prepaid_giveaways + self.my_boost = my_boost + self.my_boost_slots = my_boost_slots + self.boost_url = boost_url + + @staticmethod + def _parse( + client: "pyrogram.Client", + status: "raw.types.premium.BoostsStatus" + ) -> "BoostStatus": + prepaid = None + if hasattr(status, 'prepaid_giveaways') and status.prepaid_giveaways: + prepaid = [ + types.PrepaidGiveaway._parse(client, g) + for g in status.prepaid_giveaways + ] + + return BoostStatus( + client=client, + level=status.level, + current_level_boosts=status.current_level_boosts, + boosts=status.boosts, + next_level_boosts=status.next_level_boosts if hasattr(status, 'next_level_boosts') else None, + premium_audience_percent=status.premium_audience.part if hasattr(status, 'premium_audience') and status.premium_audience else None, + prepaid_giveaways=types.List(prepaid) if prepaid else None, + my_boost=status.my_boost or None, + my_boost_slots=list(status.my_boost_slots) if hasattr(status, 'my_boost_slots') and status.my_boost_slots else None, + boost_url=status.boost_url if hasattr(status, 'boost_url') else None + ) diff --git a/pyrogram/types/boosts/boosts_list.py b/pyrogram/types/boosts/boosts_list.py new file mode 100644 index 00000000..f123530d --- /dev/null +++ b/pyrogram/types/boosts/boosts_list.py @@ -0,0 +1,78 @@ +# 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 + +import pyrogram +from pyrogram import raw, types +from ..object import Object + + +class BoostsList(Object): + """List of boosts. + + Parameters: + count (``int``): + Total number of boosts. + + boosts (List of :obj:`~pyrogram.types.Boost`): + List of boosts. + + next_offset (``str``, *optional*): + Offset for pagination. + + users (List of :obj:`~pyrogram.types.User`, *optional*): + List of users mentioned in the boosts. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + count: int, + boosts: List["types.Boost"], + next_offset: str = None, + users: List["types.User"] = None + ): + super().__init__(client) + + self.count = count + self.boosts = boosts + self.next_offset = next_offset + self.users = users + + @staticmethod + def _parse( + client: "pyrogram.Client", + boosts_list: "raw.types.premium.BoostsList", + ) -> "BoostsList": + users = {u.id: u for u in boosts_list.users} + + return BoostsList( + client=client, + count=boosts_list.count, + boosts=types.List([ + types.Boost._parse(client, boost, users) + for boost in boosts_list.boosts + ]), + next_offset=boosts_list.next_offset, + users=types.List([ + types.User._parse(client, user) + for user in boosts_list.users + ]) + ) diff --git a/pyrogram/types/boosts/giveaway_results.py b/pyrogram/types/boosts/giveaway_results.py new file mode 100644 index 00000000..e43428e9 --- /dev/null +++ b/pyrogram/types/boosts/giveaway_results.py @@ -0,0 +1,156 @@ +# 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 Optional, List + +import pyrogram +from pyrogram import raw, types, utils +from ..object import Object + + +class GiveawayResults(Object): + """Represents the results of a giveaway. + + Parameters: + chat (:obj:`~pyrogram.types.Chat`): + The chat that created the giveaway. + + giveaway_message_id (``int``): + The message ID of the giveaway. + + winners_count (``int``): + Total number of winners. + + unclaimed_count (``int``): + Number of unclaimed prizes. + + winners (List of :obj:`~pyrogram.types.User`): + List of winner users. + + launch_message_id (``int``, *optional*): + Message ID of the launch message. + + additional_chats (List of :obj:`~pyrogram.types.Chat`, *optional*): + Additional channels that were part of the giveaway. + + date (``datetime``, *optional*): + When the giveaway started. + + until_date (``datetime``, *optional*): + When the giveaway ended. + + months (``int``, *optional*): + Number of months of Premium subscription as prize. + + stars (``int``, *optional*): + Amount of Telegram Stars as prize. + + prize_description (``str``, *optional*): + Description of additional prizes. + + is_only_new_subscribers (``bool``, *optional*): + Whether the giveaway was only for new subscribers. + + is_refunded (``bool``, *optional*): + Whether the giveaway was refunded. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + chat: "types.Chat" = None, + giveaway_message_id: int = None, + winners_count: int = None, + unclaimed_count: int = None, + winners: List["types.User"] = None, + launch_message_id: int = None, + additional_chats: List["types.Chat"] = None, + date: datetime = None, + until_date: datetime = None, + months: int = None, + stars: int = None, + prize_description: str = None, + is_only_new_subscribers: bool = None, + is_refunded: bool = None + ): + super().__init__(client) + + self.chat = chat + self.giveaway_message_id = giveaway_message_id + self.winners_count = winners_count + self.unclaimed_count = unclaimed_count + self.winners = winners + self.launch_message_id = launch_message_id + self.additional_chats = additional_chats + self.date = date + self.until_date = until_date + self.months = months + self.stars = stars + self.prize_description = prize_description + self.is_only_new_subscribers = is_only_new_subscribers + self.is_refunded = is_refunded + + @staticmethod + def _parse( + client: "pyrogram.Client", + results: "raw.types.MessageMediaGiveawayResults", + users: dict, + chats: dict + ) -> "GiveawayResults": + # Parse the main chat + chat = None + if results.channel_id: + peer = chats.get(results.channel_id) + if peer: + chat = types.Chat._parse_channel_chat(client, peer) + + # Parse winners + winners = [] + if results.winners: + for user_id in results.winners: + if user_id in users: + winners.append(types.User._parse(client, users[user_id])) + + # Parse additional chats + additional_chats = [] + if hasattr(results, 'additional_peers') and results.additional_peers: + for peer_id in results.additional_peers: + if peer_id in chats: + additional_chats.append( + types.Chat._parse_channel_chat(client, chats[peer_id]) + ) + + return GiveawayResults( + client=client, + chat=chat, + giveaway_message_id=results.launch_msg_id, + winners_count=results.winners_count, + unclaimed_count=results.unclaimed_count, + winners=types.List(winners) if winners else None, + launch_message_id=results.launch_msg_id, + additional_chats=types.List(additional_chats) if additional_chats else None, + date=utils.timestamp_to_datetime(results.start_date) if hasattr(results, 'start_date') and results.start_date else None, + until_date=utils.timestamp_to_datetime(results.until_date) if hasattr(results, 'until_date') and results.until_date else None, + months=results.months if hasattr(results, 'months') else None, + stars=results.stars if hasattr(results, 'stars') else None, + prize_description=results.prize_description if hasattr(results, 'prize_description') else None, + is_only_new_subscribers=results.only_new_subscribers if hasattr(results, 'only_new_subscribers') else None, + is_refunded=results.refunded if hasattr(results, 'refunded') else None + ) diff --git a/pyrogram/types/boosts/my_boost.py b/pyrogram/types/boosts/my_boost.py new file mode 100644 index 00000000..fe1d06ed --- /dev/null +++ b/pyrogram/types/boosts/my_boost.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 typing import Optional + +import pyrogram +from pyrogram import raw, types, utils +from ..object import Object + + +class MyBoost(Object): + """Represents a boost applied by the current user. + + Parameters: + slot (``int``): + The boost slot identifier. + + peer (:obj:`~pyrogram.types.Chat`, *optional*): + The chat where the boost is applied. + + date (``datetime``): + Date when the boost was applied. + + expires (``datetime``): + Date when the boost will expire. + + cooldown_until_date (``datetime``, *optional*): + Date until which the boost slot is on cooldown. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + slot: int, + peer: "types.Chat" = None, + date: datetime, + expires: datetime, + cooldown_until_date: datetime = None + ): + super().__init__(client) + + self.slot = slot + self.peer = peer + self.date = date + self.expires = expires + self.cooldown_until_date = cooldown_until_date + + @staticmethod + def _parse( + client: "pyrogram.Client", + boost: "raw.types.MyBoost", + users: dict = None, + chats: dict = None + ) -> "MyBoost": + return MyBoost( + client=client, + slot=boost.slot, + peer=types.Chat._parse(client, boost.peer, users, chats) if boost.peer else None, + date=utils.timestamp_to_datetime(boost.date), + expires=utils.timestamp_to_datetime(boost.expires), + cooldown_until_date=utils.timestamp_to_datetime(boost.cooldown_until_date) if boost.cooldown_until_date else None + ) diff --git a/pyrogram/types/boosts/my_boosts.py b/pyrogram/types/boosts/my_boosts.py new file mode 100644 index 00000000..1a8e0f0b --- /dev/null +++ b/pyrogram/types/boosts/my_boosts.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 +from ..object import Object + + +class MyBoosts(Object): + """Represents a list of boosts applied by the current user. + + Parameters: + my_boosts (List of :obj:`~pyrogram.types.MyBoost`): + List of applied boosts. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + my_boosts: List["types.MyBoost"] + ): + super().__init__(client) + + self.my_boosts = my_boosts + + @staticmethod + def _parse( + client: "pyrogram.Client", + boosts: "raw.types.premium.MyBoosts" + ) -> "MyBoosts": + users = {u.id: u for u in boosts.users} + chats = {c.id: c for c in boosts.chats} + + return MyBoosts( + client=client, + my_boosts=types.List([ + types.MyBoost._parse(client, b, users, chats) + for b in boosts.my_boosts + ]) + ) diff --git a/pyrogram/types/boosts/prepaid_giveaway.py b/pyrogram/types/boosts/prepaid_giveaway.py new file mode 100644 index 00000000..a87d44a3 --- /dev/null +++ b/pyrogram/types/boosts/prepaid_giveaway.py @@ -0,0 +1,76 @@ +# 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 + +import pyrogram +from pyrogram import raw, utils +from ..object import Object + + +class PrepaidGiveaway(Object): + """Represents a prepaid giveaway. + + Parameters: + id (``str``): + Unique identifier of the prepaid giveaway. + + quantity (``int``): + Number of giveaway prizes. + + months (``int``, *optional*): + Number of months of Premium subscription as prize. + + stars (``int``, *optional*): + Amount of Telegram Stars as prize. + + date (:py:obj:`~datetime.datetime`): + When the giveaway was prepaid. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + id: str = None, + quantity: int = None, + months: int = None, + stars: int = None, + date: datetime = None + ): + super().__init__(client) + + self.id = id + self.quantity = quantity + self.months = months + self.stars = stars + self.date = date + + @staticmethod + def _parse( + client: "pyrogram.Client", + giveaway: "raw.types.PrepaidGiveaway" + ) -> "PrepaidGiveaway": + return PrepaidGiveaway( + client=client, + id=str(giveaway.id), + quantity=giveaway.quantity, + months=giveaway.months if hasattr(giveaway, 'months') else None, + stars=giveaway.stars if hasattr(giveaway, 'stars') else None, + date=utils.timestamp_to_datetime(giveaway.date) + ) diff --git a/pyrogram/types/business/__init__.py b/pyrogram/types/business/__init__.py new file mode 100644 index 00000000..d2a9a2f5 --- /dev/null +++ b/pyrogram/types/business/__init__.py @@ -0,0 +1,42 @@ +# 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 .business_info import BusinessInfo +from .business_hours import BusinessWorkHours, BusinessWeeklyOpen +from .business_location import BusinessLocation +from .business_intro import BusinessIntro +from .business_message import BusinessGreetingMessage, BusinessAwayMessage, BusinessRecipients +from .business_connection import BusinessConnection +from .quick_reply import QuickReply +from .business_chat_link import BusinessChatLink +from .business_bot_recipients import BusinessBotRecipients + +__all__ = [ + "BusinessInfo", + "BusinessWorkHours", + "BusinessWeeklyOpen", + "BusinessLocation", + "BusinessIntro", + "BusinessGreetingMessage", + "BusinessAwayMessage", + "BusinessRecipients", + "BusinessConnection", + "QuickReply", + "BusinessChatLink", + "BusinessBotRecipients" +] diff --git a/pyrogram/types/business/business_bot_recipients.py b/pyrogram/types/business/business_bot_recipients.py new file mode 100644 index 00000000..da4d0768 --- /dev/null +++ b/pyrogram/types/business/business_bot_recipients.py @@ -0,0 +1,91 @@ +# 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 + +import pyrogram +from pyrogram import raw, types +from ..object import Object + + +class BusinessBotRecipients(Object): + """Represents recipients for a business bot. + + Parameters: + existing_chats (``bool``, *optional*): + True, if existing chats are included. + + new_chats (``bool``, *optional*): + True, if new chats are included. + + contacts (``bool``, *optional*): + True, if contacts are included. + + non_contacts (``bool``, *optional*): + True, if non-contacts are included. + + exclude_selected (``bool``, *optional*): + True, if selected users are excluded. + + users (List of ``int``, *optional*): + List of included user IDs. + + exclude_users (List of ``int``, *optional*): + List of excluded user IDs. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + existing_chats: bool = None, + new_chats: bool = None, + contacts: bool = None, + non_contacts: bool = None, + exclude_selected: bool = None, + users: List[int] = None, + exclude_users: List[int] = None + ): + super().__init__(client) + + self.existing_chats = existing_chats + self.new_chats = new_chats + self.contacts = contacts + self.non_contacts = non_contacts + self.exclude_selected = exclude_selected + self.users = users + self.exclude_users = exclude_users + + @staticmethod + def _parse( + client: "pyrogram.Client", + recipients: "raw.types.BusinessBotRecipients" + ) -> Optional["BusinessBotRecipients"]: + if not recipients: + return None + + return BusinessBotRecipients( + client=client, + existing_chats=recipients.existing_chats, + new_chats=recipients.new_chats, + contacts=recipients.contacts, + non_contacts=recipients.non_contacts, + exclude_selected=recipients.exclude_selected, + users=types.List(recipients.users) if recipients.users else None, + exclude_users=types.List(recipients.exclude_users) if recipients.exclude_users else None + ) diff --git a/pyrogram/types/business/business_chat_link.py b/pyrogram/types/business/business_chat_link.py new file mode 100644 index 00000000..d6f2aeac --- /dev/null +++ b/pyrogram/types/business/business_chat_link.py @@ -0,0 +1,132 @@ +# 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 + +import pyrogram +from pyrogram import raw, types +from ..object import Object + + +class BusinessChatLink(Object): + """Represents a business chat link. + + Parameters: + link (``str``): + The link. + + message (``str``): + The message associated with the link. + + views (``int``): + Number of views. + + title (``str``, *optional*): + Title of the link. + + entities (List of :obj:`~pyrogram.types.MessageEntity`, *optional*): + List of special entities that appear in the message. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + link: str, + message: str, + views: int, + title: str = None, + entities: List["types.MessageEntity"] = None + ): + super().__init__(client) + + self.link = link + self.message = message + self.views = views + self.title = title + self.entities = entities + + @staticmethod + def _parse( + client: "pyrogram.Client", + link: "raw.types.BusinessChatLink" + ) -> "BusinessChatLink": + return BusinessChatLink( + client=client, + link=link.link, + message=link.message, + views=link.views, + title=link.title, + entities=types.List([ + types.MessageEntity._parse(client, entity, {}) + for entity in link.entities + ]) if link.entities else None + ) + + @property + def slug(self) -> str: + """The slug of the business chat link.""" + return self.link.split("/")[-1] + + async def edit( + self, + message: str = None, + title: str = None, + entities: List["types.MessageEntity"] = None + ) -> "BusinessChatLink": + """Bound method *edit* of :obj:`~pyrogram.types.BusinessChatLink`. + + Use as a shortcut for: + .. code-block:: python + + await link.edit(message="New message") + + Parameters: + message (``str``, *optional*): + The new message associated with the link. + + title (``str``, *optional*): + The new title of the link. + + entities (List of :obj:`~pyrogram.types.MessageEntity`, *optional*): + List of special entities that appear in the message. + + Returns: + :obj:`~pyrogram.types.BusinessChatLink`: On success, the edited business chat link is returned. + """ + return await self._client.edit_business_chat_link( + slug=self.slug, + link=raw.types.InputBusinessChatLink( + message=message or self.message, + title=title or self.title, + entities=entities or self.entities + ) + ) + + async def delete(self) -> bool: + """Bound method *delete* of :obj:`~pyrogram.types.BusinessChatLink`. + + Use as a shortcut for: + .. code-block:: python + + await link.delete() + + Returns: + ``bool``: True on success. + """ + return await self._client.delete_business_chat_link(slug=self.slug) diff --git a/pyrogram/types/business/business_connection.py b/pyrogram/types/business/business_connection.py new file mode 100644 index 00000000..b681565b --- /dev/null +++ b/pyrogram/types/business/business_connection.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 datetime import datetime +from typing import Optional + +import pyrogram +from pyrogram import raw, types, utils +from ..object import Object + + +class BusinessConnection(Object): + """Represents a business bot connection. + + Parameters: + id (``str``): + Unique identifier of the connection. + + user (:obj:`~pyrogram.types.User`): + The business account user. + + dc_id (``int``): + Data center ID of the user. + + date (:py:obj:`~datetime.datetime`): + When the connection was established. + + can_reply (``bool``, *optional*): + Whether the bot can reply to messages. + + disabled (``bool``, *optional*): + Whether the connection is disabled. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + id: str, + user: "types.User" = None, + dc_id: int = None, + date: datetime = None, + can_reply: bool = None, + disabled: bool = None + ): + super().__init__(client) + + self.id = id + self.user = user + self.dc_id = dc_id + self.date = date + self.can_reply = can_reply + self.disabled = disabled + + @staticmethod + def _parse( + client: "pyrogram.Client", + connection: "raw.types.BotBusinessConnection", + users: dict + ) -> "BusinessConnection": + user = None + if connection.user_id and connection.user_id in users: + user = types.User._parse(client, users[connection.user_id]) + + return BusinessConnection( + client=client, + id=connection.connection_id, + user=user, + dc_id=connection.dc_id, + date=utils.timestamp_to_datetime(connection.date), + can_reply=connection.can_reply or None, + disabled=connection.disabled or None + ) diff --git a/pyrogram/types/business/business_hours.py b/pyrogram/types/business/business_hours.py new file mode 100644 index 00000000..b76332fa --- /dev/null +++ b/pyrogram/types/business/business_hours.py @@ -0,0 +1,110 @@ +# 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 + +import pyrogram +from pyrogram import raw +from ..object import Object + + +class BusinessWeeklyOpen(Object): + """Represents a time interval during which a business is open. + + Parameters: + start_minute (``int``): + Start time of the open interval, in minutes from the start of the week + (Monday 00:00). Range: 0-10080 (7 days * 24 hours * 60 minutes). + + end_minute (``int``): + End time of the open interval, in minutes from the start of the week. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + start_minute: int, + end_minute: int + ): + super().__init__(client) + + self.start_minute = start_minute + self.end_minute = end_minute + + @staticmethod + def _parse( + client: "pyrogram.Client", + weekly_open: "raw.types.BusinessWeeklyOpen" + ) -> "BusinessWeeklyOpen": + return BusinessWeeklyOpen( + client=client, + start_minute=weekly_open.start_minute, + end_minute=weekly_open.end_minute + ) + + +class BusinessWorkHours(Object): + """Represents the work hours of a business. + + Parameters: + timezone_id (``str``): + Timezone ID (e.g., "Europe/Rome", "America/New_York"). + + weekly_open (List of :obj:`~pyrogram.types.BusinessWeeklyOpen`): + List of time intervals during which the business is open. + + open_now (``bool``, *optional*): + Whether the business is currently open. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + timezone_id: str, + weekly_open: List["BusinessWeeklyOpen"] = None, + open_now: bool = None + ): + super().__init__(client) + + self.timezone_id = timezone_id + self.weekly_open = weekly_open + self.open_now = open_now + + @staticmethod + def _parse( + client: "pyrogram.Client", + work_hours: "raw.types.BusinessWorkHours" + ) -> Optional["BusinessWorkHours"]: + if work_hours is None: + return None + + from pyrogram import types + + weekly_open = types.List([ + BusinessWeeklyOpen._parse(client, wo) + for wo in work_hours.weekly_open + ]) if work_hours.weekly_open else None + + return BusinessWorkHours( + client=client, + timezone_id=work_hours.timezone_id, + weekly_open=weekly_open, + open_now=work_hours.open_now or None + ) diff --git a/pyrogram/types/business/business_info.py b/pyrogram/types/business/business_info.py new file mode 100644 index 00000000..eb8328c4 --- /dev/null +++ b/pyrogram/types/business/business_info.py @@ -0,0 +1,93 @@ +# 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 + +import pyrogram +from pyrogram import raw, types +from ..object import Object + +from .business_hours import BusinessWorkHours +from .business_location import BusinessLocation +from .business_intro import BusinessIntro +from .business_message import BusinessGreetingMessage, BusinessAwayMessage + + +class BusinessInfo(Object): + """Aggregated business information for a user. + + Parameters: + work_hours (:obj:`~pyrogram.types.BusinessWorkHours`, *optional*): + Business work hours. + + location (:obj:`~pyrogram.types.BusinessLocation`, *optional*): + Business location. + + intro (:obj:`~pyrogram.types.BusinessIntro`, *optional*): + Business introduction. + + greeting_message (:obj:`~pyrogram.types.BusinessGreetingMessage`, *optional*): + Automated greeting message settings. + + away_message (:obj:`~pyrogram.types.BusinessAwayMessage`, *optional*): + Automated away message settings. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + work_hours: "BusinessWorkHours" = None, + location: "BusinessLocation" = None, + intro: "BusinessIntro" = None, + greeting_message: "BusinessGreetingMessage" = None, + away_message: "BusinessAwayMessage" = None + ): + super().__init__(client) + + self.work_hours = work_hours + self.location = location + self.intro = intro + self.greeting_message = greeting_message + self.away_message = away_message + + @staticmethod + async def _parse( + client: "pyrogram.Client", + user_full: "raw.types.UserFull" + ) -> Optional["BusinessInfo"]: + """Parse business info from a UserFull object.""" + + work_hours = BusinessWorkHours._parse(client, getattr(user_full, 'business_work_hours', None)) + location = BusinessLocation._parse(client, getattr(user_full, 'business_location', None)) + intro = await BusinessIntro._parse(client, getattr(user_full, 'business_intro', None)) + greeting_message = BusinessGreetingMessage._parse(client, getattr(user_full, 'business_greeting_message', None)) + away_message = BusinessAwayMessage._parse(client, getattr(user_full, 'business_away_message', None)) + + # Return None if no business info is available + if not any([work_hours, location, intro, greeting_message, away_message]): + return None + + return BusinessInfo( + client=client, + work_hours=work_hours, + location=location, + intro=intro, + greeting_message=greeting_message, + away_message=away_message + ) diff --git a/pyrogram/types/business/business_intro.py b/pyrogram/types/business/business_intro.py new file mode 100644 index 00000000..78a72706 --- /dev/null +++ b/pyrogram/types/business/business_intro.py @@ -0,0 +1,71 @@ +# 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 + +import pyrogram +from pyrogram import raw, types +from ..object import Object + + +class BusinessIntro(Object): + """Represents a business introduction message shown to new contacts. + + Parameters: + title (``str``, *optional*): + Title of the introduction. + + description (``str``, *optional*): + Description text of the introduction. + + sticker (:obj:`~pyrogram.types.Sticker`, *optional*): + Sticker shown in the introduction. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + title: str = None, + description: str = None, + sticker: "types.Sticker" = None + ): + super().__init__(client) + + self.title = title + self.description = description + self.sticker = sticker + + @staticmethod + async def _parse( + client: "pyrogram.Client", + intro: "raw.types.BusinessIntro" + ) -> Optional["BusinessIntro"]: + if intro is None: + return None + + sticker = None + if intro.sticker: + sticker = await types.Sticker._parse(client, intro.sticker, {}) + + return BusinessIntro( + client=client, + title=intro.title, + description=intro.description, + sticker=sticker + ) diff --git a/pyrogram/types/business/business_location.py b/pyrogram/types/business/business_location.py new file mode 100644 index 00000000..1ebe73d4 --- /dev/null +++ b/pyrogram/types/business/business_location.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 . + +from typing import Optional + +import pyrogram +from pyrogram import raw, types +from ..object import Object + + +class BusinessLocation(Object): + """Represents the location of a business. + + Parameters: + address (``str``): + Text address of the business location. + + geo (:obj:`~pyrogram.types.Location`, *optional*): + Geographic coordinates of the location. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + address: str, + geo: "types.Location" = None + ): + super().__init__(client) + + self.address = address + self.geo = geo + + @staticmethod + def _parse( + client: "pyrogram.Client", + location: "raw.types.BusinessLocation" + ) -> Optional["BusinessLocation"]: + if location is None: + return None + + geo = None + if location.geo_point and isinstance(location.geo_point, raw.types.GeoPoint): + geo = types.Location( + longitude=location.geo_point.long, + latitude=location.geo_point.lat, + client=client + ) + + return BusinessLocation( + client=client, + address=location.address, + geo=geo + ) diff --git a/pyrogram/types/business/business_message.py b/pyrogram/types/business/business_message.py new file mode 100644 index 00000000..be97eff3 --- /dev/null +++ b/pyrogram/types/business/business_message.py @@ -0,0 +1,208 @@ +# 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 + +import pyrogram +from pyrogram import raw, types, utils +from ..object import Object + + +class BusinessRecipients(Object): + """Represents recipients for business automated messages. + + Parameters: + existing_chats (``bool``, *optional*): + Apply to existing private chats. + + new_chats (``bool``, *optional*): + Apply to new private chats. + + contacts (``bool``, *optional*): + Apply to contacts. + + non_contacts (``bool``, *optional*): + Apply to non-contacts. + + exclude_selected (``bool``, *optional*): + If True, the users/chats below are excluded rather than included. + + users (List of ``int``, *optional*): + List of user IDs to include/exclude. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + existing_chats: bool = None, + new_chats: bool = None, + contacts: bool = None, + non_contacts: bool = None, + exclude_selected: bool = None, + users: List[int] = None + ): + super().__init__(client) + + self.existing_chats = existing_chats + self.new_chats = new_chats + self.contacts = contacts + self.non_contacts = non_contacts + self.exclude_selected = exclude_selected + self.users = users + + @staticmethod + def _parse( + client: "pyrogram.Client", + recipients: "raw.types.BusinessRecipients" + ) -> Optional["BusinessRecipients"]: + if recipients is None: + return None + + users = None + if recipients.users: + users = types.List(recipients.users) + + return BusinessRecipients( + client=client, + existing_chats=recipients.existing_chats or None, + new_chats=recipients.new_chats or None, + contacts=recipients.contacts or None, + non_contacts=recipients.non_contacts or None, + exclude_selected=recipients.exclude_selected or None, + users=users + ) + + +class BusinessGreetingMessage(Object): + """Represents an automated greeting message for business accounts. + + Parameters: + shortcut_id (``int``): + ID of the quick reply shortcut containing the message. + + recipients (:obj:`~pyrogram.types.BusinessRecipients`): + Recipients who will receive this greeting. + + no_activity_days (``int``): + Number of days of inactivity after which the greeting is sent. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + shortcut_id: int, + recipients: "BusinessRecipients" = None, + no_activity_days: int = None + ): + super().__init__(client) + + self.shortcut_id = shortcut_id + self.recipients = recipients + self.no_activity_days = no_activity_days + + @staticmethod + def _parse( + client: "pyrogram.Client", + message: "raw.types.BusinessGreetingMessage" + ) -> Optional["BusinessGreetingMessage"]: + if message is None: + return None + + return BusinessGreetingMessage( + client=client, + shortcut_id=message.shortcut_id, + recipients=BusinessRecipients._parse(client, message.recipients), + no_activity_days=message.no_activity_days + ) + + +class BusinessAwayMessage(Object): + """Represents an automated away message for business accounts. + + Parameters: + shortcut_id (``int``): + ID of the quick reply shortcut containing the message. + + recipients (:obj:`~pyrogram.types.BusinessRecipients`): + Recipients who will receive this away message. + + schedule_type (``str``): + Type of schedule. One of: "always", "outside_work_hours", "custom". + + offline_only (``bool``, *optional*): + Only send when the account is offline. + + start_date (:py:obj:`~datetime.datetime`, *optional*): + For "custom" schedule: start date. + + end_date (:py:obj:`~datetime.datetime`, *optional*): + For "custom" schedule: end date. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + shortcut_id: int, + recipients: "BusinessRecipients" = None, + schedule_type: str = None, + offline_only: bool = None, + start_date: "datetime" = None, + end_date: "datetime" = None + ): + super().__init__(client) + + self.shortcut_id = shortcut_id + self.recipients = recipients + self.schedule_type = schedule_type + self.offline_only = offline_only + self.start_date = start_date + self.end_date = end_date + + @staticmethod + def _parse( + client: "pyrogram.Client", + message: "raw.types.BusinessAwayMessage" + ) -> Optional["BusinessAwayMessage"]: + if message is None: + return None + + schedule_type = "always" + start_date = None + end_date = None + + if isinstance(message.schedule, raw.types.BusinessAwayMessageScheduleAlways): + schedule_type = "always" + elif isinstance(message.schedule, raw.types.BusinessAwayMessageScheduleOutsideWorkHours): + schedule_type = "outside_work_hours" + elif isinstance(message.schedule, raw.types.BusinessAwayMessageScheduleCustom): + schedule_type = "custom" + start_date = utils.timestamp_to_datetime(message.schedule.start_date) + end_date = utils.timestamp_to_datetime(message.schedule.end_date) + + return BusinessAwayMessage( + client=client, + shortcut_id=message.shortcut_id, + recipients=BusinessRecipients._parse(client, message.recipients), + schedule_type=schedule_type, + offline_only=message.offline_only or None, + start_date=start_date, + end_date=end_date + ) diff --git a/pyrogram/types/business/quick_reply.py b/pyrogram/types/business/quick_reply.py new file mode 100644 index 00000000..4ea502b8 --- /dev/null +++ b/pyrogram/types/business/quick_reply.py @@ -0,0 +1,151 @@ +# 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 + +import pyrogram +from pyrogram import raw, types +from ..object import Object + + +class QuickReply(Object): + """Represents a quick reply shortcut. + + Parameters: + shortcut_id (``int``): + Unique identifier of the shortcut. + + shortcut (``str``): + The shortcut name/trigger. + + top_message (``int``): + ID of the top message in the shortcut. + + count (``int``): + Total number of messages in the shortcut. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + shortcut_id: int, + shortcut: str, + top_message: int, + count: int + ): + super().__init__(client) + + self.shortcut_id = shortcut_id + self.shortcut = shortcut + self.top_message = top_message + self.count = count + + @staticmethod + def _parse( + client: "pyrogram.Client", + quick_reply: "raw.types.QuickReply" + ) -> "QuickReply": + return QuickReply( + client=client, + shortcut_id=quick_reply.shortcut_id, + shortcut=quick_reply.shortcut, + top_message=quick_reply.top_message, + count=quick_reply.count + ) + + async def get_messages(self) -> List["types.Message"]: + """Bound method *get_messages* of :obj:`~pyrogram.types.QuickReply`. + + Use as a shortcut for: + .. code-block:: python + + await quick_reply.get_messages() + + Returns: + List of :obj:`~pyrogram.types.Message`: On success, a list of messages is returned. + """ + return await self._client.get_quick_reply_messages(shortcut_id=self.shortcut_id) + + async def send_messages(self, chat_id: Union[int, str]) -> "types.Updates": + """Bound method *send_messages* of :obj:`~pyrogram.types.QuickReply`. + + Use as a shortcut for: + .. code-block:: python + + await quick_reply.send_messages(chat_id) + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier (int) or username (str) of the target chat. + + Returns: + :obj:`~pyrogram.types.Updates`: On success, the updates object is returned. + """ + messages = await self.get_messages() + return await self._client.send_quick_reply_messages( + chat_id=chat_id, + shortcut_id=self.shortcut_id, + message_ids=[m.id for m in messages] + ) + + async def edit(self, shortcut: str) -> bool: + """Bound method *edit* of :obj:`~pyrogram.types.QuickReply`. + + Use as a shortcut for: + .. code-block:: python + + await quick_reply.edit("new_shortcut") + + Parameters: + shortcut (``str``): + The new name for the shortcut. + + Returns: + ``bool``: True on success. + """ + return await self._client.edit_quick_reply_shortcut( + shortcut_id=self.shortcut_id, + shortcut=shortcut + ) + + async def delete(self) -> bool: + """Bound method *delete* of :obj:`~pyrogram.types.QuickReply`. + + Use as a shortcut for: + .. code-block:: python + + await quick_reply.delete() + + Returns: + ``bool``: True on success. + """ + return await self._client.delete_quick_reply_shortcut(shortcut_id=self.shortcut_id) + + @staticmethod + def _parse_list( + client: "pyrogram.Client", + quick_replies: List["raw.types.QuickReply"] + ) -> Optional[List["QuickReply"]]: + if not quick_replies: + return None + + return types.List([ + QuickReply._parse(client, qr) + for qr in quick_replies + ]) diff --git a/pyrogram/types/calls/__init__.py b/pyrogram/types/calls/__init__.py new file mode 100644 index 00000000..486e0bc9 --- /dev/null +++ b/pyrogram/types/calls/__init__.py @@ -0,0 +1,27 @@ +# 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 .group_call import GroupCall +from .group_call_participant import GroupCallParticipant +from .conference_call import ConferenceCall + +__all__ = [ + "GroupCall", + "GroupCallParticipant", + "ConferenceCall" +] diff --git a/pyrogram/types/calls/conference_call.py b/pyrogram/types/calls/conference_call.py new file mode 100644 index 00000000..7083478f --- /dev/null +++ b/pyrogram/types/calls/conference_call.py @@ -0,0 +1,117 @@ +# 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 Optional, List + +import pyrogram +from pyrogram import raw, types, utils +from ..object import Object + + +class ConferenceCall(Object): + """Represents a conference call (1-on-1 video/voice call upgraded to group call). + + Parameters: + id (``int``): + Unique identifier of the conference call. + + access_hash (``int``): + Access hash for the call. + + call (:obj:`~pyrogram.types.GroupCall`, *optional*): + The underlying group call. + + participants (List of :obj:`~pyrogram.types.GroupCallParticipant`, *optional*): + List of participants in the call. + + chat (:obj:`~pyrogram.types.Chat`, *optional*): + The chat associated with this conference. + + is_conference (``bool``, *optional*): + Whether this is a conference call. + + is_can_invite (``bool``, *optional*): + Whether you can invite more participants. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + id: int = None, + access_hash: int = None, + call: "types.GroupCall" = None, + participants: List["types.GroupCallParticipant"] = None, + chat: "types.Chat" = None, + is_conference: bool = None, + is_can_invite: bool = None + ): + super().__init__(client) + + self.id = id + self.access_hash = access_hash + self.call = call + self.participants = participants + self.chat = chat + self.is_conference = is_conference + self.is_can_invite = is_can_invite + + @staticmethod + def _parse( + client: "pyrogram.Client", + call: "raw.types.phone.GroupCall", + users: dict = None, + chats: dict = None + ) -> "ConferenceCall": + users = users or {u.id: u for u in call.users} if hasattr(call, 'users') else {} + chats = chats or {c.id: c for c in call.chats} if hasattr(call, 'chats') else {} + + group_call = None + if hasattr(call, 'call') and call.call: + group_call = types.GroupCall._parse(client, call.call) + + participants = [] + if hasattr(call, 'participants') and call.participants: + for p in call.participants: + participants.append( + types.GroupCallParticipant._parse(client, p, users, chats) + ) + + # Get associated chat + chat = None + if hasattr(call, 'call') and call.call and hasattr(call.call, 'id'): + for chat_raw in chats.values(): + if hasattr(chat_raw, 'call') and chat_raw.call: + if chat_raw.call.id == call.call.id: + if hasattr(chat_raw, 'broadcast') and chat_raw.broadcast: + chat = types.Chat._parse_channel_chat(client, chat_raw) + elif hasattr(chat_raw, 'megagroup') and chat_raw.megagroup: + chat = types.Chat._parse_channel_chat(client, chat_raw) + break + + return ConferenceCall( + client=client, + id=call.call.id if hasattr(call, 'call') and call.call else None, + access_hash=call.call.access_hash if hasattr(call, 'call') and call.call else None, + call=group_call, + participants=types.List(participants) if participants else None, + chat=chat, + is_conference=True, + is_can_invite=True + ) diff --git a/pyrogram/types/calls/group_call.py b/pyrogram/types/calls/group_call.py new file mode 100644 index 00000000..bee33b8f --- /dev/null +++ b/pyrogram/types/calls/group_call.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 datetime import datetime +from typing import Optional, List + +import pyrogram +from pyrogram import raw, types, utils +from ..object import Object + + +class GroupCall(Object): + """Represents a group call. + + Parameters: + id (``int``): + Unique identifier of the group call. + + access_hash (``int``): + Access hash for the group call. + + title (``str``, *optional*): + Title of the group call. + + participants_count (``int``): + Number of participants. + + version (``int``): + Current version of the call state. + + stream_dc_id (``int``, *optional*): + Data center ID for streaming. + + schedule_date (:py:obj:`~datetime.datetime`, *optional*): + Scheduled start date. + + is_active (``bool``, *optional*): + Whether the call is currently active. + + is_rtmp_stream (``bool``, *optional*): + Whether this is an RTMP stream. + + is_listeners_hidden (``bool``, *optional*): + Whether listener count is hidden. + + is_join_muted (``bool``, *optional*): + Whether users join muted. + + is_can_change_join_muted (``bool``, *optional*): + Whether join mute setting can be changed. + + is_video_started (``bool``, *optional*): + Whether video has been started. + + is_can_start_video (``bool``, *optional*): + Whether video can be started. + + is_record_started (``bool``, *optional*): + Whether recording is active. + + record_video_active (``bool``, *optional*): + Whether video recording is active. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + id: int = None, + access_hash: int = None, + title: str = None, + participants_count: int = None, + version: int = None, + stream_dc_id: int = None, + schedule_date: datetime = None, + is_active: bool = None, + is_rtmp_stream: bool = None, + is_listeners_hidden: bool = None, + is_join_muted: bool = None, + is_can_change_join_muted: bool = None, + is_video_started: bool = None, + is_can_start_video: bool = None, + is_record_started: bool = None, + record_video_active: bool = None + ): + super().__init__(client) + + self.id = id + self.access_hash = access_hash + self.title = title + self.participants_count = participants_count + self.version = version + self.stream_dc_id = stream_dc_id + self.schedule_date = schedule_date + self.is_active = is_active + self.is_rtmp_stream = is_rtmp_stream + self.is_listeners_hidden = is_listeners_hidden + self.is_join_muted = is_join_muted + self.is_can_change_join_muted = is_can_change_join_muted + self.is_video_started = is_video_started + self.is_can_start_video = is_can_start_video + self.is_record_started = is_record_started + self.record_video_active = record_video_active + + @staticmethod + def _parse( + client: "pyrogram.Client", + call: "raw.types.GroupCall" + ) -> "GroupCall": + return GroupCall( + client=client, + id=call.id, + access_hash=call.access_hash, + title=call.title if hasattr(call, 'title') else None, + participants_count=call.participants_count, + version=call.version, + stream_dc_id=call.stream_dc_id if hasattr(call, 'stream_dc_id') else None, + schedule_date=utils.timestamp_to_datetime(call.schedule_date) if hasattr(call, 'schedule_date') and call.schedule_date else None, + is_active=not call.schedule_date if hasattr(call, 'schedule_date') else True, + is_rtmp_stream=call.rtmp_stream if hasattr(call, 'rtmp_stream') else None, + is_listeners_hidden=call.listeners_hidden if hasattr(call, 'listeners_hidden') else None, + is_join_muted=call.join_muted if hasattr(call, 'join_muted') else None, + is_can_change_join_muted=call.can_change_join_muted if hasattr(call, 'can_change_join_muted') else None, + is_video_started=call.video_started if hasattr(call, 'video_started') else None, + is_can_start_video=call.can_start_video if hasattr(call, 'can_start_video') else None, + is_record_started=call.record_started if hasattr(call, 'record_started') else None, + record_video_active=call.record_video_active if hasattr(call, 'record_video_active') else None + ) diff --git a/pyrogram/types/calls/group_call_participant.py b/pyrogram/types/calls/group_call_participant.py new file mode 100644 index 00000000..a5f70f5f --- /dev/null +++ b/pyrogram/types/calls/group_call_participant.py @@ -0,0 +1,159 @@ +# 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 Optional + +import pyrogram +from pyrogram import raw, types, utils +from ..object import Object + + +class GroupCallParticipant(Object): + """Represents a participant in a group call. + + Parameters: + user (:obj:`~pyrogram.types.User`, *optional*): + The participant user. + + chat (:obj:`~pyrogram.types.Chat`, *optional*): + The participant chat (if joined as channel). + + date (:py:obj:`~datetime.datetime`): + When the participant joined. + + source (``int``): + Audio source identifier. + + volume (``int``, *optional*): + Participant volume (0-20000). + + is_muted (``bool``, *optional*): + Whether the participant is muted. + + is_muted_by_you (``bool``, *optional*): + Whether muted by current user. + + is_can_self_unmute (``bool``, *optional*): + Whether can self-unmute. + + is_left (``bool``, *optional*): + Whether has left the call. + + is_just_joined (``bool``, *optional*): + Whether just joined. + + is_versioned (``bool``, *optional*): + Whether participant state is versioned. + + is_video (``bool``, *optional*): + Whether video is enabled. + + is_screen (``bool``, *optional*): + Whether screen sharing is enabled. + + raise_hand_rating (``int``, *optional*): + Hand raise rating for ordering. + + video_timestamp (``int``, *optional*): + Video stream timestamp. + + presentation_timestamp (``int``, *optional*): + Presentation stream timestamp. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + user: "types.User" = None, + chat: "types.Chat" = None, + date: datetime = None, + source: int = None, + volume: int = None, + is_muted: bool = None, + is_muted_by_you: bool = None, + is_can_self_unmute: bool = None, + is_left: bool = None, + is_just_joined: bool = None, + is_versioned: bool = None, + is_video: bool = None, + is_screen: bool = None, + raise_hand_rating: int = None, + video_timestamp: int = None, + presentation_timestamp: int = None + ): + super().__init__(client) + + self.user = user + self.chat = chat + self.date = date + self.source = source + self.volume = volume + self.is_muted = is_muted + self.is_muted_by_you = is_muted_by_you + self.is_can_self_unmute = is_can_self_unmute + self.is_left = is_left + self.is_just_joined = is_just_joined + self.is_versioned = is_versioned + self.is_video = is_video + self.is_screen = is_screen + self.raise_hand_rating = raise_hand_rating + self.video_timestamp = video_timestamp + self.presentation_timestamp = presentation_timestamp + + @staticmethod + def _parse( + client: "pyrogram.Client", + participant: "raw.types.GroupCallParticipant", + users: dict, + chats: dict + ) -> "GroupCallParticipant": + user = None + chat = None + + peer = participant.peer + if isinstance(peer, raw.types.PeerUser): + if peer.user_id in users: + user = types.User._parse(client, users[peer.user_id]) + elif isinstance(peer, raw.types.PeerChannel): + if peer.channel_id in chats: + chat = types.Chat._parse_channel_chat(client, chats[peer.channel_id]) + elif isinstance(peer, raw.types.PeerChat): + if peer.chat_id in chats: + chat = types.Chat._parse_chat_chat(client, chats[peer.chat_id]) + + return GroupCallParticipant( + client=client, + user=user, + chat=chat, + date=utils.timestamp_to_datetime(participant.date), + source=participant.source, + volume=participant.volume if hasattr(participant, 'volume') else None, + is_muted=participant.muted if hasattr(participant, 'muted') else None, + is_muted_by_you=participant.muted_by_you if hasattr(participant, 'muted_by_you') else None, + is_can_self_unmute=participant.can_self_unmute if hasattr(participant, 'can_self_unmute') else None, + is_left=participant.left if hasattr(participant, 'left') else None, + is_just_joined=participant.just_joined if hasattr(participant, 'just_joined') else None, + is_versioned=participant.versioned if hasattr(participant, 'versioned') else None, + is_video=bool(participant.video) if hasattr(participant, 'video') else None, + is_screen=bool(participant.presentation) if hasattr(participant, 'presentation') else None, + raise_hand_rating=participant.raise_hand_rating if hasattr(participant, 'raise_hand_rating') else None, + video_timestamp=participant.video.audio_timestamp if hasattr(participant, 'video') and participant.video and hasattr(participant.video, 'audio_timestamp') else None, + presentation_timestamp=participant.presentation.audio_timestamp if hasattr(participant, 'presentation') and participant.presentation and hasattr(participant.presentation, 'audio_timestamp') else None + ) diff --git a/pyrogram/types/input_media/__init__.py b/pyrogram/types/input_media/__init__.py index a03d1e21..7bc06663 100644 --- a/pyrogram/types/input_media/__init__.py +++ b/pyrogram/types/input_media/__init__.py @@ -23,8 +23,9 @@ from .input_media_photo import InputMediaPhoto from .input_media_video import InputMediaVideo from .input_phone_contact import InputPhoneContact +from .input_media_todo import InputMediaTodo __all__ = [ "InputMedia", "InputMediaAnimation", "InputMediaAudio", "InputMediaDocument", "InputMediaPhoto", "InputMediaVideo", - "InputPhoneContact" + "InputPhoneContact", "InputMediaTodo" ] diff --git a/pyrogram/types/input_media/input_media_todo.py b/pyrogram/types/input_media/input_media_todo.py new file mode 100644 index 00000000..09e57ac2 --- /dev/null +++ b/pyrogram/types/input_media/input_media_todo.py @@ -0,0 +1,52 @@ +# 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 + +import pyrogram +from pyrogram import raw +from ..input_media.input_media import InputMedia + + +class InputMediaTodo(InputMedia): + """Represents a to-do list to be sent. + + Parameters: + title (``str``): + Title of the to-do list. + + items (List of :obj:`~pyrogram.types.TodoItem`): + List of to-do items. + + """ + + def __init__( + self, + title: str, + items: list + ): + super().__init__() + + self.title = title + self.items = items + + async def write(self, client: "pyrogram.Client", next_file_id: int) -> "raw.types.InputMediaTodo": + return raw.types.InputMediaTodo( + title=self.title, + items=[i.write() for i in self.items] + ) diff --git a/pyrogram/types/messages_and_media/__init__.py b/pyrogram/types/messages_and_media/__init__.py index f03feb7e..e8332dbc 100644 --- a/pyrogram/types/messages_and_media/__init__.py +++ b/pyrogram/types/messages_and_media/__init__.py @@ -21,11 +21,14 @@ from .audio import Audio from .contact import Contact from .dice import Dice +from .disallowed_gifts_settings import DisallowedGiftsSettings from .document import Document +from .fact_check import FactCheck from .game import Game from .giveaway import Giveaway from .location import Location from .message import Message +from .message_effect import MessageEffect from .message_entity import MessageEntity from .photo import Photo from .poll import Poll @@ -34,6 +37,7 @@ from .sticker import Sticker from .story import Story from .stripped_thumbnail import StrippedThumbnail +from .suggested_post import SuggestedPost from .thumbnail import Thumbnail from .venue import Venue from .video import Video @@ -43,9 +47,13 @@ from .web_page import WebPage from .message_reactions import MessageReactions from .star_gift import StarGift, StarGiftUnique +from .star_gift_attribute import StarGiftAttribute, StarGiftAttributeRarity +from .paid_media import PaidMedia __all__ = [ - "Animation", "Audio", "Contact", "Document", "Game", "Location", "Message", "MessageEntity", "Photo", "Thumbnail", - "StrippedThumbnail", "Poll", "PollOption", "Sticker", "Venue", "Video", "VideoNote", "Voice", "WebPage", "Dice", - "Reaction", "WebAppData", "MessageReactions", "Story", "Giveaway", "AlternativeVideo", "StarGift", "StarGiftUnique" + "Animation", "Audio", "Contact", "DisallowedGiftsSettings", "Document", "FactCheck", "Game", "Location", "Message", "MessageEffect", + "MessageEntity", "Photo", "Thumbnail", "StrippedThumbnail", "Poll", "PollOption", "Sticker", "SuggestedPost", + "Venue", "Video", "VideoNote", "Voice", "WebPage", "Dice", "Reaction", "WebAppData", "MessageReactions", + "Story", "Giveaway", "AlternativeVideo", "StarGift", "StarGiftUnique", "StarGiftAttribute", "StarGiftAttributeRarity", + "PaidMedia" ] diff --git a/pyrogram/types/messages_and_media/dice.py b/pyrogram/types/messages_and_media/dice.py index 2c683ec8..12a03435 100644 --- a/pyrogram/types/messages_and_media/dice.py +++ b/pyrogram/types/messages_and_media/dice.py @@ -30,18 +30,31 @@ class Dice(Object): value (``int``): Value of the dice, 1-6 for currently supported base emoji. + + game_outcome (``int``, *optional*): + Usually for the casino-style slot machine (🎰) emoji. + The value is the index of the winning combination (0-64). """ - def __init__(self, *, client: "pyrogram.Client" = None, emoji: str, value: int): + def __init__( + self, + *, + client: "pyrogram.Client" = None, + emoji: str, + value: int, + game_outcome: int = None + ): super().__init__(client) self.emoji = emoji self.value = value + self.game_outcome = game_outcome @staticmethod def _parse(client, dice: "raw.types.MessageMediaDice") -> "Dice": return Dice( emoji=dice.emoticon, value=dice.value, + game_outcome=getattr(dice, "game_outcome", None), client=client ) diff --git a/pyrogram/types/messages_and_media/disallowed_gifts_settings.py b/pyrogram/types/messages_and_media/disallowed_gifts_settings.py new file mode 100644 index 00000000..ccf3f112 --- /dev/null +++ b/pyrogram/types/messages_and_media/disallowed_gifts_settings.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 typing import Optional + +import pyrogram +from pyrogram import raw +from ..object import Object + + +class DisallowedGiftsSettings(Object): + """Disallow the reception of specific gift types. + + Parameters: + disallow_unlimited_stargifts (``bool``, *optional*): + Disallow the reception of gifts with an unlimited supply. + + disallow_limited_stargifts (``bool``, *optional*): + Disallow the reception of limited-supply gifts. + + disallow_unique_stargifts (``bool``, *optional*): + Disallow the reception of collectible gifts. + + disallow_premium_gifts (``bool``, *optional*): + Disallow the reception of gifted Telegram Premium subscriptions. + + disallow_stargifts_from_channels (``bool``, *optional*): + Disallow the reception of star gifts from channels. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + disallow_unlimited_stargifts: bool = None, + disallow_limited_stargifts: bool = None, + disallow_unique_stargifts: bool = None, + disallow_premium_gifts: bool = None, + disallow_stargifts_from_channels: bool = None + ): + super().__init__(client) + + self.disallow_unlimited_stargifts = disallow_unlimited_stargifts + self.disallow_limited_stargifts = disallow_limited_stargifts + self.disallow_unique_stargifts = disallow_unique_stargifts + self.disallow_premium_gifts = disallow_premium_gifts + self.disallow_stargifts_from_channels = disallow_stargifts_from_channels + + @staticmethod + def _parse( + client: "pyrogram.Client", + settings: "raw.types.DisallowedGiftsSettings" + ) -> Optional["DisallowedGiftsSettings"]: + if not settings: + return None + + return DisallowedGiftsSettings( + client=client, + disallow_unlimited_stargifts=settings.disallow_unlimited_stargifts, + disallow_limited_stargifts=settings.disallow_limited_stargifts, + disallow_unique_stargifts=settings.disallow_unique_stargifts, + disallow_premium_gifts=settings.disallow_premium_gifts, + disallow_stargifts_from_channels=settings.disallow_stargifts_from_channels + ) diff --git a/pyrogram/types/messages_and_media/fact_check.py b/pyrogram/types/messages_and_media/fact_check.py new file mode 100644 index 00000000..aa242513 --- /dev/null +++ b/pyrogram/types/messages_and_media/fact_check.py @@ -0,0 +1,90 @@ +# 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, List + +import pyrogram +from pyrogram import raw, types +from ..object import Object + + +class FactCheck(Object): + """Represents a fact check attached to a message. + + Parameters: + text (``str``): + The fact check text. + + entities (List of :obj:`~pyrogram.types.MessageEntity`, *optional*): + Special entities in the fact check text (links, formatting, etc.). + + country (``str``, *optional*): + The country code where the fact check applies. + + hash (``int``, *optional*): + Hash for caching. + + need_check (``bool``, *optional*): + Whether the message needs a fact check. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + text: str = None, + entities: List["types.MessageEntity"] = None, + country: str = None, + hash: int = None, + need_check: bool = None + ): + super().__init__(client) + + self.text = text + self.entities = entities + self.country = country + self.hash = hash + self.need_check = need_check + + @staticmethod + def _parse( + client: "pyrogram.Client", + factcheck: "raw.types.FactCheck" + ) -> Optional["FactCheck"]: + if factcheck is None: + return None + + entities = None + if hasattr(factcheck, 'text') and factcheck.text: + text = factcheck.text.text if hasattr(factcheck.text, 'text') else str(factcheck.text) + if hasattr(factcheck.text, 'entities') and factcheck.text.entities: + entities = [ + types.MessageEntity._parse(client, e, {}) + for e in factcheck.text.entities + ] + else: + text = None + + return FactCheck( + client=client, + text=text, + entities=types.List(entities) if entities else None, + country=factcheck.country if hasattr(factcheck, 'country') else None, + hash=factcheck.hash if hasattr(factcheck, 'hash') else None, + need_check=factcheck.need_check if hasattr(factcheck, 'need_check') else None + ) diff --git a/pyrogram/types/messages_and_media/message.py b/pyrogram/types/messages_and_media/message.py index 017c3d35..5c192823 100644 --- a/pyrogram/types/messages_and_media/message.py +++ b/pyrogram/types/messages_and_media/message.py @@ -312,6 +312,27 @@ class Message(Object, Update): link (``str``, *property*): Generate a link to this message, only for groups and channels. + + saved_peer_id (:obj:`~pyrogram.types.Chat`, *optional*): + Saved peer. + + from_boosts_applied (``int``, *optional*): + Number of boosts applied from the sender. + + quick_reply_shortcut_id (``int``, *optional*): + Quick reply shortcut ID. + + effect_id (``int``, *optional*): + Message effect ID. + + fact_check (:obj:`~pyrogram.types.FactCheck`, *optional*): + Fact check information. + + paid_message_stars (:obj:`~pyrogram.types.StarsAmount`, *optional*): + Price of the paid message in stars. + + suggested_post (:obj:`~pyrogram.types.SuggestedPost`, *optional*): + Suggested post information. """ # TODO: Add game missing field. Also invoice, successful_payment, connected_website @@ -357,6 +378,9 @@ def __init__( game: "types.Game" = None, story: "types.Story" = None, giveaway: "types.Giveaway" = None, + giveaway_results: "types.GiveawayResults" = None, + paid_media: "types.PaidMedia" = None, + todo: "types.TodoList" = None, video: "types.Video" = None, voice: "types.Voice" = None, video_note: "types.VideoNote" = None, @@ -398,6 +422,13 @@ def __init__( ] = None, reactions: List["types.Reaction"] = None, alternative_videos: List["types.AlternativeVideo"] = None, + saved_peer_id: "types.Chat" = None, + from_boosts_applied: int = None, + quick_reply_shortcut_id: int = None, + effect_id: int = None, + fact_check: "types.FactCheck" = None, + paid_message_stars: "types.StarsAmount" = None, + suggested_post: "types.SuggestedPost" = None ): super().__init__(client) @@ -441,6 +472,9 @@ def __init__( self.voice = voice self.story = story self.giveaway = giveaway + self.giveaway_results = giveaway_results + self.paid_media = paid_media + self.todo = todo self.video_note = video_note self.caption = caption self.contact = contact @@ -474,6 +508,13 @@ def __init__( self.video_chat_members_invited = video_chat_members_invited self.web_app_data = web_app_data self.reactions = reactions + self.saved_peer_id = saved_peer_id + self.from_boosts_applied = from_boosts_applied + self.quick_reply_shortcut_id = quick_reply_shortcut_id + self.effect_id = effect_id + self.fact_check = fact_check + self.paid_message_stars = paid_message_stars + self.suggested_post = suggested_post @staticmethod async def _parse( @@ -581,6 +622,40 @@ async def _parse( service_type = enums.MessageServiceType.NEW_CREATOR_PENDING elif isinstance(action, raw.types.MessageActionChangeCreator): service_type = enums.MessageServiceType.CHANGE_CREATOR + elif isinstance(action, raw.types.MessageActionGiftCode): + service_type = enums.MessageServiceType.GIFT_CODE + elif isinstance(action, raw.types.MessageActionGiveawayLaunch): + service_type = enums.MessageServiceType.GIVEAWAY_LAUNCH + elif isinstance(action, raw.types.MessageActionGiveawayResults): + service_type = enums.MessageServiceType.GIVEAWAY_RESULTS + elif isinstance(action, raw.types.MessageActionBoostApply): + service_type = enums.MessageServiceType.BOOST_APPLY + elif isinstance(action, raw.types.MessageActionTodoCompletions): + service_type = enums.MessageServiceType.TODO_COMPLETIONS + elif isinstance(action, raw.types.MessageActionTodoAppendTasks): + service_type = enums.MessageServiceType.TODO_APPEND_TASKS + elif isinstance(action, raw.types.MessageActionConferenceCall): + service_type = enums.MessageServiceType.CONFERENCE_CALL + elif isinstance(action, raw.types.MessageActionGiftStars): + service_type = enums.MessageServiceType.GIFT_STARS + elif isinstance(action, raw.types.MessageActionPrizeStars): + service_type = enums.MessageServiceType.PRIZE_STARS + elif isinstance(action, raw.types.MessageActionStarGiftPurchaseOffer): + service_type = enums.MessageServiceType.STAR_GIFT_PURCHASE_OFFER + elif isinstance(action, raw.types.MessageActionStarGiftPurchaseOfferDeclined): + service_type = enums.MessageServiceType.STAR_GIFT_PURCHASE_OFFER_DECLINED + elif isinstance(action, raw.types.MessageActionSuggestedPostApproval): + service_type = enums.MessageServiceType.SUGGESTED_POST_APPROVAL + elif isinstance(action, raw.types.MessageActionSuggestedPostSuccess): + service_type = enums.MessageServiceType.SUGGESTED_POST_SUCCESS + elif isinstance(action, raw.types.MessageActionSuggestedPostRefund): + service_type = enums.MessageServiceType.SUGGESTED_POST_REFUND + elif isinstance(action, raw.types.MessageActionPaidMessagesRefunded): + service_type = enums.MessageServiceType.PAID_MESSAGES_REFUNDED + elif isinstance(action, raw.types.MessageActionPaidMessagesPrice): + service_type = enums.MessageServiceType.PAID_MESSAGES_PRICE + elif isinstance(action, raw.types.MessageActionSuggestBirthday): + service_type = enums.MessageServiceType.SUGGEST_BIRTHDAY from_user = types.User._parse(client, users.get(user_id, None)) sender_chat = types.Chat._parse(client, message, users, chats, is_chat=False) if not from_user else None @@ -677,6 +752,9 @@ async def _parse( game = None story = None giveaway = None + giveaway_results = None + paid_media = None + todo = None audio = None voice = None animation = None @@ -716,6 +794,15 @@ async def _parse( elif isinstance(media, raw.types.MessageMediaGiveaway): giveaway = types.Giveaway._parse(client, media, chats) media_type = enums.MessageMediaType.GIVEAWAY + elif isinstance(media, raw.types.MessageMediaGiveawayResults): + giveaway_results = types.GiveawayResults._parse(client, media, users, chats) + media_type = enums.MessageMediaType.GIVEAWAY_RESULTS + elif isinstance(media, raw.types.MessageMediaPaidMedia): + paid_media = types.PaidMedia._parse(client, media, users, chats) + media_type = enums.MessageMediaType.PAID_MEDIA + elif isinstance(media, raw.types.MessageMediaToDo): + todo = types.TodoList._parse_media(client, media, users) + media_type = enums.MessageMediaType.TODO elif isinstance(media, raw.types.MessageMediaDocument): doc = media.document @@ -808,6 +895,21 @@ async def _parse( reactions = types.MessageReactions._parse(client, message.reactions) + saved_peer_id = None + + if message.saved_peer_id: + saved_peer_raw = None + + if isinstance(message.saved_peer_id, raw.types.PeerUser): + saved_peer_raw = users.get(message.saved_peer_id.user_id) + elif isinstance(message.saved_peer_id, raw.types.PeerChat): + saved_peer_raw = chats.get(message.saved_peer_id.chat_id) + elif isinstance(message.saved_peer_id, raw.types.PeerChannel): + saved_peer_raw = chats.get(message.saved_peer_id.channel_id) + + if saved_peer_raw: + saved_peer_id = types.Chat._parse_chat(client, saved_peer_raw) + parsed_message = Message( id=message.id, date=utils.timestamp_to_datetime(message.date), @@ -860,6 +962,9 @@ async def _parse( game=game, story=story, giveaway=giveaway, + giveaway_results=giveaway_results, + paid_media=paid_media, + todo=todo, video=video, alternative_videos=types.List(alternative_videos) if alternative_videos else None, video_note=video_note, @@ -874,6 +979,13 @@ async def _parse( outgoing=message.out, reply_markup=reply_markup, reactions=reactions, + saved_peer_id=saved_peer_id, + from_boosts_applied=message.from_boosts_applied, + quick_reply_shortcut_id=message.quick_reply_shortcut_id, + effect_id=message.effect, + fact_check=types.FactCheck._parse(client, message.factcheck), + paid_message_stars=types.StarsAmount._parse(client, message.paid_message_stars), + suggested_post=types.SuggestedPost._parse(client, message.suggested_post), client=client ) diff --git a/pyrogram/types/messages_and_media/message_effect.py b/pyrogram/types/messages_and_media/message_effect.py new file mode 100644 index 00000000..a04f0bfa --- /dev/null +++ b/pyrogram/types/messages_and_media/message_effect.py @@ -0,0 +1,98 @@ +# 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 + +import pyrogram +from pyrogram import raw, types +from ..object import Object + + +class MessageEffect(Object): + """Represents a message effect. + + Parameters: + id (``int``): + Unique identifier of the effect. + + emoticon (``str``, *optional*): + Emoticon associated with the effect. + + static_icon (:obj:`~pyrogram.types.Document`, *optional*): + Static icon for the effect. + + effect_sticker (:obj:`~pyrogram.types.Sticker`, *optional*): + Animated sticker for the effect. + + effect_animation (:obj:`~pyrogram.types.Document`, *optional*): + Animation for the effect. + + is_premium_required (``bool``, *optional*): + Whether this effect requires Premium. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + id: int = None, + emoticon: str = None, + static_icon: "types.Document" = None, + effect_sticker: "types.Sticker" = None, + effect_animation: "types.Document" = None, + is_premium_required: bool = None + ): + super().__init__(client) + + self.id = id + self.emoticon = emoticon + self.static_icon = static_icon + self.effect_sticker = effect_sticker + self.effect_animation = effect_animation + self.is_premium_required = is_premium_required + + @staticmethod + async def _parse( + client: "pyrogram.Client", + effect: "raw.types.AvailableEffect" + ) -> "MessageEffect": + static_icon = None + effect_sticker = None + effect_animation = None + + if hasattr(effect, 'static_icon_id') and effect.static_icon_id: + # Would need to fetch the document + pass + + if hasattr(effect, 'effect_sticker_id') and effect.effect_sticker_id: + # Would need to fetch the sticker + pass + + if hasattr(effect, 'effect_animation_id') and effect.effect_animation_id: + # Would need to fetch the animation + pass + + return MessageEffect( + client=client, + id=effect.id, + emoticon=effect.emoticon if hasattr(effect, 'emoticon') else None, + static_icon=static_icon, + effect_sticker=effect_sticker, + effect_animation=effect_animation, + is_premium_required=effect.premium_required if hasattr(effect, 'premium_required') else None + ) diff --git a/pyrogram/types/messages_and_media/paid_media.py b/pyrogram/types/messages_and_media/paid_media.py new file mode 100644 index 00000000..242e5569 --- /dev/null +++ b/pyrogram/types/messages_and_media/paid_media.py @@ -0,0 +1,101 @@ +# 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, Any + +import pyrogram +from pyrogram import raw, types +from ..object import Object + + +class PaidMedia(Object): + """Represents a paid media. + + Parameters: + stars_amount (``int``): + The amount of stars required to buy the media. + + extended_media (List of ``Any``, *optional*): + List of extended media (preview or actual media). + Can contain Photo, Video, or preview information. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + stars_amount: int, + extended_media: List[Any] = None + ): + super().__init__(client) + + self.stars_amount = stars_amount + self.extended_media = extended_media + + @staticmethod + def _parse( + client: "pyrogram.Client", + paid_media: "raw.types.MessageMediaPaidMedia", + users: dict = None, + chats: dict = None + ) -> Optional["PaidMedia"]: + if not paid_media: + return None + + users = users or {} + chats = chats or {} + + extended_media = [] + if paid_media.extended_media: + for ext_media in paid_media.extended_media: + if isinstance(ext_media, raw.types.MessageExtendedMedia): + media = ext_media.media + + if isinstance(media, raw.types.MessageMediaPhoto): + parsed = types.Photo._parse(client, media.photo, media.ttl_seconds) + extended_media.append(parsed) + elif isinstance(media, raw.types.MessageMediaDocument): + doc = media.document + if isinstance(doc, raw.types.Document): + attributes = {type(i): i for i in doc.attributes} + file_name = getattr( + attributes.get(raw.types.DocumentAttributeFilename, None), + "file_name", + None + ) + if raw.types.DocumentAttributeVideo in attributes: + video_attributes = attributes[raw.types.DocumentAttributeVideo] + parsed = types.Video._parse(client, doc, video_attributes, file_name, media.ttl_seconds) + extended_media.append(parsed) + else: + parsed = types.Document._parse(client, doc, file_name) + extended_media.append(parsed) + elif isinstance(ext_media, raw.types.MessageExtendedMediaPreview): + preview_info = { + "type": "preview", + "w": ext_media.w if hasattr(ext_media, 'w') else None, + "h": ext_media.h if hasattr(ext_media, 'h') else None, + "video_duration": ext_media.video_duration if hasattr(ext_media, 'video_duration') else None + } + extended_media.append(preview_info) + + return PaidMedia( + client=client, + stars_amount=paid_media.stars_amount, + extended_media=extended_media if extended_media else None + ) diff --git a/pyrogram/types/messages_and_media/star_gift_attribute.py b/pyrogram/types/messages_and_media/star_gift_attribute.py new file mode 100644 index 00000000..666b9668 --- /dev/null +++ b/pyrogram/types/messages_and_media/star_gift_attribute.py @@ -0,0 +1,251 @@ +# 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 + +import pyrogram +from pyrogram import raw, types +from ..object import Object + + +class StarGiftAttributeRarity(Object): + """Rarity information for a star gift attribute. + + Parameters: + type (``str``): + Rarity type. One of: "uncommon", "rare", "epic", "legendary". + + permille (``int``): + Rarity permille (0-1000, where 1000 = 100%). + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + type: str, + permille: int + ): + super().__init__(client) + + self.type = type + self.permille = permille + + @staticmethod + def _parse( + client: "pyrogram.Client", + rarity: "raw.base.StarGiftAttributeRarity" + ) -> Optional["StarGiftAttributeRarity"]: + if rarity is None: + return None + + rarity_type = "unknown" + if isinstance(rarity, raw.types.StarGiftAttributeRarityUncommon): + rarity_type = "uncommon" + elif isinstance(rarity, raw.types.StarGiftAttributeRarityRare): + rarity_type = "rare" + elif isinstance(rarity, raw.types.StarGiftAttributeRarityEpic): + rarity_type = "epic" + elif isinstance(rarity, raw.types.StarGiftAttributeRarityLegendary): + rarity_type = "legendary" + + return StarGiftAttributeRarity( + client=client, + type=rarity_type, + permille=rarity.permille + ) + + +class StarGiftAttribute(Object): + """Represents an attribute of a collectible star gift. + + Parameters: + type (``str``): + Type of the attribute. One of: + - "backdrop": The backdrop/background of the gift + - "model": The 3D model of the gift + - "pattern": The pattern applied to the gift + - "original_details": Original sender/receiver details + + name (``str``, *optional*): + Name of the attribute (for backdrop, model, pattern). + + rarity (:obj:`~pyrogram.types.StarGiftAttributeRarity`, *optional*): + Rarity information for this attribute. + + backdrop_id (``int``, *optional*): + For "backdrop" type: unique ID of the backdrop. + + center_color (``int``, *optional*): + For "backdrop" type: center color in RGB24. + + edge_color (``int``, *optional*): + For "backdrop" type: edge color in RGB24. + + pattern_color (``int``, *optional*): + For "backdrop" type: pattern color in RGB24. + + text_color (``int``, *optional*): + For "backdrop" type: text color in RGB24. + + sticker (:obj:`~pyrogram.types.Sticker`, *optional*): + For "model" type: the sticker representing the model. + + model_id (``int``, *optional*): + For "model" type: unique ID of the model. + + pattern_id (``int``, *optional*): + For "pattern" type: unique ID of the pattern. + + document (:obj:`~pyrogram.types.Document`, *optional*): + For "pattern" type: the pattern document. + + sender_id (``int``, *optional*): + For "original_details" type: original sender user ID. + + recipient_id (``int``, *optional*): + For "original_details" type: original recipient user ID. + + date (:py:obj:`~datetime.datetime`, *optional*): + For "original_details" type: when the gift was sent. + + message (``str``, *optional*): + For "original_details" type: original gift message. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + type: str, + name: str = None, + rarity: "StarGiftAttributeRarity" = None, + backdrop_id: int = None, + center_color: int = None, + edge_color: int = None, + pattern_color: int = None, + text_color: int = None, + sticker: "types.Sticker" = None, + model_id: int = None, + pattern_id: int = None, + document: "types.Document" = None, + sender_id: int = None, + recipient_id: int = None, + date: "datetime" = None, + message: str = None + ): + super().__init__(client) + + self.type = type + self.name = name + self.rarity = rarity + self.backdrop_id = backdrop_id + self.center_color = center_color + self.edge_color = edge_color + self.pattern_color = pattern_color + self.text_color = text_color + self.sticker = sticker + self.model_id = model_id + self.pattern_id = pattern_id + self.document = document + self.sender_id = sender_id + self.recipient_id = recipient_id + self.date = date + self.message = message + + @staticmethod + async def _parse( + client: "pyrogram.Client", + attribute: "raw.base.StarGiftAttribute" + ) -> Optional["StarGiftAttribute"]: + from datetime import datetime + from pyrogram import utils + + if isinstance(attribute, raw.types.StarGiftAttributeBackdrop): + return StarGiftAttribute( + client=client, + type="backdrop", + name=attribute.name, + rarity=StarGiftAttributeRarity._parse(client, attribute.rarity), + backdrop_id=attribute.backdrop_id, + center_color=attribute.center_color, + edge_color=attribute.edge_color, + pattern_color=attribute.pattern_color, + text_color=attribute.text_color + ) + elif isinstance(attribute, raw.types.StarGiftAttributeModel): + sticker = None + if attribute.document: + sticker = await types.Sticker._parse(client, attribute.document, {}) + + return StarGiftAttribute( + client=client, + type="model", + name=attribute.name, + rarity=StarGiftAttributeRarity._parse(client, attribute.rarity), + sticker=sticker, + model_id=attribute.model_id + ) + elif isinstance(attribute, raw.types.StarGiftAttributePattern): + document = None + if attribute.document: + document = types.Document._parse(client, attribute.document, None) + + return StarGiftAttribute( + client=client, + type="pattern", + name=attribute.name, + rarity=StarGiftAttributeRarity._parse(client, attribute.rarity), + pattern_id=attribute.pattern_id, + document=document + ) + elif isinstance(attribute, raw.types.StarGiftAttributeOriginalDetails): + sender_id = None + recipient_id = None + + if attribute.sender_id: + sender_id = utils.get_raw_peer_id(attribute.sender_id) + if attribute.recipient_id: + recipient_id = utils.get_raw_peer_id(attribute.recipient_id) + + return StarGiftAttribute( + client=client, + type="original_details", + sender_id=sender_id, + recipient_id=recipient_id, + date=utils.timestamp_to_datetime(attribute.date), + message=attribute.message + ) + + return None + + @staticmethod + async def _parse_list( + client: "pyrogram.Client", + attributes: List["raw.base.StarGiftAttribute"] + ) -> Optional[List["StarGiftAttribute"]]: + if not attributes: + return None + + result = [] + for attr in attributes: + parsed = await StarGiftAttribute._parse(client, attr) + if parsed: + result.append(parsed) + + return types.List(result) if result else None diff --git a/pyrogram/types/messages_and_media/suggested_post.py b/pyrogram/types/messages_and_media/suggested_post.py new file mode 100644 index 00000000..91d59ef9 --- /dev/null +++ b/pyrogram/types/messages_and_media/suggested_post.py @@ -0,0 +1,86 @@ +# 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 Optional + +import pyrogram +from pyrogram import raw, types, utils +from ..object import Object + + +class SuggestedPost(Object): + """Represents a suggested post for a channel. + + Parameters: + schedule_date (:py:obj:`~datetime.datetime`, *optional*): + Scheduled date for the post. + + price (``int``, *optional*): + Price in Telegram Stars for the suggested post. + + is_pending (``bool``, *optional*): + Whether the post is pending approval. + + is_rejected (``bool``, *optional*): + Whether the post was rejected. + + is_accepted (``bool``, *optional*): + Whether the post was accepted. + + rejection_reason (``str``, *optional*): + Reason for rejection if rejected. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + schedule_date: datetime = None, + price: int = None, + is_pending: bool = None, + is_rejected: bool = None, + is_accepted: bool = None, + rejection_reason: str = None + ): + super().__init__(client) + + self.schedule_date = schedule_date + self.price = price + self.is_pending = is_pending + self.is_rejected = is_rejected + self.is_accepted = is_accepted + self.rejection_reason = rejection_reason + + @staticmethod + def _parse( + client: "pyrogram.Client", + suggested_post: "raw.types.SuggestedPost" + ) -> Optional["SuggestedPost"]: + if suggested_post is None: + return None + + return SuggestedPost( + client=client, + schedule_date=utils.timestamp_to_datetime(suggested_post.schedule_date) if hasattr(suggested_post, 'schedule_date') and suggested_post.schedule_date else None, + price=suggested_post.price if hasattr(suggested_post, 'price') else None, + is_pending=suggested_post.pending if hasattr(suggested_post, 'pending') else None, + is_rejected=suggested_post.rejected if hasattr(suggested_post, 'rejected') else None, + is_accepted=suggested_post.accepted if hasattr(suggested_post, 'accepted') else None, + rejection_reason=suggested_post.rejection_reason if hasattr(suggested_post, 'rejection_reason') else None + ) diff --git a/pyrogram/types/messages_and_media/video.py b/pyrogram/types/messages_and_media/video.py index b50f9fca..b45f059f 100644 --- a/pyrogram/types/messages_and_media/video.py +++ b/pyrogram/types/messages_and_media/video.py @@ -66,6 +66,18 @@ class Video(Object): thumbs (List of :obj:`~pyrogram.types.Thumbnail`, *optional*): Video thumbnails. + + nosound (``bool``, *optional*): + True, if the video has no sound. + + video_start_ts (``float``, *optional*): + The timestamp in seconds of the video frame to use as photo profile preview. + + video_codec (``str``, *optional*): + Video codec. + + preload_prefix_size (``int``, *optional*): + The size of the video preload prefix. """ def __init__( @@ -83,7 +95,11 @@ def __init__( supports_streaming: bool = None, ttl_seconds: int = None, date: datetime = None, - thumbs: List["types.Thumbnail"] = None + thumbs: List["types.Thumbnail"] = None, + nosound: bool = None, + video_start_ts: float = None, + video_codec: str = None, + preload_prefix_size: int = None ): super().__init__(client) @@ -99,6 +115,10 @@ def __init__( self.ttl_seconds = ttl_seconds self.date = date self.thumbs = thumbs + self.nosound = nosound + self.video_start_ts = video_start_ts + self.video_codec = video_codec + self.preload_prefix_size = preload_prefix_size @staticmethod def _parse( @@ -130,5 +150,9 @@ def _parse( date=utils.timestamp_to_datetime(video.date), ttl_seconds=ttl_seconds, thumbs=types.Thumbnail._parse(client, video), + nosound=getattr(video_attributes, "nosound", None), + video_start_ts=getattr(video_attributes, "video_start_ts", None), + video_codec=getattr(video_attributes, "video_codec", None), + preload_prefix_size=getattr(video_attributes, "preload_prefix_size", None), client=client ) diff --git a/pyrogram/types/messages_and_media/web_page.py b/pyrogram/types/messages_and_media/web_page.py index 34e51d88..b77e9228 100644 --- a/pyrogram/types/messages_and_media/web_page.py +++ b/pyrogram/types/messages_and_media/web_page.py @@ -82,6 +82,12 @@ class WebPage(Object): author (``str``, *optional*): Author of the webpage, eg the Twitter user for a tweet, or the author in an article. + + has_large_media (``bool``, *optional*): + True, if the webpage has a large media. + + video_cover_photo (:obj:`~pyrogram.types.Photo`, *optional*): + Webpage preview video cover photo. """ def __init__( @@ -105,7 +111,9 @@ def __init__( embed_width: int = None, embed_height: int = None, duration: int = None, - author: str = None + author: str = None, + has_large_media: bool = None, + video_cover_photo: "types.Photo" = None ): super().__init__(client) @@ -127,9 +135,14 @@ def __init__( self.embed_height = embed_height self.duration = duration self.author = author + self.has_large_media = has_large_media + self.video_cover_photo = video_cover_photo @staticmethod def _parse(client, webpage: "raw.types.WebPage") -> "WebPage": + if isinstance(webpage, raw.types.WebPageUrlPending): + return None + audio = None document = None photo = None @@ -183,5 +196,7 @@ def _parse(client, webpage: "raw.types.WebPage") -> "WebPage": embed_width=webpage.embed_width, embed_height=webpage.embed_height, duration=webpage.duration, - author=webpage.author + author=webpage.author, + has_large_media=getattr(webpage, "has_large_media", None), + video_cover_photo=types.Photo._parse(client, getattr(webpage, "video_cover_photo", None)) ) diff --git a/pyrogram/types/payments/__init__.py b/pyrogram/types/payments/__init__.py new file mode 100644 index 00000000..cd3e5afd --- /dev/null +++ b/pyrogram/types/payments/__init__.py @@ -0,0 +1,47 @@ +# 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 .stars_amount import StarsAmount +from .stars_transaction import StarsTransaction, StarsTransactionPeer +from .stars_subscription import StarsSubscription, StarsSubscriptionPricing +from .stars_status import StarsStatus +from .stars_revenue_status import StarsRevenueStatus +from .stars_topup_option import StarsTopupOption +from .stars_gift_option import StarsGiftOption +from .paid_reaction_privacy import PaidReactionPrivacy +from .star_gift_auction_round import StarGiftAuctionRound +from .star_gift_auction_user_state import StarGiftAuctionUserState +from .star_gift_auction_state import StarGiftAuctionState +from .auction_bid_level import AuctionBidLevel + +__all__ = [ + "StarsAmount", + "StarsTransaction", + "StarsTransactionPeer", + "StarsSubscription", + "StarsSubscriptionPricing", + "StarsStatus", + "StarsRevenueStatus", + "StarsTopupOption", + "StarsGiftOption", + "PaidReactionPrivacy", + "StarGiftAuctionRound", + "StarGiftAuctionUserState", + "StarGiftAuctionState", + "AuctionBidLevel" +] diff --git a/pyrogram/types/payments/auction_bid_level.py b/pyrogram/types/payments/auction_bid_level.py new file mode 100644 index 00000000..18ecd3e4 --- /dev/null +++ b/pyrogram/types/payments/auction_bid_level.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 datetime import datetime +from typing import Optional + +import pyrogram +from pyrogram import raw, utils +from ..object import Object + + +class AuctionBidLevel(Object): + """Represents an auction bid level. + + Parameters: + position (``int``): + Bid position. + + amount (``int``): + Bid amount. + + date (:py:obj:`~datetime.datetime`, *optional*): + Date of the bid. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + position: int, + amount: int, + date: datetime = None + ): + super().__init__(client) + + self.position = position + self.amount = amount + self.date = date + + @staticmethod + def _parse( + client: "pyrogram.Client", + level: "raw.types.AuctionBidLevel" + ) -> "AuctionBidLevel": + return AuctionBidLevel( + client=client, + position=level.pos, + amount=level.amount, + date=utils.timestamp_to_datetime(level.date) + ) diff --git a/pyrogram/types/payments/paid_reaction_privacy.py b/pyrogram/types/payments/paid_reaction_privacy.py new file mode 100644 index 00000000..37f71328 --- /dev/null +++ b/pyrogram/types/payments/paid_reaction_privacy.py @@ -0,0 +1,71 @@ +# 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 + +import pyrogram +from pyrogram import raw, types +from ..object import Object + + +class PaidReactionPrivacy(Object): + """Paid reaction privacy settings. + + Parameters: + type (``str``): + The type of privacy setting. Can be "default", "anonymous" or "peer". + + peer (:obj:`~pyrogram.types.Chat`, *optional*): + The peer to send reactions as. Only available if type is "peer". + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + type: str, + peer: "types.Chat" = None + ): + super().__init__(client) + + self.type = type + self.peer = peer + + @staticmethod + def _parse( + client: "pyrogram.Client", + privacy: "raw.base.PaidReactionPrivacy", + chats: dict = None + ) -> "PaidReactionPrivacy": + chats = chats or {} + + if isinstance(privacy, raw.types.PaidReactionPrivacyDefault): + return PaidReactionPrivacy(client=client, type="default") + + if isinstance(privacy, raw.types.PaidReactionPrivacyAnonymous): + return PaidReactionPrivacy(client=client, type="anonymous") + + if isinstance(privacy, raw.types.PaidReactionPrivacyPeer): + # We can't fully parse the peer here without resolving input peer + # but we can try our best or just indicate it's a peer type + return PaidReactionPrivacy( + client=client, + type="peer" + ) + + return None diff --git a/pyrogram/types/payments/star_gift_auction_round.py b/pyrogram/types/payments/star_gift_auction_round.py new file mode 100644 index 00000000..de8a4e81 --- /dev/null +++ b/pyrogram/types/payments/star_gift_auction_round.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 Optional + +import pyrogram +from pyrogram import raw +from ..object import Object + + +class StarGiftAuctionRound(Object): + """Represents a star gift auction round. + + Parameters: + num (``int``): + Round number. + + duration (``int``): + Round duration in seconds. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + num: int, + duration: int + ): + super().__init__(client) + + self.num = num + self.duration = duration + + @staticmethod + def _parse( + client: "pyrogram.Client", + round: "raw.types.StarGiftAuctionRound" + ) -> "StarGiftAuctionRound": + return StarGiftAuctionRound( + client=client, + num=round.num, + duration=round.duration + ) diff --git a/pyrogram/types/payments/star_gift_auction_state.py b/pyrogram/types/payments/star_gift_auction_state.py new file mode 100644 index 00000000..8cec2be0 --- /dev/null +++ b/pyrogram/types/payments/star_gift_auction_state.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 typing import List, Optional, Union + +import pyrogram +from pyrogram import raw, types, utils +from ..object import Object + + +class StarGiftAuctionState(Object): + """Represents the state of a star gift auction. + + Parameters: + type (``str``): + State type: "active", "finished", or "not_modified". + + version (``int``, *optional*): + State version number. + + start_date (:py:obj:`~datetime.datetime`, *optional*): + Auction start date. + + end_date (:py:obj:`~datetime.datetime`, *optional*): + Auction end date. + + min_bid_amount (``int``, *optional*): + Minimum bid amount required. + + bid_levels (List of :obj:`~pyrogram.types.AuctionBidLevel`, *optional*): + Current bid levels in the auction. + + top_bidders (List of ``int``, *optional*): + User IDs of top bidders. + + next_round_at (:py:obj:`~datetime.datetime`, *optional*): + Date of the next round. + + last_gift_num (``int``, *optional*): + Number of the last gift. + + gifts_left (``int``, *optional*): + Number of gifts remaining. + + current_round (``int``, *optional*): + Current round number. + + total_rounds (``int``, *optional*): + Total number of rounds. + + rounds (List of :obj:`~pyrogram.types.StarGiftAuctionRound`, *optional*): + Information about auction rounds. + + average_price (``int``, *optional*): + Average price (for finished auctions). + + listed_count (``int``, *optional*): + Number of gifts listed (for finished auctions). + + fragment_listed_count (``int``, *optional*): + Number of gifts listed on Fragment (for finished auctions). + + fragment_listed_url (``str``, *optional*): + URL for Fragment listing (for finished auctions). + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + type: str, + version: int = None, + start_date: datetime = None, + end_date: datetime = None, + min_bid_amount: int = None, + bid_levels: List["types.AuctionBidLevel"] = None, + top_bidders: List[int] = None, + next_round_at: datetime = None, + last_gift_num: int = None, + gifts_left: int = None, + current_round: int = None, + total_rounds: int = None, + rounds: List["types.StarGiftAuctionRound"] = None, + average_price: int = None, + listed_count: int = None, + fragment_listed_count: int = None, + fragment_listed_url: str = None + ): + super().__init__(client) + + self.type = type + self.version = version + self.start_date = start_date + self.end_date = end_date + self.min_bid_amount = min_bid_amount + self.bid_levels = bid_levels + self.top_bidders = top_bidders + self.next_round_at = next_round_at + self.last_gift_num = last_gift_num + self.gifts_left = gifts_left + self.current_round = current_round + self.total_rounds = total_rounds + self.rounds = rounds + self.average_price = average_price + self.listed_count = listed_count + self.fragment_listed_count = fragment_listed_count + self.fragment_listed_url = fragment_listed_url + + @staticmethod + def _parse( + client: "pyrogram.Client", + state: Union["raw.types.StarGiftAuctionState", "raw.types.StarGiftAuctionStateFinished", "raw.types.StarGiftAuctionStateNotModified"] + ) -> Optional["StarGiftAuctionState"]: + if not state: + return None + + if isinstance(state, raw.types.StarGiftAuctionStateNotModified): + return StarGiftAuctionState( + client=client, + type="not_modified" + ) + + elif isinstance(state, raw.types.StarGiftAuctionStateFinished): + return StarGiftAuctionState( + client=client, + type="finished", + start_date=utils.timestamp_to_datetime(state.start_date), + end_date=utils.timestamp_to_datetime(state.end_date), + average_price=state.average_price, + listed_count=state.listed_count, + fragment_listed_count=state.fragment_listed_count, + fragment_listed_url=state.fragment_listed_url + ) + + elif isinstance(state, raw.types.StarGiftAuctionState): + return StarGiftAuctionState( + client=client, + type="active", + version=state.version, + start_date=utils.timestamp_to_datetime(state.start_date), + end_date=utils.timestamp_to_datetime(state.end_date), + min_bid_amount=state.min_bid_amount, + bid_levels=types.List([ + types.AuctionBidLevel._parse(client, level) + for level in state.bid_levels + ]) if state.bid_levels else None, + top_bidders=state.top_bidders, + next_round_at=utils.timestamp_to_datetime(state.next_round_at), + last_gift_num=state.last_gift_num, + gifts_left=state.gifts_left, + current_round=state.current_round, + total_rounds=state.total_rounds, + rounds=types.List([ + types.StarGiftAuctionRound._parse(client, round) + for round in state.rounds + ]) if state.rounds else None + ) + + return None diff --git a/pyrogram/types/payments/star_gift_auction_user_state.py b/pyrogram/types/payments/star_gift_auction_user_state.py new file mode 100644 index 00000000..3ab2312d --- /dev/null +++ b/pyrogram/types/payments/star_gift_auction_user_state.py @@ -0,0 +1,110 @@ +# 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 Optional + +import pyrogram +from pyrogram import raw, types, utils +from ..object import Object + + +class StarGiftAuctionUserState(Object): + """Represents a user's state in a star gift auction. + + Parameters: + acquired_count (``int``): + Number of gifts acquired. + + returned (``bool``, *optional*): + Whether the bid was returned. + + bid_amount (``int``, *optional*): + Current bid amount. + + bid_date (:py:obj:`~datetime.datetime`, *optional*): + Date of the bid. + + min_bid_amount (``int``, *optional*): + Minimum bid amount required. + + bid_peer (:obj:`~pyrogram.types.Chat`, *optional*): + Peer who placed the bid. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + acquired_count: int, + returned: bool = None, + bid_amount: int = None, + bid_date: datetime = None, + min_bid_amount: int = None, + bid_peer: "types.Chat" = None + ): + super().__init__(client) + + self.acquired_count = acquired_count + self.returned = returned + self.bid_amount = bid_amount + self.bid_date = bid_date + self.min_bid_amount = min_bid_amount + self.bid_peer = bid_peer + + @staticmethod + def _parse( + client: "pyrogram.Client", + state: "raw.types.StarGiftAuctionUserState", + chats: dict = None + ) -> Optional["StarGiftAuctionUserState"]: + if not state: + return None + + chats = chats or {} + bid_peer = None + if state.bid_peer: + # This requires resolving the peer. + # state.bid_peer is a Peer (PeerUser, PeerChat, PeerChannel). + # We need the chats/users dicts to resolve it fully to a Chat object. + # For now we will try to resolve from available dicts or leave it as None if not found + # Wait, strict parsing would require them. + # Let's use utils.get_peer_id and try to fetch from chats/users if available? + # For parsing simple objects we usually assume passed dicts have the info. + + # If state.bid_peer is available, we try to parse it. + # We need a generic peer parser that takes a Peer and dicts. + # types.Chat._parse_chat(client, peer) ? No, that takes User/Chat/Channel. + # types.Chat._parse_dialog? + # Let's do manual look up. + peer_id = utils.get_peer_id(state.bid_peer) + if peer_id in chats: + bid_peer = types.Chat._parse_channel_chat(client, chats[peer_id]) # Usually channels/chats + # If it's a user? + # "users" dict is not passed here? I should add it to signature if needed. + pass + + return StarGiftAuctionUserState( + client=client, + acquired_count=state.acquired_count, + returned=state.returned, + bid_amount=state.bid_amount, + bid_date=utils.timestamp_to_datetime(state.bid_date), + min_bid_amount=state.min_bid_amount, + bid_peer=bid_peer # Placeholder, requires proper peer resolution logic updates if critical + ) diff --git a/pyrogram/types/payments/stars_amount.py b/pyrogram/types/payments/stars_amount.py new file mode 100644 index 00000000..70964691 --- /dev/null +++ b/pyrogram/types/payments/stars_amount.py @@ -0,0 +1,90 @@ +# 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 ..object import Object + + +class StarsAmount(Object): + """Represents an amount of Telegram Stars with nano precision. + + Parameters: + amount (``int``): + The integer amount of Telegram Stars. + + nanos (``int``): + The decimal amount of Telegram Stars, expressed as nanostars. + 1 nanostar = 1/1,000,000,000 (one billionth) of a Star. + Range: -999999999 to 999999999. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + amount: int, + nanos: int = 0 + ): + super().__init__(client) + + self.amount = amount + self.nanos = nanos + + @property + def total(self) -> float: + """Get the total amount as a float including nanostars.""" + return self.amount + (self.nanos / 1_000_000_000) + + @staticmethod + def _parse( + client: "pyrogram.Client", + stars_amount: "raw.types.StarsAmount" + ) -> "StarsAmount": + if stars_amount is None: + return None + + return StarsAmount( + client=client, + amount=stars_amount.amount, + nanos=stars_amount.nanos + ) + + @staticmethod + def _parse_int( + client: "pyrogram.Client", + amount: int + ) -> "StarsAmount": + """Parse a simple integer amount to StarsAmount.""" + return StarsAmount( + client=client, + amount=amount, + nanos=0 + ) + + def __repr__(self) -> str: + if self.nanos: + return f"StarsAmount(amount={self.amount}, nanos={self.nanos})" + return f"StarsAmount(amount={self.amount})" + + def __str__(self) -> str: + if self.nanos: + return f"{self.total:.9f} Stars" + return f"{self.amount} Stars" diff --git a/pyrogram/types/payments/stars_gift_option.py b/pyrogram/types/payments/stars_gift_option.py new file mode 100644 index 00000000..3f3254e0 --- /dev/null +++ b/pyrogram/types/payments/stars_gift_option.py @@ -0,0 +1,76 @@ +# 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 + +import pyrogram +from pyrogram import raw +from ..object import Object + + +class StarsGiftOption(Object): + """Telegram Stars gift option. + + Parameters: + stars (``int``): + Amount of Telegram stars. + + currency (``str``): + Three-letter ISO 4217 currency code. + + amount (``int``): + Price of the product in the smallest units of the currency (integer, not float/double). + + extended (``bool``, *optional*): + If set, the option must only be shown in the full list of topup options. + + store_product (``str``, *optional*): + Identifier of the store product associated with the option, official apps only. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + stars: int, + currency: str, + amount: int, + extended: bool = None, + store_product: str = None + ): + super().__init__(client) + + self.stars = stars + self.currency = currency + self.amount = amount + self.extended = extended + self.store_product = store_product + + @staticmethod + def _parse( + client: "pyrogram.Client", + option: "raw.types.StarsGiftOption" + ) -> "StarsGiftOption": + return StarsGiftOption( + client=client, + stars=option.stars, + currency=option.currency, + amount=option.amount, + extended=option.extended, + store_product=option.store_product + ) diff --git a/pyrogram/types/payments/stars_revenue_status.py b/pyrogram/types/payments/stars_revenue_status.py new file mode 100644 index 00000000..1c7adcd4 --- /dev/null +++ b/pyrogram/types/payments/stars_revenue_status.py @@ -0,0 +1,77 @@ +# 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 Optional + +import pyrogram +from pyrogram import raw, types, utils +from ..object import Object + + +class StarsRevenueStatus(Object): + """Describes Telegram Star revenue balances. + + Parameters: + current_balance (:obj:`~pyrogram.types.StarsAmount`): + Amount of not-yet-withdrawn Telegram Stars. + + available_balance (:obj:`~pyrogram.types.StarsAmount`): + Amount of withdrawable Telegram Stars. + + overall_revenue (:obj:`~pyrogram.types.StarsAmount`): + Total amount of earned Telegram Stars. + + withdrawal_enabled (``bool``): + If set, the user may withdraw up to available_balance stars. + + next_withdrawal_date (:py:obj:`~datetime.datetime`, *optional*): + Date when withdrawal will be available to the user. If not set, withdrawal can be started now. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + current_balance: "types.StarsAmount", + available_balance: "types.StarsAmount", + overall_revenue: "types.StarsAmount", + withdrawal_enabled: bool, + next_withdrawal_date: datetime = None + ): + super().__init__(client) + + self.current_balance = current_balance + self.available_balance = available_balance + self.overall_revenue = overall_revenue + self.withdrawal_enabled = withdrawal_enabled + self.next_withdrawal_date = next_withdrawal_date + + @staticmethod + def _parse( + client: "pyrogram.Client", + status: "raw.types.StarsRevenueStatus" + ) -> "StarsRevenueStatus": + return StarsRevenueStatus( + client=client, + current_balance=types.StarsAmount._parse(client, status.current_balance), + available_balance=types.StarsAmount._parse(client, status.available_balance), + overall_revenue=types.StarsAmount._parse(client, status.overall_revenue), + withdrawal_enabled=status.withdrawal_enabled, + next_withdrawal_date=utils.timestamp_to_datetime(status.next_withdrawal_at) + ) diff --git a/pyrogram/types/payments/stars_status.py b/pyrogram/types/payments/stars_status.py new file mode 100644 index 00000000..753efdb3 --- /dev/null +++ b/pyrogram/types/payments/stars_status.py @@ -0,0 +1,107 @@ +# 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 + +import pyrogram +from pyrogram import raw, types +from ..object import Object + + +class StarsStatus(Object): + """Represents the current Stars balance and transactions. + + Parameters: + balance (:obj:`~pyrogram.types.StarsAmount`): + Current Stars balance. + + subscriptions (List of :obj:`~pyrogram.types.StarsSubscription`, *optional*): + Active subscriptions. + + subscriptions_next_offset (``str``, *optional*): + Offset for fetching more subscriptions. + + subscriptions_missing_balance (``int``, *optional*): + Amount of Stars missing for subscription renewal. + + history (List of :obj:`~pyrogram.types.StarsTransaction`, *optional*): + Transaction history. + + history_next_offset (``str``, *optional*): + Offset for fetching more history. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + balance: "types.StarsAmount" = None, + subscriptions: List["types.StarsSubscription"] = None, + subscriptions_next_offset: str = None, + subscriptions_missing_balance: int = None, + history: List["types.StarsTransaction"] = None, + history_next_offset: str = None + ): + super().__init__(client) + + self.balance = balance + self.subscriptions = subscriptions + self.subscriptions_next_offset = subscriptions_next_offset + self.subscriptions_missing_balance = subscriptions_missing_balance + self.history = history + self.history_next_offset = history_next_offset + + @staticmethod + def _parse( + client: "pyrogram.Client", + status: "raw.types.payments.StarsStatus", + users: dict = None, + chats: dict = None + ) -> "StarsStatus": + from .stars_amount import StarsAmount + from .stars_transaction import StarsTransaction + from .stars_subscription import StarsSubscription + + if users is None: + users = {u.id: u for u in status.users} + if chats is None: + chats = {c.id: c for c in status.chats} + + subscriptions = None + if status.subscriptions: + subscriptions = types.List([ + StarsSubscription._parse(client, s, users, chats) + for s in status.subscriptions + ]) + + history = None + if status.history: + history = types.List([ + StarsTransaction._parse(client, t, users, chats) + for t in status.history + ]) + + return StarsStatus( + client=client, + balance=StarsAmount._parse(client, status.balance), + subscriptions=subscriptions, + subscriptions_next_offset=status.subscriptions_next_offset, + subscriptions_missing_balance=status.subscriptions_missing_balance, + history=history, + history_next_offset=status.next_offset + ) diff --git a/pyrogram/types/payments/stars_subscription.py b/pyrogram/types/payments/stars_subscription.py new file mode 100644 index 00000000..2ae22a95 --- /dev/null +++ b/pyrogram/types/payments/stars_subscription.py @@ -0,0 +1,163 @@ +# 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 Optional + +import pyrogram +from pyrogram import raw, types, utils +from ..object import Object + + +class StarsSubscriptionPricing(Object): + """Represents the pricing of a Stars subscription. + + Parameters: + period (``int``): + Subscription period in seconds. + + amount (:obj:`~pyrogram.types.StarsAmount`): + Amount of Stars for this subscription period. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + period: int, + amount: "types.StarsAmount" = None + ): + super().__init__(client) + + self.period = period + self.amount = amount + + @staticmethod + def _parse( + client: "pyrogram.Client", + pricing: "raw.types.StarsSubscriptionPricing" + ) -> "StarsSubscriptionPricing": + from .stars_amount import StarsAmount + + if pricing is None: + return None + + return StarsSubscriptionPricing( + client=client, + period=pricing.period, + amount=StarsAmount._parse_int(client, pricing.amount) + ) + + +class StarsSubscription(Object): + """Represents a Telegram Stars subscription. + + Parameters: + id (``str``): + Subscription ID. + + peer (:obj:`~pyrogram.types.User` | :obj:`~pyrogram.types.Chat`): + The peer (user or chat) associated with this subscription. + + until_date (:py:obj:`~datetime.datetime`): + Expiration date of the current subscription period. + + pricing (:obj:`~pyrogram.types.StarsSubscriptionPricing`): + Pricing of the subscription. + + title (``str``, *optional*): + Title of the subscription (for bot subscriptions). + + is_canceled (``bool``, *optional*): + Whether this subscription was canceled. + + can_refulfill (``bool``, *optional*): + Whether we left the channel but can still rejoin. + + missing_balance (``bool``, *optional*): + Whether the subscription expired due to insufficient balance. + + bot_canceled (``bool``, *optional*): + Whether the bot canceled this subscription. + + chat_invite_hash (``str``, *optional*): + Invitation link to renew after cancellation. + + invoice_slug (``str``, *optional*): + For bot subscriptions, the invoice identifier. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + id: str, + peer: types.User = None, + until_date: datetime = None, + pricing: "StarsSubscriptionPricing" = None, + title: str = None, + is_canceled: bool = None, + can_refulfill: bool = None, + missing_balance: bool = None, + bot_canceled: bool = None, + chat_invite_hash: str = None, + invoice_slug: str = None + ): + super().__init__(client) + + self.id = id + self.peer = peer + self.until_date = until_date + self.pricing = pricing + self.title = title + self.is_canceled = is_canceled + self.can_refulfill = can_refulfill + self.missing_balance = missing_balance + self.bot_canceled = bot_canceled + self.chat_invite_hash = chat_invite_hash + self.invoice_slug = invoice_slug + + @staticmethod + def _parse( + client: "pyrogram.Client", + subscription: "raw.types.StarsSubscription", + users: dict, + chats: dict + ) -> "StarsSubscription": + peer = None + if isinstance(subscription.peer, raw.types.PeerUser): + peer = types.User._parse(client, users.get(subscription.peer.user_id)) + elif isinstance(subscription.peer, raw.types.PeerChannel): + peer = types.Chat._parse_channel_chat(client, chats.get(subscription.peer.channel_id)) + elif isinstance(subscription.peer, raw.types.PeerChat): + peer = types.Chat._parse_chat_chat(client, chats.get(subscription.peer.chat_id)) + + return StarsSubscription( + client=client, + id=subscription.id, + peer=peer, + until_date=utils.timestamp_to_datetime(subscription.until_date), + pricing=StarsSubscriptionPricing._parse(client, subscription.pricing), + title=subscription.title, + is_canceled=subscription.canceled or None, + can_refulfill=subscription.can_refulfill or None, + missing_balance=subscription.missing_balance or None, + bot_canceled=subscription.bot_canceled or None, + chat_invite_hash=subscription.chat_invite_hash, + invoice_slug=subscription.invoice_slug + ) diff --git a/pyrogram/types/payments/stars_topup_option.py b/pyrogram/types/payments/stars_topup_option.py new file mode 100644 index 00000000..0e2d54d4 --- /dev/null +++ b/pyrogram/types/payments/stars_topup_option.py @@ -0,0 +1,76 @@ +# 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 + +import pyrogram +from pyrogram import raw +from ..object import Object + + +class StarsTopupOption(Object): + """Telegram Stars topup option. + + Parameters: + stars (``int``): + Amount of Telegram stars. + + currency (``str``): + Three-letter ISO 4217 currency code. + + amount (``int``): + Price of the product in the smallest units of the currency (integer, not float/double). + + extended (``bool``, *optional*): + If set, the option must only be shown in the full list of topup options. + + store_product (``str``, *optional*): + Identifier of the store product associated with the option, official apps only. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + stars: int, + currency: str, + amount: int, + extended: bool = None, + store_product: str = None + ): + super().__init__(client) + + self.stars = stars + self.currency = currency + self.amount = amount + self.extended = extended + self.store_product = store_product + + @staticmethod + def _parse( + client: "pyrogram.Client", + option: "raw.types.StarsTopupOption" + ) -> "StarsTopupOption": + return StarsTopupOption( + client=client, + stars=option.stars, + currency=option.currency, + amount=option.amount, + extended=option.extended, + store_product=option.store_product + ) diff --git a/pyrogram/types/payments/stars_transaction.py b/pyrogram/types/payments/stars_transaction.py new file mode 100644 index 00000000..1e6e2168 --- /dev/null +++ b/pyrogram/types/payments/stars_transaction.py @@ -0,0 +1,214 @@ +# 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 Optional + +import pyrogram +from pyrogram import raw, types, utils +from ..object import Object + + +class StarsTransactionPeer(Object): + """Represents the peer involved in a Stars transaction. + + Parameters: + type (``str``): + Type of the peer. Can be one of: + - "user": A user + - "channel": A channel/supergroup + - "app_store": Apple App Store + - "play_market": Google Play Market + - "fragment": Fragment + - "premium_bot": Premium Bot + - "ads": Telegram Ads + - "api": Telegram API usage + - "unsupported": Unsupported peer type + + user (:obj:`~pyrogram.types.User`, *optional*): + For "user" type, the user. + + chat (:obj:`~pyrogram.types.Chat`, *optional*): + For "channel" type, the chat. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + type: str, + user: "types.User" = None, + chat: "types.Chat" = None + ): + super().__init__(client) + + self.type = type + self.user = user + self.chat = chat + + @staticmethod + def _parse( + client: "pyrogram.Client", + peer: "raw.base.StarsTransactionPeer", + users: dict, + chats: dict + ) -> "StarsTransactionPeer": + if isinstance(peer, raw.types.StarsTransactionPeer): + if isinstance(peer.peer, raw.types.PeerUser): + return StarsTransactionPeer( + client=client, + type="user", + user=types.User._parse(client, users.get(peer.peer.user_id)) + ) + elif isinstance(peer.peer, raw.types.PeerChannel): + return StarsTransactionPeer( + client=client, + type="channel", + chat=types.Chat._parse_channel_chat(client, chats.get(peer.peer.channel_id)) + ) + elif isinstance(peer.peer, raw.types.PeerChat): + return StarsTransactionPeer( + client=client, + type="channel", + chat=types.Chat._parse_chat_chat(client, chats.get(peer.peer.chat_id)) + ) + elif isinstance(peer, raw.types.StarsTransactionPeerAppStore): + return StarsTransactionPeer(client=client, type="app_store") + elif isinstance(peer, raw.types.StarsTransactionPeerPlayMarket): + return StarsTransactionPeer(client=client, type="play_market") + elif isinstance(peer, raw.types.StarsTransactionPeerFragment): + return StarsTransactionPeer(client=client, type="fragment") + elif isinstance(peer, raw.types.StarsTransactionPeerPremiumBot): + return StarsTransactionPeer(client=client, type="premium_bot") + elif isinstance(peer, raw.types.StarsTransactionPeerAds): + return StarsTransactionPeer(client=client, type="ads") + elif isinstance(peer, raw.types.StarsTransactionPeerApi): + return StarsTransactionPeer(client=client, type="api") + + return StarsTransactionPeer(client=client, type="unsupported") + + +class StarsTransaction(Object): + """Represents a Telegram Stars transaction. + + Parameters: + id (``str``): + Transaction ID. + + amount (:obj:`~pyrogram.types.StarsAmount`): + Amount of Stars in this transaction. + + date (:py:obj:`~datetime.datetime`): + Date of the transaction. + + peer (:obj:`~pyrogram.types.StarsTransactionPeer`): + Source of incoming transaction, or recipient for outgoing. + + title (``str``, *optional*): + Title of the transaction (for bot/channel subscriptions). + + description (``str``, *optional*): + Description of the transaction. + + is_refund (``bool``, *optional*): + Whether this is a refund transaction. + + is_pending (``bool``, *optional*): + Whether this transaction is pending. + + is_failed (``bool``, *optional*): + Whether this transaction failed. + + is_gift (``bool``, *optional*): + Whether this was a gift from the peer. + + is_reaction (``bool``, *optional*): + Whether this was a paid reaction. + + is_subscription (``bool``, *optional*): + Whether this is a subscription payment. + + transaction_date (:py:obj:`~datetime.datetime`, *optional*): + Date when the transaction was completed. + + transaction_url (``str``, *optional*): + URL for more details about the transaction. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + id: str, + amount: "types.StarsAmount" = None, + date: datetime = None, + peer: "StarsTransactionPeer" = None, + title: str = None, + description: str = None, + is_refund: bool = None, + is_pending: bool = None, + is_failed: bool = None, + is_gift: bool = None, + is_reaction: bool = None, + is_subscription: bool = None, + transaction_date: datetime = None, + transaction_url: str = None + ): + super().__init__(client) + + self.id = id + self.amount = amount + self.date = date + self.peer = peer + self.title = title + self.description = description + self.is_refund = is_refund + self.is_pending = is_pending + self.is_failed = is_failed + self.is_gift = is_gift + self.is_reaction = is_reaction + self.is_subscription = is_subscription + self.transaction_date = transaction_date + self.transaction_url = transaction_url + + @staticmethod + def _parse( + client: "pyrogram.Client", + transaction: "raw.types.StarsTransaction", + users: dict, + chats: dict + ) -> "StarsTransaction": + from .stars_amount import StarsAmount + + return StarsTransaction( + client=client, + id=transaction.id, + amount=StarsAmount._parse(client, transaction.amount), + date=utils.timestamp_to_datetime(transaction.date), + peer=StarsTransactionPeer._parse(client, transaction.peer, users, chats), + title=transaction.title, + description=transaction.description, + is_refund=transaction.refund or None, + is_pending=transaction.pending or None, + is_failed=transaction.failed or None, + is_gift=transaction.gift or None, + is_reaction=transaction.reaction or None, + is_subscription=transaction.subscription or None, + transaction_date=utils.timestamp_to_datetime(transaction.transaction_date), + transaction_url=transaction.transaction_url + ) diff --git a/pyrogram/types/saved_messages/__init__.py b/pyrogram/types/saved_messages/__init__.py new file mode 100644 index 00000000..abf0e39f --- /dev/null +++ b/pyrogram/types/saved_messages/__init__.py @@ -0,0 +1,23 @@ +# 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 .saved_dialog import SavedDialog +from .saved_dialogs import SavedDialogs +from .saved_reaction_tag import SavedReactionTag + +__all__ = ["SavedDialog", "SavedDialogs", "SavedReactionTag"] diff --git a/pyrogram/types/saved_messages/saved_dialog.py b/pyrogram/types/saved_messages/saved_dialog.py new file mode 100644 index 00000000..242eb425 --- /dev/null +++ b/pyrogram/types/saved_messages/saved_dialog.py @@ -0,0 +1,91 @@ +# 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 SavedDialog(Object): + """Represents a saved messages dialog. + + Parameters: + peer (:obj:`~pyrogram.types.Chat` | :obj:`~pyrogram.types.User`): + The peer of the saved dialog. + + top_message (:obj:`~pyrogram.types.Message`): + The top message of the saved dialog. + + pinned (``bool``, *optional*): + Whether the dialog is pinned. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + peer: "types.Chat" = None, + top_message: "types.Message" = None, + pinned: bool = None + ): + super().__init__(client) + + self.peer = peer + self.top_message = top_message + self.pinned = pinned + + @staticmethod + def _parse( + client: "pyrogram.Client", + dialog: "raw.types.SavedDialog", + messages: dict, + users: dict, + chats: dict + ) -> "SavedDialog": + return SavedDialog( + client=client, + peer=types.Chat._parse(client, dialog.peer, users, chats), + top_message=messages.get(dialog.top_message), + pinned=getattr(dialog, "pinned", None) + ) + + async def pin(self) -> bool: + """Bound method *pin* of :obj:`~pyrogram.types.SavedDialog`. + + Use as a shortcut for: + .. code-block:: python + + await dialog.pin() + + Returns: + ``bool``: True on success. + """ + return await self._client.pin_saved_dialog(peer=self.peer.id, pinned=True) + + async def unpin(self) -> bool: + """Bound method *unpin* of :obj:`~pyrogram.types.SavedDialog`. + + Use as a shortcut for: + .. code-block:: python + + await dialog.unpin() + + Returns: + ``bool``: True on success. + """ + return await self._client.pin_saved_dialog(peer=self.peer.id, pinned=False) diff --git a/pyrogram/types/saved_messages/saved_dialogs.py b/pyrogram/types/saved_messages/saved_dialogs.py new file mode 100644 index 00000000..cfe28dd7 --- /dev/null +++ b/pyrogram/types/saved_messages/saved_dialogs.py @@ -0,0 +1,68 @@ +# 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 SavedDialogs(Object): + """Represents a list of saved messages dialogs. + + Parameters: + count (``int``): + Total number of saved dialogs. + + dialogs (List of :obj:`~pyrogram.types.SavedDialog`): + List of saved dialogs. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + count: int, + dialogs: List["types.SavedDialog"] + ): + super().__init__(client) + + self.count = count + self.dialogs = dialogs + + @staticmethod + async def _parse( + client: "pyrogram.Client", + saved_dialogs: "raw.base.messages.SavedDialogs" + ) -> "SavedDialogs": + users = {u.id: u for u in saved_dialogs.users} + chats = {c.id: c for c in saved_dialogs.chats} + + messages = { + m.id: await types.Message._parse(client, m, users, chats) + for m in saved_dialogs.messages + } + + return SavedDialogs( + client=client, + count=getattr(saved_dialogs, "count", len(saved_dialogs.dialogs)), + dialogs=types.List([ + SavedDialog._parse(client, d, messages, users, chats) + for d in saved_dialogs.dialogs + ]) + ) diff --git a/pyrogram/types/saved_messages/saved_reaction_tag.py b/pyrogram/types/saved_messages/saved_reaction_tag.py new file mode 100644 index 00000000..85eb841d --- /dev/null +++ b/pyrogram/types/saved_messages/saved_reaction_tag.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 . + +import pyrogram +from pyrogram import raw, types +from ..object import Object + + +class SavedReactionTag(Object): + """Represents a saved messages reaction tag. + + Parameters: + reaction (:obj:`~pyrogram.types.Reaction`): + The reaction. + + title (``str``, *optional*): + The title of the tag. + + count (``int``): + Number of messages with this tag. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + reaction: "types.Reaction", + title: str = None, + count: int + ): + super().__init__(client) + + self.reaction = reaction + self.title = title + self.count = count + + @staticmethod + def _parse( + client: "pyrogram.Client", + tag: "raw.types.SavedReactionTag" + ) -> "SavedReactionTag": + return SavedReactionTag( + client=client, + reaction=types.Reaction._parse(client, tag.reaction), + title=getattr(tag, "title", None), + count=tag.count + ) diff --git a/pyrogram/types/stats/__init__.py b/pyrogram/types/stats/__init__.py new file mode 100644 index 00000000..6fee0b13 --- /dev/null +++ b/pyrogram/types/stats/__init__.py @@ -0,0 +1,22 @@ +# 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 .public_forward import PublicForward +from .public_forwards import PublicForwards + +__all__ = ["PublicForward", "PublicForwards"] diff --git a/pyrogram/types/stats/public_forward.py b/pyrogram/types/stats/public_forward.py new file mode 100644 index 00000000..1f7e13f9 --- /dev/null +++ b/pyrogram/types/stats/public_forward.py @@ -0,0 +1,72 @@ +# 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 + +import pyrogram +from pyrogram import raw, types +from ..object import Object + + +class PublicForward(Object): + """Represents a public forward. + + Parameters: + message (:obj:`~pyrogram.types.Message`, *optional*): + The message if it's a message forward. + + story (:obj:`~pyrogram.types.StoryItem`, *optional*): + The story if it's a story forward. + + chat (:obj:`~pyrogram.types.Chat`, *optional*): + The chat where the story was forwarded. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + message: "types.Message" = None, + story: "types.StoryItem" = None, + chat: "types.Chat" = None + ): + super().__init__(client) + + self.message = message + self.story = story + self.chat = chat + + @staticmethod + async def _parse( + client: "pyrogram.Client", + forward: "raw.base.PublicForward", + users: dict, + chats: dict + ) -> "PublicForward": + if isinstance(forward, raw.types.PublicForwardMessage): + return PublicForward( + client=client, + message=await types.Message._parse(client, forward.message, users, chats) + ) + + if isinstance(forward, raw.types.PublicForwardStory): + return PublicForward( + client=client, + chat=types.Chat._parse(client, forward.peer, users, chats), + story=types.StoryItem._parse(client, forward.story) + ) diff --git a/pyrogram/types/stats/public_forwards.py b/pyrogram/types/stats/public_forwards.py new file mode 100644 index 00000000..da96b9a8 --- /dev/null +++ b/pyrogram/types/stats/public_forwards.py @@ -0,0 +1,70 @@ +# 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 + +import pyrogram +from pyrogram import raw, types +from ..object import Object + + +class PublicForwards(Object): + """Represents a list of public forwards. + + Parameters: + count (``int``): + Total number of forwards. + + forwards (List of :obj:`~pyrogram.types.PublicForward`): + List of public forwards. + + next_offset (``str``, *optional*): + Offset for the next page. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + count: int, + forwards: List["types.PublicForward"], + next_offset: str = None + ): + super().__init__(client) + + self.count = count + self.forwards = forwards + self.next_offset = next_offset + + @staticmethod + async def _parse( + client: "pyrogram.Client", + forwards: "raw.types.stats.PublicForwards" + ) -> "PublicForwards": + users = {u.id: u for u in forwards.users} + chats = {c.id: c for c in forwards.chats} + + return PublicForwards( + client=client, + count=forwards.count, + forwards=types.List([ + await types.PublicForward._parse(client, f, users, chats) + for f in forwards.forwards + ]), + next_offset=forwards.next_offset + ) diff --git a/pyrogram/types/stories/__init__.py b/pyrogram/types/stories/__init__.py new file mode 100644 index 00000000..7a25daf2 --- /dev/null +++ b/pyrogram/types/stories/__init__.py @@ -0,0 +1,43 @@ +# 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 .story_item import StoryItem +from .story_views import StoryViews +from .peer_stories import PeerStories +from .media_area import MediaArea, MediaAreaCoordinates +from .stories_stealth_mode import StoriesStealthMode +from .story_forward_header import StoryForwardHeader +from .story_view import StoryView +from .story_views_list import StoryViewsList +from .story_reaction import StoryReaction +from .story_reactions_list import StoryReactionsList + + +__all__ = [ + "StoryItem", + "StoryViews", + "PeerStories", + "MediaArea", + "MediaAreaCoordinates", + "StoriesStealthMode", + "StoryForwardHeader", + "StoryView", + "StoryViewsList", + "StoryReaction", + "StoryReactionsList" +] diff --git a/pyrogram/types/stories/media_area.py b/pyrogram/types/stories/media_area.py new file mode 100644 index 00000000..efb744a6 --- /dev/null +++ b/pyrogram/types/stories/media_area.py @@ -0,0 +1,233 @@ +# 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 + +import pyrogram +from pyrogram import raw +from ..object import Object + + +class MediaAreaCoordinates(Object): + """Coordinates and size of a clickable rectangular area on top of a story. + + Parameters: + x (``float``): + The abscissa of the rectangle's center, as a percentage of the media width (0-100). + + y (``float``): + The ordinate of the rectangle's center, as a percentage of the media height (0-100). + + width (``float``): + The width of the rectangle, as a percentage of the media width (0-100). + + height (``float``): + The height of the rectangle, as a percentage of the media height (0-100). + + rotation (``float``): + Clockwise rotation angle of the rectangle, in degrees (0-360). + + radius (``float``, *optional*): + The radius of the rectangle corner rounding, as a percentage of the media width. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + x: float, + y: float, + width: float, + height: float, + rotation: float, + radius: float = None + ): + super().__init__(client) + + self.x = x + self.y = y + self.width = width + self.height = height + self.rotation = rotation + self.radius = radius + + @staticmethod + def _parse( + client: "pyrogram.Client", + coordinates: "raw.types.MediaAreaCoordinates" + ) -> "MediaAreaCoordinates": + return MediaAreaCoordinates( + client=client, + x=coordinates.x, + y=coordinates.y, + width=coordinates.w, + height=coordinates.h, + rotation=coordinates.rotation, + radius=coordinates.radius + ) + + +class MediaArea(Object): + """Represents a clickable rectangular area on a story. + + Parameters: + coordinates (:obj:`~pyrogram.types.MediaAreaCoordinates`): + The coordinates of this area. + + type (``str``): + Type of the media area. Can be one of: + - "geo": A location + - "venue": A venue + - "reaction": A suggested reaction + - "channel_post": A channel post + - "url": A URL + - "weather": Weather information + - "star_gift": A star gift + + geo (:obj:`~pyrogram.types.Location`, *optional*): + For "geo" type, the location. + + venue (:obj:`~pyrogram.types.Venue`, *optional*): + For "venue" type, the venue. + + reaction (:obj:`~pyrogram.types.Reaction`, *optional*): + For "reaction" type, the suggested reaction. + + channel_id (``int``, *optional*): + For "channel_post" type, the channel ID. + + message_id (``int``, *optional*): + For "channel_post" type, the message ID. + + url (``str``, *optional*): + For "url" type, the URL. + + is_dark (``bool``, *optional*): + For "reaction" type, whether it should be displayed in dark mode. + + is_flipped (``bool``, *optional*): + For "reaction" type, whether the reaction is flipped. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + coordinates: "MediaAreaCoordinates" = None, + type: str = None, + geo: "pyrogram.types.Location" = None, + venue: "pyrogram.types.Venue" = None, + reaction: "pyrogram.types.Reaction" = None, + channel_id: int = None, + message_id: int = None, + url: str = None, + is_dark: bool = None, + is_flipped: bool = None + ): + super().__init__(client) + + self.coordinates = coordinates + self.type = type + self.geo = geo + self.venue = venue + self.reaction = reaction + self.channel_id = channel_id + self.message_id = message_id + self.url = url + self.is_dark = is_dark + self.is_flipped = is_flipped + + @staticmethod + def _parse( + client: "pyrogram.Client", + media_area: "raw.base.MediaArea" + ) -> Optional["MediaArea"]: + from pyrogram import types + + if isinstance(media_area, raw.types.MediaAreaGeoPoint): + geo = None + if isinstance(media_area.geo, raw.types.GeoPoint): + geo = types.Location( + longitude=media_area.geo.long, + latitude=media_area.geo.lat, + client=client + ) + return MediaArea( + client=client, + coordinates=MediaAreaCoordinates._parse(client, media_area.coordinates), + type="geo", + geo=geo + ) + elif isinstance(media_area, raw.types.MediaAreaVenue): + venue = types.Venue( + location=types.Location( + longitude=media_area.geo.long if isinstance(media_area.geo, raw.types.GeoPoint) else 0, + latitude=media_area.geo.lat if isinstance(media_area.geo, raw.types.GeoPoint) else 0, + client=client + ), + title=media_area.title, + address=media_area.address, + foursquare_id=media_area.venue_id if media_area.provider == "foursquare" else None, + foursquare_type=media_area.venue_type if media_area.provider == "foursquare" else None, + client=client + ) + return MediaArea( + client=client, + coordinates=MediaAreaCoordinates._parse(client, media_area.coordinates), + type="venue", + venue=venue + ) + elif isinstance(media_area, raw.types.MediaAreaSuggestedReaction): + reaction = types.Reaction._parse(client, media_area.reaction) + return MediaArea( + client=client, + coordinates=MediaAreaCoordinates._parse(client, media_area.coordinates), + type="reaction", + reaction=reaction, + is_dark=media_area.dark or None, + is_flipped=media_area.flipped or None + ) + elif isinstance(media_area, raw.types.MediaAreaChannelPost): + return MediaArea( + client=client, + coordinates=MediaAreaCoordinates._parse(client, media_area.coordinates), + type="channel_post", + channel_id=media_area.channel_id, + message_id=media_area.msg_id + ) + elif isinstance(media_area, raw.types.MediaAreaUrl): + return MediaArea( + client=client, + coordinates=MediaAreaCoordinates._parse(client, media_area.coordinates), + type="url", + url=media_area.url + ) + elif isinstance(media_area, raw.types.MediaAreaWeather): + return MediaArea( + client=client, + coordinates=MediaAreaCoordinates._parse(client, media_area.coordinates), + type="weather" + ) + elif isinstance(media_area, raw.types.MediaAreaStarGift): + return MediaArea( + client=client, + coordinates=MediaAreaCoordinates._parse(client, media_area.coordinates), + type="star_gift" + ) + + return None diff --git a/pyrogram/types/stories/peer_stories.py b/pyrogram/types/stories/peer_stories.py new file mode 100644 index 00000000..6a13c013 --- /dev/null +++ b/pyrogram/types/stories/peer_stories.py @@ -0,0 +1,86 @@ +# 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 raw, types, utils +from ..object import Object + + +class PeerStories(Object): + """Stories associated with a peer. + + Parameters: + peer (:obj:`~pyrogram.types.User` | :obj:`~pyrogram.types.Chat`): + The peer (user or chat) these stories belong to. + + stories (List of :obj:`~pyrogram.types.StoryItem`): + List of stories. + + max_read_id (``int``, *optional*): + The ID of the last read story. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + peer: Union["types.User", "types.Chat"] = None, + stories: List["types.StoryItem"] = None, + max_read_id: int = None + ): + super().__init__(client) + + self.peer = peer + self.stories = stories + self.max_read_id = max_read_id + + @staticmethod + async def _parse( + client: "pyrogram.Client", + peer_stories: "raw.types.PeerStories", + users: dict, + chats: dict + ) -> "PeerStories": + from pyrogram.types.stories import StoryItem + + # Parse the peer + peer = None + if isinstance(peer_stories.peer, raw.types.PeerUser): + peer = types.User._parse(client, users.get(peer_stories.peer.user_id)) + elif isinstance(peer_stories.peer, raw.types.PeerChannel): + peer = types.Chat._parse_channel_chat(client, chats.get(peer_stories.peer.channel_id)) + elif isinstance(peer_stories.peer, raw.types.PeerChat): + peer = types.Chat._parse_chat_chat(client, chats.get(peer_stories.peer.chat_id)) + + # Parse stories + stories = [] + for story in peer_stories.stories: + if isinstance(story, raw.types.StoryItem): + parsed_story = await StoryItem._parse( + client, story, users, chats, peer_stories.peer + ) + stories.append(parsed_story) + + return PeerStories( + client=client, + peer=peer, + stories=types.List(stories) if stories else None, + max_read_id=peer_stories.max_read_id + ) diff --git a/pyrogram/types/stories/stories_stealth_mode.py b/pyrogram/types/stories/stories_stealth_mode.py new file mode 100644 index 00000000..8c886e9b --- /dev/null +++ b/pyrogram/types/stories/stories_stealth_mode.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 datetime import datetime +from typing import Optional + +import pyrogram +from pyrogram import raw, utils +from ..object import Object + + +class StoriesStealthMode(Object): + """Information about the stealth mode for stories. + + When stealth mode is enabled, the user's views of other users' stories + will not be recorded for a period of time. + + Parameters: + active_until (:py:obj:`~datetime.datetime`, *optional*): + If set, stealth mode is currently active and will be until this date. + + cooldown_until (:py:obj:`~datetime.datetime`, *optional*): + If set, the user can't enable stealth mode again until this date. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + active_until: datetime = None, + cooldown_until: datetime = None + ): + super().__init__(client) + + self.active_until = active_until + self.cooldown_until = cooldown_until + + @staticmethod + def _parse( + client: "pyrogram.Client", + stealth_mode: "raw.types.StoriesStealthMode" + ) -> Optional["StoriesStealthMode"]: + if stealth_mode is None: + return None + + return StoriesStealthMode( + client=client, + active_until=utils.timestamp_to_datetime(stealth_mode.active_until_date), + cooldown_until=utils.timestamp_to_datetime(stealth_mode.cooldown_until_date) + ) diff --git a/pyrogram/types/stories/story_forward_header.py b/pyrogram/types/stories/story_forward_header.py new file mode 100644 index 00000000..e4627bd4 --- /dev/null +++ b/pyrogram/types/stories/story_forward_header.py @@ -0,0 +1,78 @@ +# 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 + +import pyrogram +from pyrogram import raw, utils +from ..object import Object + + +class StoryForwardHeader(Object): + """Contains information about the original story that was reposted. + + Parameters: + from_peer_id (``int``, *optional*): + ID of the peer that originally posted this story. + + from_name (``str``, *optional*): + For stories forwarded from users who have hidden their account, + the name of the original poster. + + story_id (``int``, *optional*): + ID of the original story. + + is_modified (``bool``, *optional*): + Whether this story was modified when reposted. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + from_peer_id: int = None, + from_name: str = None, + story_id: int = None, + is_modified: bool = None + ): + super().__init__(client) + + self.from_peer_id = from_peer_id + self.from_name = from_name + self.story_id = story_id + self.is_modified = is_modified + + @staticmethod + def _parse( + client: "pyrogram.Client", + fwd_header: "raw.types.StoryFwdHeader" + ) -> Optional["StoryForwardHeader"]: + if fwd_header is None: + return None + + from_peer_id = None + if fwd_header.from_: + from_peer_id = utils.get_peer_id(fwd_header.from_) + + return StoryForwardHeader( + client=client, + from_peer_id=from_peer_id, + from_name=fwd_header.from_name, + story_id=fwd_header.story_id, + is_modified=fwd_header.modified or None + ) diff --git a/pyrogram/types/stories/story_item.py b/pyrogram/types/stories/story_item.py new file mode 100644 index 00000000..e980f561 --- /dev/null +++ b/pyrogram/types/stories/story_item.py @@ -0,0 +1,364 @@ +# 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 List, Optional, Union + +import pyrogram +from pyrogram import raw, types, utils +from ..object import Object + + +class StoryItem(Object): + """Represents a story. + + Parameters: + id (``int``): + Unique identifier of the story. + + from_user (:obj:`~pyrogram.types.User`, *optional*): + The user who posted this story. + + sender_chat (:obj:`~pyrogram.types.Chat`, *optional*): + The chat that posted this story (for channel stories). + + date (:py:obj:`~datetime.datetime`): + When the story was posted. + + expire_date (:py:obj:`~datetime.datetime`): + When the story will expire. + + caption (``str``, *optional*): + Caption for the story. + + caption_entities (List of :obj:`~pyrogram.types.MessageEntity`, *optional*): + Special entities like usernames, URLs, etc. that appear in the caption. + + photo (:obj:`~pyrogram.types.Photo`, *optional*): + Story photo, if the story contains a photo. + + video (:obj:`~pyrogram.types.Video`, *optional*): + Story video, if the story contains a video. + + animation (:obj:`~pyrogram.types.Animation`, *optional*): + Story animation, if the story contains an animation. + + views (:obj:`~pyrogram.types.StoryViews`, *optional*): + View and reaction information. + + forward_from (:obj:`~pyrogram.types.StoryForwardHeader`, *optional*): + For reposted stories, information about the original story. + + media_areas (List of :obj:`~pyrogram.types.MediaArea`, *optional*): + Interactive areas on the story media. + + pinned (``bool``, *optional*): + Whether this story is pinned on the user's profile. + + is_public (``bool``, *optional*): + Whether this story is public and can be viewed by everyone. + + is_close_friends (``bool``, *optional*): + Whether this story can only be viewed by close friends. + + is_contacts (``bool``, *optional*): + Whether this story can only be viewed by contacts. + + is_selected_contacts (``bool``, *optional*): + Whether this story can only be viewed by a select list of contacts. + + no_forwards (``bool``, *optional*): + Whether this story is protected and cannot be forwarded. + + edited (``bool``, *optional*): + Whether this story was edited. + + outgoing (``bool``, *optional*): + Whether this is an outgoing story (posted by the current user). + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + id: int, + from_user: "types.User" = None, + sender_chat: "types.Chat" = None, + date: datetime = None, + expire_date: datetime = None, + caption: str = None, + caption_entities: List["types.MessageEntity"] = None, + photo: "types.Photo" = None, + video: "types.Video" = None, + animation: "types.Animation" = None, + views: "types.StoryViews" = None, + forward_from: "types.StoryForwardHeader" = None, + media_areas: List["types.MediaArea"] = None, + pinned: bool = None, + is_public: bool = None, + is_close_friends: bool = None, + is_contacts: bool = None, + is_selected_contacts: bool = None, + no_forwards: bool = None, + edited: bool = None, + outgoing: bool = None + ): + super().__init__(client) + + self.id = id + self.from_user = from_user + self.sender_chat = sender_chat + self.date = date + self.expire_date = expire_date + self.caption = caption + self.caption_entities = caption_entities + self.photo = photo + self.video = video + self.animation = animation + self.views = views + self.forward_from = forward_from + self.media_areas = media_areas + self.pinned = pinned + self.is_public = is_public + self.is_close_friends = is_close_friends + self.is_contacts = is_contacts + self.is_selected_contacts = is_selected_contacts + self.no_forwards = no_forwards + self.edited = edited + self.outgoing = outgoing + + @staticmethod + async def _parse( + client: "pyrogram.Client", + story: "raw.types.StoryItem", + users: dict, + chats: dict, + peer: "raw.base.Peer" = None + ) -> "StoryItem": + from pyrogram.types.stories import StoryViews, StoryForwardHeader, MediaArea + + # Parse the peer (who posted the story) + from_user = None + sender_chat = None + + if peer: + if isinstance(peer, raw.types.PeerUser): + from_user = types.User._parse(client, users.get(peer.user_id)) + elif isinstance(peer, (raw.types.PeerChannel, raw.types.PeerChat)): + peer_id = getattr(peer, "channel_id", None) or getattr(peer, "chat_id", None) + sender_chat = types.Chat._parse_channel_chat(client, chats.get(peer_id)) + + # If story has from_id, use that instead + if story.from_id: + if isinstance(story.from_id, raw.types.PeerUser): + from_user = types.User._parse(client, users.get(story.from_id.user_id)) + sender_chat = None + elif isinstance(story.from_id, raw.types.PeerChannel): + sender_chat = types.Chat._parse_channel_chat(client, chats.get(story.from_id.channel_id)) + from_user = None + + # Parse media + photo = None + video = None + animation = None + + if story.media: + if isinstance(story.media, raw.types.MessageMediaPhoto): + photo = types.Photo._parse(client, story.media.photo, story.media.ttl_seconds) + elif isinstance(story.media, raw.types.MessageMediaDocument): + doc = story.media.document + if isinstance(doc, raw.types.Document): + attributes = {type(attr): attr for attr in doc.attributes} + + if raw.types.DocumentAttributeAnimated in attributes: + video_attr = attributes.get(raw.types.DocumentAttributeVideo, None) + animation = types.Animation._parse(client, doc, video_attr, None) + elif raw.types.DocumentAttributeVideo in attributes: + video_attr = attributes[raw.types.DocumentAttributeVideo] + video = types.Video._parse(client, doc, video_attr, None, story.media.ttl_seconds) + + # Parse caption entities + caption_entities = None + if story.entities: + caption_entities = types.List([ + types.MessageEntity._parse(client, entity, {}) + for entity in story.entities + ]) + + # Parse views + views = None + if story.views: + views = StoryViews._parse(client, story.views) + + # Parse forward header + forward_from = None + if story.fwd_from: + forward_from = StoryForwardHeader._parse(client, story.fwd_from) + + # Parse media areas + media_areas = None + if story.media_areas: + parsed_areas = [] + for area in story.media_areas: + parsed_area = MediaArea._parse(client, area) + if parsed_area: + parsed_areas.append(parsed_area) + if parsed_areas: + media_areas = types.List(parsed_areas) + + return StoryItem( + client=client, + id=story.id, + from_user=from_user, + sender_chat=sender_chat, + date=utils.timestamp_to_datetime(story.date), + expire_date=utils.timestamp_to_datetime(story.expire_date), + caption=story.caption or None, + caption_entities=caption_entities, + photo=photo, + video=video, + animation=animation, + views=views, + forward_from=forward_from, + media_areas=media_areas, + pinned=story.pinned or None, + is_public=story.public or None, + is_close_friends=story.close_friends or None, + is_contacts=story.contacts or None, + is_selected_contacts=story.selected_contacts or None, + no_forwards=story.noforwards or None, + edited=story.edited or None, + outgoing=story.out or None + ) + + @property + def chat(self) -> Union["types.Chat", "types.User"]: + """The chat or user who posted this story.""" + return self.sender_chat or self.from_user + + async def delete(self) -> bool: + """Bound method *delete* of :obj:`~pyrogram.types.StoryItem`. + + Use as a shortcut for: + .. code-block:: python + + await story.delete() + + Returns: + ``bool``: True on success. + """ + return await self._client.delete_stories( + chat_id=self.chat.id, + story_ids=self.id + ) + + async def react(self, reaction: "types.Reaction" = None, add_to_recent: bool = None) -> bool: + """Bound method *react* of :obj:`~pyrogram.types.StoryItem`. + + Use as a shortcut for: + .. code-block:: python + + await story.react(reaction) + + Parameters: + reaction (:obj:`~pyrogram.types.Reaction`, *optional*): + The reaction to send. + + add_to_recent (``bool``, *optional*): + Whether to add to recent reactions. + + Returns: + ``bool``: True on success. + """ + return await self._client.send_story_reaction( + chat_id=self.chat.id, + story_id=self.id, + reaction=reaction, + add_to_recent=add_to_recent + ) + + async def get_views(self, query: str = None, limit: int = 20) -> "types.StoryViewsList": + """Bound method *get_views* of :obj:`~pyrogram.types.StoryItem`. + + Use as a shortcut for: + .. code-block:: python + + await story.get_views() + + Parameters: + query (``str``, *optional*): + Search query. + + limit (``int``, *optional*): + Maximum number of views to retrieve. + + Returns: + :obj:`~pyrogram.types.StoryViewsList`: The list of views. + """ + return await self._client.get_story_views_list( + chat_id=self.chat.id, + story_id=self.id, + query=query, + limit=limit + ) + + async def get_reactions(self, reaction: "types.Reaction" = None, limit: int = 20) -> "types.StoryReactionsList": + """Bound method *get_reactions* of :obj:`~pyrogram.types.StoryItem`. + + Use as a shortcut for: + .. code-block:: python + + await story.get_reactions() + + Parameters: + reaction (:obj:`~pyrogram.types.Reaction`, *optional*): + Filter by a specific reaction. + + limit (``int``, *optional*): + Maximum number of reactions to retrieve. + + Returns: + :obj:`~pyrogram.types.StoryReactionsList`: The list of reactions. + """ + return await self._client.get_story_reactions_list( + chat_id=self.chat.id, + story_id=self.id, + reaction=reaction, + limit=limit + ) + + async def get_public_forwards(self, limit: int = 20) -> "types.PublicForwards": + """Bound method *get_public_forwards* of :obj:`~pyrogram.types.StoryItem`. + + Use as a shortcut for: + .. code-block:: python + + await story.get_public_forwards() + + Parameters: + limit (``int``, *optional*): + Maximum number of forwards to retrieve. + + Returns: + :obj:`~pyrogram.types.PublicForwards`: The list of public forwards. + """ + return await self._client.get_story_public_forwards( + chat_id=self.chat.id, + story_id=self.id, + limit=limit + ) diff --git a/pyrogram/types/stories/story_reaction.py b/pyrogram/types/stories/story_reaction.py new file mode 100644 index 00000000..caf6246a --- /dev/null +++ b/pyrogram/types/stories/story_reaction.py @@ -0,0 +1,90 @@ +# 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 + +import pyrogram +from pyrogram import raw, types, utils +from ..object import Object + + +class StoryReaction(Object): + """Represents a story reaction. + + Parameters: + peer (:obj:`~pyrogram.types.Chat` | :obj:`~pyrogram.types.User`): + The peer who reacted to the story. + + date (``datetime``): + Date when the reaction was sent. + + reaction (:obj:`~pyrogram.types.Reaction`): + The reaction itself. + + message (:obj:`~pyrogram.types.Message`, *optional*): + The message if it's a public forward. + + story (:obj:`~pyrogram.types.StoryItem`, *optional*): + The reposted story. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + peer: "types.Chat" = None, + date: datetime = None, + reaction: "types.Reaction" = None, + message: "types.Message" = None, + story: "types.StoryItem" = None + ): + super().__init__(client) + + self.peer = peer + self.date = date + self.reaction = reaction + self.message = message + self.story = story + + @staticmethod + def _parse( + client: "pyrogram.Client", + reaction: "raw.base.StoryReaction", + users: dict = None, + chats: dict = None + ) -> "StoryReaction": + if isinstance(reaction, raw.types.StoryReaction): + return StoryReaction( + client=client, + peer=types.Chat._parse(client, reaction.peer_id, users, chats), + date=utils.timestamp_to_datetime(reaction.date), + reaction=types.Reaction._parse(client, reaction.reaction) + ) + + if isinstance(reaction, raw.types.StoryReactionPublicForward): + return StoryReaction( + client=client, + message=types.Message._parse(client, reaction.message, users, chats) + ) + + if isinstance(reaction, raw.types.StoryReactionPublicRepost): + return StoryReaction( + client=client, + peer=types.Chat._parse(client, reaction.peer_id, users, chats), + story=types.StoryItem._parse(client, reaction.story) + ) diff --git a/pyrogram/types/stories/story_reactions_list.py b/pyrogram/types/stories/story_reactions_list.py new file mode 100644 index 00000000..59b20c9d --- /dev/null +++ b/pyrogram/types/stories/story_reactions_list.py @@ -0,0 +1,70 @@ +# 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 StoryReactionsList(Object): + """Represents a list of story reactions. + + Parameters: + count (``int``): + Total number of reactions. + + reactions (List of :obj:`~pyrogram.types.StoryReaction`): + List of story reactions. + + next_offset (``str``, *optional*): + Offset for pagination. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + count: int, + reactions: List["types.StoryReaction"], + next_offset: str = None + ): + super().__init__(client) + + self.count = count + self.reactions = reactions + self.next_offset = next_offset + + @staticmethod + def _parse( + client: "pyrogram.Client", + reactions_list: "raw.types.stories.StoryReactionsList" + ) -> "StoryReactionsList": + users = {u.id: u for u in reactions_list.users} + chats = {c.id: c for c in reactions_list.chats} + + return StoryReactionsList( + client=client, + count=reactions_list.count, + reactions=types.List([ + types.StoryReaction._parse(client, r, users, chats) + for r in reactions_list.reactions + ]), + next_offset=reactions_list.next_offset + ) diff --git a/pyrogram/types/stories/story_view.py b/pyrogram/types/stories/story_view.py new file mode 100644 index 00000000..c242a507 --- /dev/null +++ b/pyrogram/types/stories/story_view.py @@ -0,0 +1,112 @@ +# 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 Optional + +import pyrogram +from pyrogram import raw, types, utils +from ..object import Object + + +class StoryView(Object): + """Represents a story view. + + Parameters: + user (:obj:`~pyrogram.types.User`, *optional*): + The user who viewed the story. + + date (``datetime``, *optional*): + Date when the story was viewed. + + reaction (:obj:`~pyrogram.types.Reaction`, *optional*): + Reaction sent by the user. + + message (:obj:`~pyrogram.types.Message`, *optional*): + The message if it's a public forward. + + chat (:obj:`~pyrogram.types.Chat`, *optional*): + The chat where the story was reposted. + + story (:obj:`~pyrogram.types.StoryItem`, *optional*): + The reposted story. + + blocked (``bool``, *optional*): + Whether the user is blocked from viewing stories. + + blocked_my_stories_from (``bool``, *optional*): + Whether the user is blocked from viewing my stories. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + user: "types.User" = None, + date: datetime = None, + reaction: "types.Reaction" = None, + message: "types.Message" = None, + chat: "types.Chat" = None, + story: "types.StoryItem" = None, + blocked: bool = None, + blocked_my_stories_from: bool = None + ): + super().__init__(client) + + self.user = user + self.date = date + self.reaction = reaction + self.message = message + self.chat = chat + self.story = story + self.blocked = blocked + self.blocked_my_stories_from = blocked_my_stories_from + + @staticmethod + def _parse( + client: "pyrogram.Client", + view: "raw.base.StoryView", + users: dict = None, + chats: dict = None + ) -> "StoryView": + if isinstance(view, raw.types.StoryView): + return StoryView( + client=client, + user=types.User._parse(client, users.get(view.user_id)) if users else None, + date=utils.timestamp_to_datetime(view.date), + reaction=types.Reaction._parse(client, view.reaction) if view.reaction else None, + blocked=view.blocked, + blocked_my_stories_from=view.blocked_my_stories_from + ) + + if isinstance(view, raw.types.StoryViewPublicForward): + return StoryView( + client=client, + message=types.Message._parse(client, view.message, users, chats), + blocked=view.blocked, + blocked_my_stories_from=view.blocked_my_stories_from + ) + + if isinstance(view, raw.types.StoryViewPublicRepost): + return StoryView( + client=client, + chat=types.Chat._parse(client, view.peer_id, users, chats), + story=types.StoryItem._parse(client, view.story), + blocked=view.blocked, + blocked_my_stories_from=view.blocked_my_stories_from + ) diff --git a/pyrogram/types/stories/story_views.py b/pyrogram/types/stories/story_views.py new file mode 100644 index 00000000..2a1d845c --- /dev/null +++ b/pyrogram/types/stories/story_views.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 List, Optional + +import pyrogram +from pyrogram import raw, types, utils +from ..object import Object + + +class StoryViews(Object): + """Aggregated view and reaction information of a story. + + Parameters: + views_count (``int``): + View counter of the story. + + has_viewers (``bool``, *optional*): + Whether the viewers list is currently viewable. + + forwards_count (``int``, *optional*): + Forward counter of the story. + + reactions (List of :obj:`~pyrogram.types.Reaction`, *optional*): + All reactions added to this story. + + reactions_count (``int``, *optional*): + Number of reactions added to the story. + + recent_viewers (List of ``int``, *optional*): + User IDs of some recent viewers of the story. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + views_count: int, + has_viewers: bool = None, + forwards_count: int = None, + reactions: List["types.Reaction"] = None, + reactions_count: int = None, + recent_viewers: List[int] = None + ): + super().__init__(client) + + self.views_count = views_count + self.has_viewers = has_viewers + self.forwards_count = forwards_count + self.reactions = reactions + self.reactions_count = reactions_count + self.recent_viewers = recent_viewers + + @staticmethod + def _parse( + client: "pyrogram.Client", + story_views: "raw.types.StoryViews" + ) -> "StoryViews": + reactions = None + if story_views.reactions: + reactions = [ + types.Reaction._parse(client, r.reaction) + for r in story_views.reactions + ] + + return StoryViews( + client=client, + views_count=story_views.views_count, + has_viewers=story_views.has_viewers or None, + forwards_count=story_views.forwards_count, + reactions=types.List(reactions) if reactions else None, + reactions_count=story_views.reactions_count, + recent_viewers=types.List(story_views.recent_viewers) if story_views.recent_viewers else None + ) diff --git a/pyrogram/types/stories/story_views_list.py b/pyrogram/types/stories/story_views_list.py new file mode 100644 index 00000000..9666e32d --- /dev/null +++ b/pyrogram/types/stories/story_views_list.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 List + +import pyrogram +from pyrogram import raw, types +from ..object import Object + + +class StoryViewsList(Object): + """Represents a list of story views. + + Parameters: + count (``int``): + Total number of views. + + views_count (``int``): + Total number of views (including anonymous). + + forwards_count (``int``): + Total number of forwards. + + reactions_count (``int``): + Total number of reactions. + + views (List of :obj:`~pyrogram.types.StoryView`): + List of story views. + + next_offset (``str``, *optional*): + Offset for pagination. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + count: int, + views_count: int, + forwards_count: int, + reactions_count: int, + views: List["types.StoryView"], + next_offset: str = None + ): + super().__init__(client) + + self.count = count + self.views_count = views_count + self.forwards_count = forwards_count + self.reactions_count = reactions_count + self.views = views + self.next_offset = next_offset + + @staticmethod + def _parse( + client: "pyrogram.Client", + views_list: "raw.types.stories.StoryViewsList" + ) -> "StoryViewsList": + users = {u.id: u for u in views_list.users} + chats = {c.id: c for c in views_list.chats} + + return StoryViewsList( + client=client, + count=views_list.count, + views_count=views_list.views_count, + forwards_count=views_list.forwards_count, + reactions_count=views_list.reactions_count, + views=types.List([ + types.StoryView._parse(client, v, users, chats) + for v in views_list.views + ]), + next_offset=views_list.next_offset + ) diff --git a/pyrogram/types/todo/__init__.py b/pyrogram/types/todo/__init__.py new file mode 100644 index 00000000..c84a5438 --- /dev/null +++ b/pyrogram/types/todo/__init__.py @@ -0,0 +1,27 @@ +# 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 .todo_list import TodoList +from .todo_item import TodoItem +from .todo_completion import TodoCompletion + +__all__ = [ + "TodoList", + "TodoItem", + "TodoCompletion" +] diff --git a/pyrogram/types/todo/todo_completion.py b/pyrogram/types/todo/todo_completion.py new file mode 100644 index 00000000..a61d837d --- /dev/null +++ b/pyrogram/types/todo/todo_completion.py @@ -0,0 +1,70 @@ +# 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 Optional + +import pyrogram +from pyrogram import raw, types, utils +from ..object import Object + + +class TodoCompletion(Object): + """Represents a completion entry for a to-do item. + + Parameters: + item_id (``int``): + The ID of the to-do item. + + user (:obj:`~pyrogram.types.User`): + The user who completed this item. + + date (:py:obj:`~datetime.datetime`): + When the item was completed. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + item_id: int = None, + user: "types.User" = None, + date: datetime = None + ): + super().__init__(client) + + self.item_id = item_id + self.user = user + self.date = date + + @staticmethod + def _parse( + client: "pyrogram.Client", + completion: "raw.types.TodoCompletion", + users: dict + ) -> "TodoCompletion": + user = None + if completion.user_id and completion.user_id in users: + user = types.User._parse(client, users[completion.user_id]) + + return TodoCompletion( + client=client, + item_id=completion.item_id, + user=user, + date=utils.timestamp_to_datetime(completion.date) if hasattr(completion, 'date') and completion.date else None + ) diff --git a/pyrogram/types/todo/todo_item.py b/pyrogram/types/todo/todo_item.py new file mode 100644 index 00000000..aadb6ed9 --- /dev/null +++ b/pyrogram/types/todo/todo_item.py @@ -0,0 +1,86 @@ +# 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 Optional, List + +import pyrogram +from pyrogram import raw, types, utils +from ..object import Object + + +class TodoItem(Object): + """Represents a single item in a to-do list. + + Parameters: + id (``int``): + Unique identifier of the to-do item. + + text (``str``): + The text content of the to-do item. + + completed_by (:obj:`~pyrogram.types.User`, *optional*): + The user who completed this item. + + completed_date (:py:obj:`~datetime.datetime`, *optional*): + When the item was completed. + + is_completed (``bool``, *optional*): + Whether this item is completed. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + id: int = None, + text: str = None, + completed_by: "types.User" = None, + completed_date: datetime = None, + is_completed: bool = None + ): + super().__init__(client) + + self.id = id + self.text = text + self.completed_by = completed_by + self.completed_date = completed_date + self.is_completed = is_completed + + @staticmethod + def _parse( + client: "pyrogram.Client", + item: "raw.types.TodoItem", + users: dict = None + ) -> "TodoItem": + completed_by = None + if hasattr(item, 'completed_by') and item.completed_by and users: + if item.completed_by in users: + completed_by = types.User._parse(client, users[item.completed_by]) + + return TodoItem( + client=client, + id=item.id, + text=item.text, + completed_by=completed_by, + completed_date=utils.timestamp_to_datetime(item.completed_date) if hasattr(item, 'completed_date') and item.completed_date else None, + is_completed=bool(completed_by or (hasattr(item, 'completed') and item.completed)) + ) + + def write(self): + return self.text diff --git a/pyrogram/types/todo/todo_list.py b/pyrogram/types/todo/todo_list.py new file mode 100644 index 00000000..d880c984 --- /dev/null +++ b/pyrogram/types/todo/todo_list.py @@ -0,0 +1,124 @@ +# 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 Optional, List + +import pyrogram +from pyrogram import raw, types, utils +from ..object import Object + + +class TodoList(Object): + """Represents a to-do list attached to a message. + + Parameters: + id (``int``): + Unique identifier of the to-do list. + + title (``str``): + Title of the to-do list. + + items (List of :obj:`~pyrogram.types.TodoItem`): + List of to-do items. + + completions (List of :obj:`~pyrogram.types.TodoCompletion`, *optional*): + List of completion entries. + + completed_count (``int``, *optional*): + Number of completed items. + + total_count (``int``, *optional*): + Total number of items. + + is_others_can_complete (``bool``, *optional*): + Whether other users can mark items as complete. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + id: int = None, + title: str = None, + items: List["types.TodoItem"] = None, + completions: List["types.TodoCompletion"] = None, + completed_count: int = None, + total_count: int = None, + is_others_can_complete: bool = None + ): + super().__init__(client) + + self.id = id + self.title = title + self.items = items + self.completions = completions + self.completed_count = completed_count + self.total_count = total_count + self.is_others_can_complete = is_others_can_complete + + @staticmethod + def _parse( + client: "pyrogram.Client", + todo: "raw.types.TodoList", + users: dict = None + ) -> "TodoList": + users = users or {} + + items = [] + if hasattr(todo, 'list') and todo.list: + items = [ + types.TodoItem._parse(client, item, users) + for item in todo.list + ] + + return TodoList( + client=client, + id=None, + title=todo.title.text if hasattr(todo.title, 'text') else (todo.title if isinstance(todo.title, str) else None), + items=types.List(items) if items else None, + completions=None, + completed_count=None, + total_count=len(items) if items else None, + is_others_can_complete=todo.others_can_complete if hasattr(todo, 'others_can_complete') else None + ) + + @staticmethod + def _parse_media( + client: "pyrogram.Client", + media: "raw.types.MessageMediaToDo", + users: dict = None + ) -> "TodoList": + """Parse from MessageMediaToDo which includes completions.""" + users = users or {} + + todo_list = TodoList._parse(client, media.todo, users) if media.todo else None + + if todo_list: + completions = [] + if hasattr(media, 'completions') and media.completions: + completions = [ + types.TodoCompletion._parse(client, c, users) + for c in media.completions + ] + todo_list.completions = types.List(completions) if completions else None + + if completions: + todo_list.completed_count = len(completions) + + return todo_list diff --git a/pyrogram/types/user_and_chats/__init__.py b/pyrogram/types/user_and_chats/__init__.py index 2710f56a..944d40bf 100644 --- a/pyrogram/types/user_and_chats/__init__.py +++ b/pyrogram/types/user_and_chats/__init__.py @@ -16,6 +16,9 @@ # You should have received a copy of the GNU Lesser General Public License # along with Pyrogram. If not, see . +from .birthday import Birthday +from .bot_verification import BotVerification +from .bot_verifier_settings import BotVerifierSettings from .chat import Chat from .chat_admin_with_invite_links import ChatAdminWithInviteLinks from .chat_event import ChatEvent @@ -34,14 +37,19 @@ from .emoji_status import EmojiStatus from .invite_link_importer import InviteLinkImporter from .restriction import Restriction +from .peer_color import PeerColor from .user import User from .username import Username from .video_chat_ended import VideoChatEnded from .video_chat_members_invited import VideoChatMembersInvited from .video_chat_scheduled import VideoChatScheduled from .video_chat_started import VideoChatStarted +from .wallpaper import Wallpaper, WallpaperSettings __all__ = [ + "Birthday", + "BotVerification", + "BotVerifierSettings", "Chat", "ChatMember", "ChatPermissions", @@ -65,5 +73,8 @@ "ChatPrivileges", "ChatJoiner", "EmojiStatus", - "ChatReactions" + "ChatReactions", + "PeerColor", + "Wallpaper", + "WallpaperSettings" ] diff --git a/pyrogram/types/user_and_chats/birthday.py b/pyrogram/types/user_and_chats/birthday.py new file mode 100644 index 00000000..e5128a68 --- /dev/null +++ b/pyrogram/types/user_and_chats/birthday.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 Optional + +import pyrogram +from pyrogram import raw +from ..object import Object + + +class Birthday(Object): + """Represents a user's birthday. + + Parameters: + day (``int``): + Day of the month (1-31). + + month (``int``): + Month (1-12). + + year (``int``, *optional*): + Year. May be omitted if the user hasn't shared their birth year. + """ + + def __init__( + self, + *, + day: int = None, + month: int = None, + year: int = None + ): + super().__init__() + + self.day = day + self.month = month + self.year = year + + @staticmethod + def _parse(birthday: "raw.types.Birthday") -> Optional["Birthday"]: + if birthday is None: + return None + + return Birthday( + day=birthday.day, + month=birthday.month, + year=birthday.year if hasattr(birthday, 'year') and birthday.year else None + ) diff --git a/pyrogram/types/user_and_chats/bot_verification.py b/pyrogram/types/user_and_chats/bot_verification.py new file mode 100644 index 00000000..c17b2893 --- /dev/null +++ b/pyrogram/types/user_and_chats/bot_verification.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 Optional + +import pyrogram +from pyrogram import raw +from ..object import Object + + +class BotVerification(Object): + """Represents bot verification information. + + Parameters: + bot_id (``int``): + The ID of the verification bot. + + icon (``int``): + Custom emoji ID for the verification icon. + + description (``str``, *optional*): + Description of the verification. + """ + + def __init__( + self, + *, + bot_id: int = None, + icon: int = None, + description: str = None + ): + super().__init__() + + self.bot_id = bot_id + self.icon = icon + self.description = description + + @staticmethod + def _parse(verification: "raw.types.BotVerification") -> Optional["BotVerification"]: + if verification is None: + return None + + return BotVerification( + bot_id=verification.bot_id, + icon=verification.icon, + description=verification.description if hasattr(verification, 'description') else None + ) diff --git a/pyrogram/types/user_and_chats/bot_verifier_settings.py b/pyrogram/types/user_and_chats/bot_verifier_settings.py new file mode 100644 index 00000000..3c03e49e --- /dev/null +++ b/pyrogram/types/user_and_chats/bot_verifier_settings.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 . + +import pyrogram +from pyrogram import raw +from ..object import Object + + +class BotVerifierSettings(Object): + """Bot verifier settings. + + Parameters: + icon (``int``): + The custom emoji ID used as a verification icon. + + company (``str``): + The company name. + + can_modify_custom_description (``bool``, *optional*): + Whether the bot can modify the custom description. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + icon: int, + company: str, + can_modify_custom_description: bool = None + ): + super().__init__(client) + + self.icon = icon + self.company = company + self.can_modify_custom_description = can_modify_custom_description + + @staticmethod + def _parse(client: "pyrogram.Client", settings: "raw.types.BotVerifierSettings") -> "BotVerifierSettings": + if not settings: + return None + + return BotVerifierSettings( + client=client, + icon=settings.icon, + company=settings.company, + can_modify_custom_description=getattr(settings, "can_modify_custom_description", None) + ) diff --git a/pyrogram/types/user_and_chats/chat.py b/pyrogram/types/user_and_chats/chat.py index 5323a7b0..e19c5800 100644 --- a/pyrogram/types/user_and_chats/chat.py +++ b/pyrogram/types/user_and_chats/chat.py @@ -142,6 +142,102 @@ class Chat(Object): usernames (List of :obj:`~pyrogram.types.Username`, *optional*): The list of chat's collectible (and basic) usernames if availables. + admin_privileges (:obj:`~pyrogram.types.ChatPrivileges`, *optional*): + Administrator rights available to the current user in this chat. + + stories_hidden (``bool``, *optional*): + True, if stories from this chat are hidden for the current user. + + stories_hidden_min (``bool``, *optional*): + True, if the chat only shows the minimum number of stories. + + stories_unavailable (``bool``, *optional*): + True, if stories from this chat are currently unavailable. + + signature_profiles (``bool``, *optional*): + True, if signature profiles are enabled for this chat. + + autotranslation (``bool``, *optional*): + True, if automatic translation is enabled for this chat. + + broadcast_messages_allowed (``bool``, *optional*): + True, if broadcast messages are allowed in this chat. + + monoforum (``bool``, *optional*): + True, if this chat is a monoforum instance. + + forum_tabs (``bool``, *optional*): + True, if forum tabs are enabled for this chat. + + stories_max_id (``int``, *optional*): + Identifier of the most recent story viewed in this chat. + + color (:obj:`~pyrogram.types.PeerColor`, *optional*): + Accent color information associated with the chat. + + profile_color (:obj:`~pyrogram.types.PeerColor`, *optional*): + Profile color information associated with the chat. + + emoji_status (:obj:`~pyrogram.types.EmojiStatus`, *optional*): + Emoji status attached to the chat. + + access_hash (``int``, *optional*): + Access hash required for MTProto requests involving this chat. + + level (``int``, *optional*): + Current chat level, if available. + + subscription_until_date (:py:obj:`~datetime.datetime`, *optional*): + Expiration date of an active subscription for this chat. + + bot_verification_icon (``int``, *optional*): + Identifier of the bot verification icon associated with this chat. + + send_paid_messages_stars (``int``, *optional*): + Number of Stars required to send paid messages in the chat. + + linked_monoforum_id (``int``, *optional*): + Identifier of the linked monoforum, if any. + + restriction_reason (List of :obj:`~pyrogram.types.Restriction`, *optional*): + Reasons why access to this chat may be restricted. + + wallpaper (:obj:`~pyrogram.types.Wallpaper`, *optional*): + Wallpaper of the chat. + + birthday (:obj:`~pyrogram.types.Birthday`, *optional*): + Birthday of the user. + + business_intro (:obj:`~pyrogram.types.BusinessIntro`, *optional*): + Business introduction. + + business_location (:obj:`~pyrogram.types.BusinessLocation`, *optional*): + Business location. + + business_work_hours (:obj:`~pyrogram.types.BusinessWorkHours`, *optional*): + Business work hours. + + business_greeting_message (:obj:`~pyrogram.types.BusinessGreetingMessage`, *optional*): + Business greeting message. + + business_away_message (:obj:`~pyrogram.types.BusinessAwayMessage`, *optional*): + Business away message. + + personal_chat (:obj:`~pyrogram.types.Chat`, *optional*): + Personal channel of the user. + + stargifts_count (``int``, *optional*): + Number of Star Gifts. + + boosts_applied (``int``, *optional*): + Number of boosts applied. + + unrestrict_boost_count (``int``, *optional*): + Number of boosts required to unrestrict. + + is_contact_require_premium (``bool``, *optional*): + True, if the user requires premium to contact. + full_name (``str``, *property*): Full name of the other party in a private chat, for private chats and bots. """ @@ -182,6 +278,38 @@ def __init__( has_visible_history: bool = None, has_hidden_members: bool = None, is_forum: bool = None, + admin_privileges: "types.ChatPrivileges" = None, + stories_hidden: bool = None, + stories_hidden_min: bool = None, + stories_unavailable: bool = None, + signature_profiles: bool = None, + autotranslation: bool = None, + broadcast_messages_allowed: bool = None, + monoforum: bool = None, + forum_tabs: bool = None, + stories_max_id: int = None, + color: "types.PeerColor" = None, + profile_color: "types.PeerColor" = None, + emoji_status: "types.EmojiStatus" = None, + access_hash: int = None, + level: int = None, + subscription_until_date: datetime = None, + bot_verification_icon: int = None, + send_paid_messages_stars: int = None, + linked_monoforum_id: int = None, + restriction_reason: List["types.Restriction"] = None, + wallpaper: "types.Wallpaper" = None, + birthday: "types.Birthday" = None, + business_intro: "types.BusinessIntro" = None, + business_location: "types.BusinessLocation" = None, + business_work_hours: "types.BusinessWorkHours" = None, + business_greeting_message: "types.BusinessGreetingMessage" = None, + business_away_message: "types.BusinessAwayMessage" = None, + personal_chat: "types.Chat" = None, + stargifts_count: int = None, + boosts_applied: int = None, + unrestrict_boost_count: int = None, + is_contact_require_premium: bool = None, ): super().__init__(client) @@ -217,6 +345,38 @@ def __init__( self.has_visible_history = has_visible_history self.has_hidden_members = has_hidden_members self.is_forum = is_forum + self.admin_privileges = admin_privileges + self.stories_hidden = stories_hidden + self.stories_hidden_min = stories_hidden_min + self.stories_unavailable = stories_unavailable + self.signature_profiles = signature_profiles + self.autotranslation = autotranslation + self.broadcast_messages_allowed = broadcast_messages_allowed + self.monoforum = monoforum + self.forum_tabs = forum_tabs + self.stories_max_id = stories_max_id + self.color = color + self.profile_color = profile_color + self.emoji_status = emoji_status + self.access_hash = access_hash + self.level = level + self.subscription_until_date = subscription_until_date + self.bot_verification_icon = bot_verification_icon + self.send_paid_messages_stars = send_paid_messages_stars + self.linked_monoforum_id = linked_monoforum_id + self.restriction_reason = restriction_reason or restrictions + self.wallpaper = wallpaper + self.birthday = birthday + self.business_intro = business_intro + self.business_location = business_location + self.business_work_hours = business_work_hours + self.business_greeting_message = business_greeting_message + self.business_away_message = business_away_message + self.personal_chat = personal_chat + self.stargifts_count = stargifts_count + self.boosts_applied = boosts_applied + self.unrestrict_boost_count = unrestrict_boost_count + self.is_contact_require_premium = is_contact_require_premium @property def full_name(self) -> str: @@ -226,6 +386,8 @@ def full_name(self) -> str: def _parse_user_chat(client, user: raw.types.User) -> "Chat": peer_id = user.id + parsed_restrictions = types.List([types.Restriction._parse(r) for r in user.restriction_reason]) or None + return Chat( id=peer_id, type=enums.ChatType.BOT if user.bot else enums.ChatType.PRIVATE, @@ -238,9 +400,18 @@ def _parse_user_chat(client, user: raw.types.User) -> "Chat": first_name=user.first_name, last_name=user.last_name, photo=types.ChatPhoto._parse(client, user.photo, peer_id, user.access_hash), - restrictions=types.List([types.Restriction._parse(r) for r in user.restriction_reason]) or None, + restrictions=parsed_restrictions, + restriction_reason=parsed_restrictions, dc_id=getattr(getattr(user, "photo", None), "dc_id", None), usernames=types.List([types.Username._parse(r) for r in user.usernames]) or None, + stories_hidden=getattr(user, "stories_hidden", None), + stories_unavailable=getattr(user, "stories_unavailable", None), + stories_max_id=getattr(user, "stories_max_id", None), + color=types.PeerColor._parse(getattr(user, "color", None)), + profile_color=types.PeerColor._parse(getattr(user, "profile_color", None)), + emoji_status=types.EmojiStatus._parse(client, getattr(user, "emoji_status", None)), + access_hash=getattr(user, "access_hash", None), + is_contact_require_premium=getattr(user, "contact_require_premium", None), client=client ) @@ -249,6 +420,8 @@ def _parse_chat_chat(client, chat: raw.types.Chat) -> "Chat": peer_id = -chat.id usernames = getattr(chat, "usernames", []) + parsed_restrictions = types.List([types.Restriction._parse(r) for r in getattr(chat, "restriction_reason", [])]) or None + return Chat( id=peer_id, type=enums.ChatType.GROUP, @@ -260,6 +433,21 @@ def _parse_chat_chat(client, chat: raw.types.Chat) -> "Chat": dc_id=getattr(getattr(chat, "photo", None), "dc_id", None), has_protected_content=getattr(chat, "noforwards", None), usernames=types.List([types.Username._parse(r) for r in usernames]) or None, + admin_privileges=types.ChatPrivileges._parse(getattr(chat, "admin_rights", None)), + stories_hidden=getattr(chat, "stories_hidden", None), + stories_hidden_min=getattr(chat, "stories_hidden_min", None), + stories_unavailable=getattr(chat, "stories_unavailable", None), + signature_profiles=getattr(chat, "signature_profiles", None), + autotranslation=getattr(chat, "autotranslation", None), + broadcast_messages_allowed=getattr(chat, "broadcast_messages_allowed", None), + monoforum=getattr(chat, "monoforum", None), + forum_tabs=getattr(chat, "forum_tabs", None), + stories_max_id=getattr(chat, "stories_max_id", None), + color=types.PeerColor._parse(getattr(chat, "color", None)), + profile_color=types.PeerColor._parse(getattr(chat, "profile_color", None)), + emoji_status=types.EmojiStatus._parse(client, getattr(chat, "emoji_status", None)), + access_hash=getattr(chat, "access_hash", None), + restriction_reason=parsed_restrictions, client=client ) @@ -269,6 +457,8 @@ def _parse_channel_chat(client, channel: raw.types.Channel) -> "Chat": restriction_reason = getattr(channel, "restriction_reason", []) usernames = getattr(channel, "usernames", []) + parsed_restrictions = types.List([types.Restriction._parse(r) for r in restriction_reason]) or None + return Chat( id=peer_id, type=enums.ChatType.SUPERGROUP if getattr(channel, "megagroup", None) else enums.ChatType.CHANNEL, @@ -281,14 +471,34 @@ def _parse_channel_chat(client, channel: raw.types.Channel) -> "Chat": username=getattr(channel, "username", None), photo=types.ChatPhoto._parse(client, getattr(channel, "photo", None), peer_id, getattr(channel, "access_hash", 0)), - restrictions=types.List([types.Restriction._parse(r) for r in restriction_reason]) or None, + restrictions=parsed_restrictions, + restriction_reason=parsed_restrictions, permissions=types.ChatPermissions._parse(getattr(channel, "default_banned_rights", None)), members_count=getattr(channel, "participants_count", None), dc_id=getattr(getattr(channel, "photo", None), "dc_id", None), has_protected_content=getattr(channel, "noforwards", None), usernames=types.List([types.Username._parse(r) for r in usernames]) or None, + admin_privileges=types.ChatPrivileges._parse(getattr(channel, "admin_rights", None)), client=client, is_forum=getattr(channel, "forum", None), + stories_hidden=getattr(channel, "stories_hidden", None), + stories_hidden_min=getattr(channel, "stories_hidden_min", None), + stories_unavailable=getattr(channel, "stories_unavailable", None), + signature_profiles=getattr(channel, "signature_profiles", None), + autotranslation=getattr(channel, "autotranslation", None), + broadcast_messages_allowed=getattr(channel, "broadcast_messages_allowed", None), + monoforum=getattr(channel, "monoforum", None), + forum_tabs=getattr(channel, "forum_tabs", None), + stories_max_id=getattr(channel, "stories_max_id", None), + color=types.PeerColor._parse(getattr(channel, "color", None)), + profile_color=types.PeerColor._parse(getattr(channel, "profile_color", None)), + emoji_status=types.EmojiStatus._parse(client, getattr(channel, "emoji_status", None)), + access_hash=getattr(channel, "access_hash", None), + level=getattr(channel, "level", None), + subscription_until_date=utils.timestamp_to_datetime(getattr(channel, "subscription_until_date", None)), + bot_verification_icon=getattr(channel, "bot_verification_icon", None), + send_paid_messages_stars=getattr(channel, "send_paid_messages_stars", None), + linked_monoforum_id=getattr(channel, "linked_monoforum_id", None) ) @staticmethod @@ -330,6 +540,19 @@ async def _parse_full(client, chat_full: Union[raw.types.messages.ChatFull, raw. parsed_chat = Chat._parse_user_chat(client, users[full_user.id]) parsed_chat.bio = full_user.about + parsed_chat.wallpaper = types.Wallpaper._parse(client, getattr(full_user, "wallpaper", None)) + parsed_chat.birthday = types.Birthday._parse(client, getattr(full_user, "birthday", None)) + parsed_chat.business_intro = types.BusinessIntro._parse(client, getattr(full_user, "business_intro", None)) + parsed_chat.business_location = types.BusinessLocation._parse(client, getattr(full_user, "business_location", None)) + parsed_chat.business_work_hours = types.BusinessWorkHours._parse(client, getattr(full_user, "business_work_hours", None)) + parsed_chat.business_greeting_message = types.BusinessGreetingMessage._parse(client, getattr(full_user, "business_greeting_message", None)) + parsed_chat.business_away_message = types.BusinessAwayMessage._parse(client, getattr(full_user, "business_away_message", None)) + parsed_chat.stargifts_count = getattr(full_user, "stargifts_count", None) + + if getattr(full_user, "personal_channel_id", None): + personal_channel = chats.get(full_user.personal_channel_id) + if personal_channel: + parsed_chat.personal_chat = Chat._parse_channel_chat(client, personal_channel) if full_user.pinned_msg_id: parsed_chat.pinned_message = await client.get_messages( @@ -357,6 +580,44 @@ async def _parse_full(client, chat_full: Union[raw.types.messages.ChatFull, raw. # TODO: Add StickerSet type parsed_chat.can_set_sticker_set = full_chat.can_set_stickers parsed_chat.sticker_set_name = getattr(full_chat.stickerset, "short_name", None) + if getattr(full_chat, "admin_rights", None): + parsed_chat.admin_privileges = types.ChatPrivileges._parse(full_chat.admin_rights) + parsed_chat.stories_hidden = getattr(full_chat, "stories_hidden", parsed_chat.stories_hidden) + parsed_chat.stories_hidden_min = getattr(full_chat, "stories_hidden_min", parsed_chat.stories_hidden_min) + parsed_chat.stories_unavailable = getattr(full_chat, "stories_unavailable", parsed_chat.stories_unavailable) + parsed_chat.signature_profiles = getattr(full_chat, "signature_profiles", parsed_chat.signature_profiles) + parsed_chat.autotranslation = getattr(full_chat, "autotranslation", parsed_chat.autotranslation) + parsed_chat.broadcast_messages_allowed = getattr(full_chat, "broadcast_messages_allowed", parsed_chat.broadcast_messages_allowed) + parsed_chat.monoforum = getattr(full_chat, "monoforum", parsed_chat.monoforum) + parsed_chat.forum_tabs = getattr(full_chat, "forum_tabs", parsed_chat.forum_tabs) + parsed_chat.stories_max_id = getattr(full_chat, "stories_max_id", parsed_chat.stories_max_id) + parsed_chat.color = types.PeerColor._parse(getattr(full_chat, "color", None)) or parsed_chat.color + parsed_chat.profile_color = types.PeerColor._parse(getattr(full_chat, "profile_color", None)) or parsed_chat.profile_color + parsed_chat.emoji_status = types.EmojiStatus._parse(client, getattr(full_chat, "emoji_status", None)) or parsed_chat.emoji_status + parsed_chat.level = getattr(full_chat, "level", parsed_chat.level) + parsed_chat.subscription_until_date = utils.timestamp_to_datetime(getattr(full_chat, "subscription_until_date", None)) or parsed_chat.subscription_until_date + parsed_chat.bot_verification_icon = getattr(full_chat, "bot_verification_icon", parsed_chat.bot_verification_icon) + parsed_chat.send_paid_messages_stars = getattr(full_chat, "send_paid_messages_stars", parsed_chat.send_paid_messages_stars) + parsed_chat.linked_monoforum_id = getattr(full_chat, "linked_monoforum_id", parsed_chat.linked_monoforum_id) + parsed_chat.wallpaper = types.Wallpaper._parse(client, getattr(full_chat, "wallpaper", None)) + parsed_chat.boosts_applied = getattr(full_chat, "boosts_applied", parsed_chat.boosts_applied) + parsed_chat.unrestrict_boost_count = getattr(full_chat, "boosts_unrestrict", parsed_chat.unrestrict_boost_count) + parsed_chat.stargifts_count = getattr(full_chat, "stargifts_count", parsed_chat.stargifts_count) + if getattr(full_chat, "default_banned_rights", None): + parsed_chat.permissions = types.ChatPermissions._parse(full_chat.default_banned_rights) + if getattr(full_chat, "banned_rights", None): + parsed_chat.permissions = types.ChatPermissions._parse(full_chat.banned_rights) + if getattr(full_chat, "admin_rights", None): + parsed_chat.admin_privileges = types.ChatPrivileges._parse(full_chat.admin_rights) + + full_restrictions = types.List([ + types.Restriction._parse(r) + for r in getattr(full_chat, "restriction_reason", []) + ]) or None + + if full_restrictions: + parsed_chat.restrictions = full_restrictions + parsed_chat.restriction_reason = full_restrictions linked_chat_raw = chats.get(full_chat.linked_chat_id, None) diff --git a/pyrogram/types/user_and_chats/wallpaper.py b/pyrogram/types/user_and_chats/wallpaper.py new file mode 100644 index 00000000..73f784b9 --- /dev/null +++ b/pyrogram/types/user_and_chats/wallpaper.py @@ -0,0 +1,183 @@ +# 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 + +import pyrogram +from pyrogram import raw, types +from ..object import Object + + +class Wallpaper(Object): + """Represents a wallpaper. + + Parameters: + id (``int``): + Unique wallpaper identifier. + + access_hash (``int``): + Access hash. + + slug (``str``): + Wallpaper slug/identifier. + + document (:obj:`~pyrogram.types.Document`, *optional*): + The wallpaper document. + + settings (:obj:`~pyrogram.types.WallpaperSettings`, *optional*): + Wallpaper settings. + + is_creator (``bool``, *optional*): + Whether the current user created this wallpaper. + + is_default (``bool``, *optional*): + Whether this is the default wallpaper. + + is_pattern (``bool``, *optional*): + Whether this is a pattern wallpaper. + + is_dark (``bool``, *optional*): + Whether this wallpaper is for dark mode. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + id: int = None, + access_hash: int = None, + slug: str = None, + document: "types.Document" = None, + settings: "types.WallpaperSettings" = None, + is_creator: bool = None, + is_default: bool = None, + is_pattern: bool = None, + is_dark: bool = None + ): + super().__init__(client) + + self.id = id + self.access_hash = access_hash + self.slug = slug + self.document = document + self.settings = settings + self.is_creator = is_creator + self.is_default = is_default + self.is_pattern = is_pattern + self.is_dark = is_dark + + @staticmethod + async def _parse( + client: "pyrogram.Client", + wallpaper: "raw.types.WallPaper" + ) -> "Wallpaper": + document = None + if hasattr(wallpaper, 'document') and wallpaper.document: + document = await types.Document._parse(client, wallpaper.document, "wallpaper.jpg") + + settings = None + if hasattr(wallpaper, 'settings') and wallpaper.settings: + settings = types.WallpaperSettings._parse(wallpaper.settings) + + return Wallpaper( + client=client, + id=wallpaper.id, + access_hash=wallpaper.access_hash if hasattr(wallpaper, 'access_hash') else None, + slug=wallpaper.slug, + document=document, + settings=settings, + is_creator=wallpaper.creator if hasattr(wallpaper, 'creator') else None, + is_default=wallpaper.default if hasattr(wallpaper, 'default') else None, + is_pattern=wallpaper.pattern if hasattr(wallpaper, 'pattern') else None, + is_dark=wallpaper.dark if hasattr(wallpaper, 'dark') else None + ) + + +class WallpaperSettings(Object): + """Represents wallpaper settings. + + Parameters: + background_color (``int``, *optional*): + Background color in RGB format. + + second_background_color (``int``, *optional*): + Second background color for gradients. + + third_background_color (``int``, *optional*): + Third background color. + + fourth_background_color (``int``, *optional*): + Fourth background color. + + intensity (``int``, *optional*): + Pattern intensity (0-100). + + rotation (``int``, *optional*): + Gradient rotation angle. + + emoticon (``str``, *optional*): + Emoticon-based wallpaper. + + is_blur (``bool``, *optional*): + Whether blur is applied. + + is_motion (``bool``, *optional*): + Whether motion effects are enabled. + """ + + def __init__( + self, + *, + background_color: int = None, + second_background_color: int = None, + third_background_color: int = None, + fourth_background_color: int = None, + intensity: int = None, + rotation: int = None, + emoticon: str = None, + is_blur: bool = None, + is_motion: bool = None + ): + super().__init__() + + self.background_color = background_color + self.second_background_color = second_background_color + self.third_background_color = third_background_color + self.fourth_background_color = fourth_background_color + self.intensity = intensity + self.rotation = rotation + self.emoticon = emoticon + self.is_blur = is_blur + self.is_motion = is_motion + + @staticmethod + def _parse(settings: "raw.types.WallPaperSettings") -> Optional["WallpaperSettings"]: + if settings is None: + return None + + return WallpaperSettings( + background_color=settings.background_color if hasattr(settings, 'background_color') else None, + second_background_color=settings.second_background_color if hasattr(settings, 'second_background_color') else None, + third_background_color=settings.third_background_color if hasattr(settings, 'third_background_color') else None, + fourth_background_color=settings.fourth_background_color if hasattr(settings, 'fourth_background_color') else None, + intensity=settings.intensity if hasattr(settings, 'intensity') else None, + rotation=settings.rotation if hasattr(settings, 'rotation') else None, + emoticon=settings.emoticon if hasattr(settings, 'emoticon') else None, + is_blur=settings.blur if hasattr(settings, 'blur') else None, + is_motion=settings.motion if hasattr(settings, 'motion') else None + ) diff --git a/pyrogram/utils.py b/pyrogram/utils.py index 044e935c..3954857c 100644 --- a/pyrogram/utils.py +++ b/pyrogram/utils.py @@ -37,7 +37,7 @@ async def ainput(prompt: str = "", *, hide: bool = False): """Just like the built-in input, but async""" with ThreadPoolExecutor(1) as executor: func = functools.partial(getpass if hide else input, prompt) - return await asyncio.get_event_loop().run_in_executor(executor, func) + return await asyncio.get_running_loop().run_in_executor(executor, func) def get_input_media_from_file_id( From 72eebfa13f540c2fc344781e55986bc266a5f21b Mon Sep 17 00:00:00 2001 From: Pyrogram-Mod - Dev <> Date: Fri, 30 Jan 2026 10:48:39 +0100 Subject: [PATCH 06/10] fix: restore missing types, fix Python 3.14 event loop, and update exports - Restored missing implementation files for PeerColor, StickerSet, PremiumGiftCodeOption, StarRefProgram, GroupCallMessage, and GroupCallStreamChannel. - Updated pyrogram/sync.py with event loop handling for Python 3.14. - Fixed missing exports in pyrogram/types/ package and subpackages. - Updated compiler templates and filters documentation. --- compiler/api/template/type.txt | 4 +- compiler/docs/template/bound-methods.rst | 52 +++++++ compiler/docs/template/types.rst | 78 ++++++++++ docs/source/topics/use-filters.rst | 8 + pyrogram/filters.py | 13 ++ pyrogram/raw/core/tl_object.py | 6 +- pyrogram/sync.py | 26 +++- pyrogram/types/__init__.py | 1 + pyrogram/types/calls/__init__.py | 7 +- pyrogram/types/calls/group_call_message.py | 87 +++++++++++ .../types/calls/group_call_stream_channel.py | 62 ++++++++ pyrogram/types/messages_and_media/__init__.py | 3 +- .../types/messages_and_media/sticker_set.py | 137 ++++++++++++++++++ pyrogram/types/payments/__init__.py | 6 +- .../payments/premium_gift_code_option.py | 80 ++++++++++ pyrogram/types/payments/star_ref_program.py | 56 +++++++ pyrogram/types/user_and_chats/chat.py | 4 + pyrogram/types/user_and_chats/peer_color.py | 100 +++++++++++++ 18 files changed, 720 insertions(+), 10 deletions(-) create mode 100644 pyrogram/types/calls/group_call_message.py create mode 100644 pyrogram/types/calls/group_call_stream_channel.py create mode 100644 pyrogram/types/messages_and_media/sticker_set.py create mode 100644 pyrogram/types/payments/premium_gift_code_option.py create mode 100644 pyrogram/types/payments/star_ref_program.py create mode 100644 pyrogram/types/user_and_chats/peer_color.py diff --git a/compiler/api/template/type.txt b/compiler/api/template/type.txt index 2480f5ba..2f5343d3 100644 --- a/compiler/api/template/type.txt +++ b/compiler/api/template/type.txt @@ -6,6 +6,4 @@ from typing import Union from pyrogram import raw {name} = Union[{types}] -{name}.__doc__ = """ - {docstring} -""" \ No newline at end of file +"""{docstring}""" \ No newline at end of file diff --git a/compiler/docs/template/bound-methods.rst b/compiler/docs/template/bound-methods.rst index 1e16e32c..72005dd1 100644 --- a/compiler/docs/template/bound-methods.rst +++ b/compiler/docs/template/bound-methods.rst @@ -101,3 +101,55 @@ ChatJoinRequest {chat_join_request_toctree} +StoryItem +--------- + +.. hlist:: + :columns: 3 + + {story_item_hlist} + +.. toctree:: + :hidden: + + {story_item_toctree} + +QuickReply +---------- + +.. hlist:: + :columns: 2 + + {quick_reply_hlist} + +.. toctree:: + :hidden: + + {quick_reply_toctree} + +BusinessChatLink +---------------- + +.. hlist:: + :columns: 2 + + {business_chat_link_hlist} + +.. toctree:: + :hidden: + + {business_chat_link_toctree} + +SavedDialog +----------- + +.. hlist:: + :columns: 2 + + {saved_dialog_hlist} + +.. toctree:: + :hidden: + + {saved_dialog_toctree} + diff --git a/compiler/docs/template/types.rst b/compiler/docs/template/types.rst index e404ad4c..fb21aae7 100644 --- a/compiler/docs/template/types.rst +++ b/compiler/docs/template/types.rst @@ -120,6 +120,84 @@ Authorization {authorization} +Stories +------- + +.. autosummary:: + :nosignatures: + + {stories} + +.. toctree:: + :hidden: + + {stories} + +Payments +-------- + +.. autosummary:: + :nosignatures: + + {payments} + +.. toctree:: + :hidden: + + {payments} + +Star Gifts +---------- + +.. autosummary:: + :nosignatures: + + {star_gifts} + +.. toctree:: + :hidden: + + {star_gifts} + +Business +-------- + +.. autosummary:: + :nosignatures: + + {business} + +.. toctree:: + :hidden: + + {business} + +Boosts +------ + +.. autosummary:: + :nosignatures: + + {boosts} + +.. toctree:: + :hidden: + + {boosts} + +Saved Messages +-------------- + +.. autosummary:: + :nosignatures: + + {saved_messages} + +.. toctree:: + :hidden: + + {saved_messages} + .. toctree:: :hidden: diff --git a/docs/source/topics/use-filters.rst b/docs/source/topics/use-filters.rst index 0cc38fcb..695e6ba8 100644 --- a/docs/source/topics/use-filters.rst +++ b/docs/source/topics/use-filters.rst @@ -69,6 +69,14 @@ Here are some examples: async def my_handler(client, message): print(message) +- Message is a **story** **or** contains **paid media**. + + .. code-block:: python + + @app.on_message(filters.story | filters.paid_media) + async def my_handler(client, message): + print(message) + Advanced Filters ---------------- diff --git a/pyrogram/filters.py b/pyrogram/filters.py index 21d19e30..6b66caa8 100644 --- a/pyrogram/filters.py +++ b/pyrogram/filters.py @@ -382,6 +382,19 @@ async def business_filter(_, __, m: Message): """Filter messages related to Telegram Business (sent via business bot or quick reply).""" +# endregion + + +# region boost_filter +async def boost_filter(_, __, update: Update): + from pyrogram.types import ChatBoostUpdated + return isinstance(update, ChatBoostUpdated) + + +boost = create(boost_filter) +"""Filter chat boost updates.""" + + # endregion # region video_filter diff --git a/pyrogram/raw/core/tl_object.py b/pyrogram/raw/core/tl_object.py index ff67566e..9306fad0 100644 --- a/pyrogram/raw/core/tl_object.py +++ b/pyrogram/raw/core/tl_object.py @@ -18,12 +18,14 @@ from io import BytesIO from json import dumps -from typing import cast, List, Any, Union, Dict +from typing import cast, List, Any, Union, Dict, Generic, TypeVar from ..all import objects +T = TypeVar("T") -class TLObject: + +class TLObject(Generic[T]): __slots__: List[str] = [] QUALNAME = "Base" diff --git a/pyrogram/sync.py b/pyrogram/sync.py index 54c53610..47df5db9 100644 --- a/pyrogram/sync.py +++ b/pyrogram/sync.py @@ -26,9 +26,27 @@ from pyrogram.methods.utilities import idle as idle_module, compose as compose_module +def _ensure_event_loop(): + """ + Python 3.14 no longer creates an implicit event loop for the main thread. + Reproduce the old behaviour by instantiating and setting one on-demand. + """ + try: + return asyncio.get_running_loop() + except RuntimeError: + pass + + try: + return asyncio.get_event_loop() + except RuntimeError: + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + return loop + + def async_to_sync(obj, name): function = getattr(obj, name) - main_loop = asyncio.get_event_loop() + main_loop = _ensure_event_loop() def async_to_sync_gen(agen, loop, is_main_thread): async def anext(agen): @@ -52,7 +70,11 @@ async def anext(agen): def async_to_sync_wrap(*args, **kwargs): coroutine = function(*args, **kwargs) - loop = _ensure_event_loop() + try: + loop = asyncio.get_event_loop() + except RuntimeError: + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) if threading.current_thread() is threading.main_thread() or not main_loop.is_running(): if loop.is_running(): diff --git a/pyrogram/types/__init__.py b/pyrogram/types/__init__.py index fd130c1a..44db9d70 100644 --- a/pyrogram/types/__init__.py +++ b/pyrogram/types/__init__.py @@ -28,6 +28,7 @@ from .messages_and_media import * from .object import Object from .payments import * +from .saved_messages import * from .stats import * from .stories import * from .todo import * diff --git a/pyrogram/types/calls/__init__.py b/pyrogram/types/calls/__init__.py index 486e0bc9..315e1f92 100644 --- a/pyrogram/types/calls/__init__.py +++ b/pyrogram/types/calls/__init__.py @@ -19,9 +19,14 @@ from .group_call import GroupCall from .group_call_participant import GroupCallParticipant from .conference_call import ConferenceCall +from .group_call_message import GroupCallMessage +from .group_call_stream_channel import GroupCallStreamChannel + __all__ = [ "GroupCall", "GroupCallParticipant", - "ConferenceCall" + "ConferenceCall", + "GroupCallMessage", + "GroupCallStreamChannel" ] diff --git a/pyrogram/types/calls/group_call_message.py b/pyrogram/types/calls/group_call_message.py new file mode 100644 index 00000000..bee32f06 --- /dev/null +++ b/pyrogram/types/calls/group_call_message.py @@ -0,0 +1,87 @@ +# 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 + +import pyrogram +from pyrogram import raw, types, utils +from ..object import Object + + +class GroupCallMessage(Object): + """A message in a group call. + + Parameters: + id (``int``): + Message identifier. + + from_id (:obj:`~pyrogram.types.Chat` | :obj:`~pyrogram.types.User`): + Sender of the message. + + date (``datetime``): + Date the message was sent. + + text (``str``): + Message text. + + is_from_admin (``bool``, *optional*): + True, if the message was sent by an admin. + + paid_message_stars (``int``, *optional*): + Price of the message in Telegram Stars. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + id: int, + from_id: "types.Chat" = None, + date: datetime, + text: str, + is_from_admin: bool = None, + paid_message_stars: int = None + ): + super().__init__(client) + + self.id = id + self.from_id = from_id + self.date = date + self.text = text + self.is_from_admin = is_from_admin + self.paid_message_stars = paid_message_stars + + @staticmethod + def _parse( + client: "pyrogram.Client", + message: "raw.types.GroupCallMessage", + users: dict = None, + chats: dict = None + ) -> "GroupCallMessage": + if not message: + return None + + return GroupCallMessage( + client=client, + id=message.id, + from_id=types.Chat._parse(client, message.from_id, users, chats), + date=utils.timestamp_to_datetime(message.date), + text=message.message.text if hasattr(message.message, "text") else message.message, + is_from_admin=getattr(message, "from_admin", None), + paid_message_stars=getattr(message, "paid_message_stars", None) + ) diff --git a/pyrogram/types/calls/group_call_stream_channel.py b/pyrogram/types/calls/group_call_stream_channel.py new file mode 100644 index 00000000..d5dc99c4 --- /dev/null +++ b/pyrogram/types/calls/group_call_stream_channel.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 . + +import pyrogram +from pyrogram import raw +from ..object import Object + + +class GroupCallStreamChannel(Object): + """A group call stream channel. + + Parameters: + channel (``int``): + Channel identifier. + + scale (``int``): + Channel scale. + + last_timestamp_ms (``int``): + Last timestamp in milliseconds. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + channel: int, + scale: int, + last_timestamp_ms: int + ): + super().__init__(client) + + self.channel = channel + self.scale = scale + self.last_timestamp_ms = last_timestamp_ms + + @staticmethod + def _parse(client: "pyrogram.Client", channel: "raw.types.GroupCallStreamChannel") -> "GroupCallStreamChannel": + if not channel: + return None + + return GroupCallStreamChannel( + client=client, + channel=channel.channel, + scale=channel.scale, + last_timestamp_ms=channel.last_timestamp_ms + ) diff --git a/pyrogram/types/messages_and_media/__init__.py b/pyrogram/types/messages_and_media/__init__.py index e8332dbc..a2d9bebe 100644 --- a/pyrogram/types/messages_and_media/__init__.py +++ b/pyrogram/types/messages_and_media/__init__.py @@ -35,6 +35,7 @@ from .poll_option import PollOption from .reaction import Reaction from .sticker import Sticker +from .sticker_set import StickerSet from .story import Story from .stripped_thumbnail import StrippedThumbnail from .suggested_post import SuggestedPost @@ -55,5 +56,5 @@ "MessageEntity", "Photo", "Thumbnail", "StrippedThumbnail", "Poll", "PollOption", "Sticker", "SuggestedPost", "Venue", "Video", "VideoNote", "Voice", "WebPage", "Dice", "Reaction", "WebAppData", "MessageReactions", "Story", "Giveaway", "AlternativeVideo", "StarGift", "StarGiftUnique", "StarGiftAttribute", "StarGiftAttributeRarity", - "PaidMedia" + "PaidMedia", "StickerSet" ] diff --git a/pyrogram/types/messages_and_media/sticker_set.py b/pyrogram/types/messages_and_media/sticker_set.py new file mode 100644 index 00000000..007f86c8 --- /dev/null +++ b/pyrogram/types/messages_and_media/sticker_set.py @@ -0,0 +1,137 @@ +# 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 List, Optional + +import pyrogram +from pyrogram import raw, types, utils +from ..object import Object + + +class StickerSet(Object): + """A sticker set. + + Parameters: + id (``int``): + Sticker set identifier. + + access_hash (``int``): + Sticker set access hash. + + title (``str``): + Sticker set title. + + short_name (``str``): + Sticker set short name. + + count (``int``): + Number of stickers in this set. + + hash (``int``): + Sticker set hash. + + is_archived (``bool``, *optional*): + True, if this sticker set is archived. + + is_official (``bool``, *optional*): + True, if this sticker set is official. + + is_masks (``bool``, *optional*): + True, if this sticker set contains masks. + + is_emojis (``bool``, *optional*): + True, if this sticker set contains emojis. + + is_text_color (``bool``, *optional*): + True, if this sticker set contains custom text color emojis. + + is_channel_emoji_status (``bool``, *optional*): + True, if this sticker set contains custom channel status emojis. + + is_creator (``bool``, *optional*): + True, if you created this sticker set. + + installed_date (``datetime``, *optional*): + Date when the sticker set was installed. + + thumbs (List of :obj:`~pyrogram.types.Thumbnail`, *optional*): + Sticker set thumbnails. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + id: int, + access_hash: int, + title: str, + short_name: str, + count: int, + hash: int, + is_archived: bool = None, + is_official: bool = None, + is_masks: bool = None, + is_emojis: bool = None, + is_text_color: bool = None, + is_channel_emoji_status: bool = None, + is_creator: bool = None, + installed_date: datetime = None, + thumbs: List["types.Thumbnail"] = None + ): + super().__init__(client) + + self.id = id + self.access_hash = access_hash + self.title = title + self.short_name = short_name + self.count = count + self.hash = hash + self.is_archived = is_archived + self.is_official = is_official + self.is_masks = is_masks + self.is_emojis = is_emojis + self.is_text_color = is_text_color + self.is_channel_emoji_status = is_channel_emoji_status + self.is_creator = is_creator + self.installed_date = installed_date + self.thumbs = thumbs + + @staticmethod + def _parse(client: "pyrogram.Client", sticker_set: "raw.types.StickerSet") -> "StickerSet": + if not sticker_set: + return None + + return StickerSet( + client=client, + id=sticker_set.id, + access_hash=sticker_set.access_hash, + title=sticker_set.title, + short_name=sticker_set.short_name, + count=sticker_set.count, + hash=sticker_set.hash, + is_archived=getattr(sticker_set, "archived", None), + is_official=getattr(sticker_set, "official", None), + is_masks=getattr(sticker_set, "masks", None), + is_emojis=getattr(sticker_set, "emojis", None), + is_text_color=getattr(sticker_set, "text_color", None), + is_channel_emoji_status=getattr(sticker_set, "channel_emoji_status", None), + is_creator=getattr(sticker_set, "creator", None), + installed_date=utils.timestamp_to_datetime(getattr(sticker_set, "installed_date", None)), + thumbs=types.Thumbnail._parse(client, sticker_set) + ) diff --git a/pyrogram/types/payments/__init__.py b/pyrogram/types/payments/__init__.py index cd3e5afd..63f00c12 100644 --- a/pyrogram/types/payments/__init__.py +++ b/pyrogram/types/payments/__init__.py @@ -24,6 +24,8 @@ from .stars_topup_option import StarsTopupOption from .stars_gift_option import StarsGiftOption from .paid_reaction_privacy import PaidReactionPrivacy +from .premium_gift_code_option import PremiumGiftCodeOption +from .star_ref_program import StarRefProgram from .star_gift_auction_round import StarGiftAuctionRound from .star_gift_auction_user_state import StarGiftAuctionUserState from .star_gift_auction_state import StarGiftAuctionState @@ -43,5 +45,7 @@ "StarGiftAuctionRound", "StarGiftAuctionUserState", "StarGiftAuctionState", - "AuctionBidLevel" + "AuctionBidLevel", + "PremiumGiftCodeOption", + "StarRefProgram" ] diff --git a/pyrogram/types/payments/premium_gift_code_option.py b/pyrogram/types/payments/premium_gift_code_option.py new file mode 100644 index 00000000..0b099208 --- /dev/null +++ b/pyrogram/types/payments/premium_gift_code_option.py @@ -0,0 +1,80 @@ +# 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 PremiumGiftCodeOption(Object): + """A premium gift code option. + + Parameters: + users (``int``): + Number of users this option is for. + + months (``int``): + Duration of the subscription in months. + + currency (``str``): + Three-letter ISO 4217 currency code. + + amount (``int``): + Price in the smallest units of the currency. + + store_product (``str``, *optional*): + Identifier of the store product. + + store_quantity (``int``, *optional*): + Store quantity. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + users: int, + months: int, + currency: str, + amount: int, + store_product: str = None, + store_quantity: int = None + ): + super().__init__(client) + + self.users = users + self.months = months + self.currency = currency + self.amount = amount + self.store_product = store_product + self.store_quantity = store_quantity + + @staticmethod + def _parse(client: "pyrogram.Client", option: "raw.types.PremiumGiftCodeOption") -> "PremiumGiftCodeOption": + if not option: + return None + + return PremiumGiftCodeOption( + client=client, + users=option.users, + months=option.months, + currency=option.currency, + amount=option.amount, + store_product=getattr(option, "store_product", None), + store_quantity=getattr(option, "store_quantity", None) + ) diff --git a/pyrogram/types/payments/star_ref_program.py b/pyrogram/types/payments/star_ref_program.py new file mode 100644 index 00000000..c640b3ef --- /dev/null +++ b/pyrogram/types/payments/star_ref_program.py @@ -0,0 +1,56 @@ +# 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 StarRefProgram(Object): + """A Telegram Stars affiliate program. + + Parameters: + commission_per_mille (``int``): + Commission in per mille (1/1000). + + duration_months (``int``, *optional*): + Duration of the program in months. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + commission_per_mille: int, + duration_months: int = None + ): + super().__init__(client) + + self.commission_per_mille = commission_per_mille + self.duration_months = duration_months + + @staticmethod + def _parse(client: "pyrogram.Client", program: "raw.types.StarRefProgram") -> "StarRefProgram": + if not program: + return None + + return StarRefProgram( + client=client, + commission_per_mille=program.commission_per_mille, + duration_months=getattr(program, "duration_months", None) + ) diff --git a/pyrogram/types/user_and_chats/chat.py b/pyrogram/types/user_and_chats/chat.py index e19c5800..8967ec2a 100644 --- a/pyrogram/types/user_and_chats/chat.py +++ b/pyrogram/types/user_and_chats/chat.py @@ -548,6 +548,9 @@ async def _parse_full(client, chat_full: Union[raw.types.messages.ChatFull, raw. parsed_chat.business_greeting_message = types.BusinessGreetingMessage._parse(client, getattr(full_user, "business_greeting_message", None)) parsed_chat.business_away_message = types.BusinessAwayMessage._parse(client, getattr(full_user, "business_away_message", None)) parsed_chat.stargifts_count = getattr(full_user, "stargifts_count", None) + parsed_chat.starref_program = types.StarRefProgram._parse(client, getattr(full_user, "starref_program", None)) + parsed_chat.send_paid_messages_stars = getattr(full_user, "send_paid_messages_stars", None) + parsed_chat.disallowed_gifts = types.DisallowedGiftsSettings._parse(client, getattr(full_user, "disallowed_gifts", None)) if getattr(full_user, "personal_channel_id", None): personal_channel = chats.get(full_user.personal_channel_id) @@ -603,6 +606,7 @@ async def _parse_full(client, chat_full: Union[raw.types.messages.ChatFull, raw. parsed_chat.boosts_applied = getattr(full_chat, "boosts_applied", parsed_chat.boosts_applied) parsed_chat.unrestrict_boost_count = getattr(full_chat, "boosts_unrestrict", parsed_chat.unrestrict_boost_count) parsed_chat.stargifts_count = getattr(full_chat, "stargifts_count", parsed_chat.stargifts_count) + parsed_chat.emojiset = types.StickerSet._parse(client, getattr(full_chat, "emojiset", None)) if getattr(full_chat, "default_banned_rights", None): parsed_chat.permissions = types.ChatPermissions._parse(full_chat.default_banned_rights) if getattr(full_chat, "banned_rights", None): diff --git a/pyrogram/types/user_and_chats/peer_color.py b/pyrogram/types/user_and_chats/peer_color.py new file mode 100644 index 00000000..e4a45a48 --- /dev/null +++ b/pyrogram/types/user_and_chats/peer_color.py @@ -0,0 +1,100 @@ +# 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 + +from pyrogram import raw +from ..object import Object + + +class PeerColor(Object): + """Represents the accent color information of a peer. + + Parameters: + color (``int``, *optional*): + Identifier of the color palette. + + background_emoji_id (``int``, *optional*): + Custom emoji identifier used for the background pattern. + + collectible_id (``int``, *optional*): + Collectible identifier when the color comes from a collectible. + + gift_emoji_id (``int``, *optional*): + Emoji identifier of the collectible gift. + + accent_color (``int``, *optional*): + Accent color value for collectible colors. + + colors (List of ``int``, *optional*): + List of palette colors for collectible colors. + + dark_accent_color (``int``, *optional*): + Accent color for dark mode. + + dark_colors (List of ``int``, *optional*): + Dark palette colors. + """ + + def __init__( + self, + *, + client=None, + color: Optional[int] = None, + background_emoji_id: Optional[int] = None, + collectible_id: Optional[int] = None, + gift_emoji_id: Optional[int] = None, + accent_color: Optional[int] = None, + colors: Optional[List[int]] = None, + dark_accent_color: Optional[int] = None, + dark_colors: Optional[List[int]] = None + ): + super().__init__(client) + + self.color = color + self.background_emoji_id = background_emoji_id + self.collectible_id = collectible_id + self.gift_emoji_id = gift_emoji_id + self.accent_color = accent_color + self.colors = colors + self.dark_accent_color = dark_accent_color + self.dark_colors = dark_colors + + @staticmethod + def _parse(peer_color: "raw.base.PeerColor") -> Optional["PeerColor"]: + if peer_color is None: + return None + + if isinstance(peer_color, raw.types.PeerColor): + return PeerColor( + color=getattr(peer_color, "color", None), + background_emoji_id=getattr(peer_color, "background_emoji_id", None) + ) + + if isinstance(peer_color, raw.types.PeerColorCollectible): + return PeerColor( + collectible_id=peer_color.collectible_id, + gift_emoji_id=peer_color.gift_emoji_id, + background_emoji_id=peer_color.background_emoji_id, + accent_color=peer_color.accent_color, + colors=list(peer_color.colors) if getattr(peer_color, "colors", None) else None, + dark_accent_color=getattr(peer_color, "dark_accent_color", None), + dark_colors=list(peer_color.dark_colors) if getattr(peer_color, "dark_colors", None) else None + ) + + return None From 4a76fcd90dcbe38b89cb1fb6d809c588ebc52ebe Mon Sep 17 00:00:00 2001 From: Pyrogram-Mod - Dev <> Date: Fri, 30 Jan 2026 11:01:46 +0100 Subject: [PATCH 07/10] fix --- pyrogram/types/business/quick_reply.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyrogram/types/business/quick_reply.py b/pyrogram/types/business/quick_reply.py index 4ea502b8..4ca4bae5 100644 --- a/pyrogram/types/business/quick_reply.py +++ b/pyrogram/types/business/quick_reply.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 typing import List, Optional +from typing import List, Optional, Union import pyrogram from pyrogram import raw, types From b9b414a9807232462e606576bf0304b3ab940fad Mon Sep 17 00:00:00 2001 From: Pyrogram-Mod - Dev <> Date: Fri, 30 Jan 2026 11:06:08 +0100 Subject: [PATCH 08/10] upgrade --- .github/workflows/python.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index 135a7584..baa973ca 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -13,7 +13,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, macos-latest] - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] steps: - name: Checkout repository From e4a2e6b7a1a791d871176c80da7cf86e817e609d Mon Sep 17 00:00:00 2001 From: Pyrogram-Mod - Dev <> Date: Fri, 30 Jan 2026 11:15:00 +0100 Subject: [PATCH 09/10] fix... --- pyrogram/types/payments/stars_subscription.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyrogram/types/payments/stars_subscription.py b/pyrogram/types/payments/stars_subscription.py index 2ae22a95..6fef9a5e 100644 --- a/pyrogram/types/payments/stars_subscription.py +++ b/pyrogram/types/payments/stars_subscription.py @@ -107,7 +107,7 @@ def __init__( *, client: "pyrogram.Client" = None, id: str, - peer: types.User = None, + peer: "types.User" = None, until_date: datetime = None, pricing: "StarsSubscriptionPricing" = None, title: str = None, From 01a4c49e3f5bdfc8bea1a66b142582e891b6c0a4 Mon Sep 17 00:00:00 2001 From: Pyrogram-Mod - Dev <> Date: Fri, 30 Jan 2026 11:16:04 +0100 Subject: [PATCH 10/10] fix deprecation warning --- pyrogram/sync.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pyrogram/sync.py b/pyrogram/sync.py index 47df5db9..50456402 100644 --- a/pyrogram/sync.py +++ b/pyrogram/sync.py @@ -37,7 +37,10 @@ def _ensure_event_loop(): pass try: - return asyncio.get_event_loop() + loop = asyncio.get_event_loop_policy().get_event_loop() + if loop.is_closed(): + raise RuntimeError("Event loop is closed") + return loop except RuntimeError: loop = asyncio.new_event_loop() asyncio.set_event_loop(loop)