diff --git a/compiler/api/source/main_api.tl b/compiler/api/source/main_api.tl index 628f519e..aa2ea4e0 100644 --- a/compiler/api/source/main_api.tl +++ b/compiler/api/source/main_api.tl @@ -53,6 +53,7 @@ inputMediaPoll#f94e5f1 flags:# poll:Poll correct_answers:flags.0?Vector s inputMediaDice#e66fbf7b emoticon:string = InputMedia; inputMediaStory#89fdd778 peer:InputPeer id:int = InputMedia; inputMediaWebPage#c21b8849 flags:# force_large_media:flags.0?true force_small_media:flags.1?true optional:flags.2?true url:string = InputMedia; +inputMediaPaidMedia#aa661fc3 stars_amount:long extended_media:Vector = InputMedia; inputChatPhotoEmpty#1ca48f57 = InputChatPhoto; inputChatUploadedPhoto#bdcdaec0 flags:# file:flags.0?InputFile video:flags.1?InputFile video_start_ts:flags.2?double video_emoji_markup:flags.3?VideoSize = InputChatPhoto; @@ -110,7 +111,7 @@ channel#aadfc8f flags:# creator:flags.0?true left:flags.2?true broadcast:flags.5 channelForbidden#17d493d5 flags:# broadcast:flags.5?true megagroup:flags.8?true id:long access_hash:long title:string until_date:flags.16?int = Chat; chatFull#2633421b flags:# can_set_username:flags.7?true has_scheduled:flags.8?true translations_disabled:flags.19?true id:long about:string participants:ChatParticipants chat_photo:flags.2?Photo notify_settings:PeerNotifySettings exported_invite:flags.13?ExportedChatInvite bot_info:flags.3?Vector pinned_msg_id:flags.6?int folder_id:flags.11?int call:flags.12?InputGroupCall ttl_period:flags.14?int groupcall_default_join_as:flags.15?Peer theme_emoticon:flags.16?string requests_pending:flags.17?int recent_requesters:flags.17?Vector available_reactions:flags.18?ChatReactions reactions_limit:flags.20?int = ChatFull; -channelFull#bbab348d flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true flags2:# can_delete_channel:flags2.0?true antispam:flags2.1?true participants_hidden:flags2.2?true translations_disabled:flags2.3?true stories_pinned_available:flags2.5?true view_forum_as_messages:flags2.6?true restricted_sponsored:flags2.11?true can_view_revenue:flags2.12?true id:long about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector migrated_from_chat_id:flags.4?long migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?long location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string requests_pending:flags.28?int recent_requesters:flags.28?Vector default_send_as:flags.29?Peer available_reactions:flags.30?ChatReactions reactions_limit:flags2.13?int stories:flags2.4?PeerStories wallpaper:flags2.7?WallPaper boosts_applied:flags2.8?int boosts_unrestrict:flags2.9?int emojiset:flags2.10?StickerSet = ChatFull; +channelFull#bbab348d flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true can_set_location:flags.16?true has_scheduled:flags.19?true can_view_stats:flags.20?true blocked:flags.22?true flags2:# can_delete_channel:flags2.0?true antispam:flags2.1?true participants_hidden:flags2.2?true translations_disabled:flags2.3?true stories_pinned_available:flags2.5?true view_forum_as_messages:flags2.6?true restricted_sponsored:flags2.11?true can_view_revenue:flags2.12?true paid_media_allowed:flags2.14?true id:long about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int online_count:flags.13?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:flags.23?ExportedChatInvite bot_info:Vector migrated_from_chat_id:flags.4?long migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int folder_id:flags.11?int linked_chat_id:flags.14?long location:flags.15?ChannelLocation slowmode_seconds:flags.17?int slowmode_next_send_date:flags.18?int stats_dc:flags.12?int pts:int call:flags.21?InputGroupCall ttl_period:flags.24?int pending_suggestions:flags.25?Vector groupcall_default_join_as:flags.26?Peer theme_emoticon:flags.27?string requests_pending:flags.28?int recent_requesters:flags.28?Vector default_send_as:flags.29?Peer available_reactions:flags.30?ChatReactions reactions_limit:flags2.13?int stories:flags2.4?PeerStories wallpaper:flags2.7?WallPaper boosts_applied:flags2.8?int boosts_unrestrict:flags2.9?int emojiset:flags2.10?StickerSet = ChatFull; chatParticipant#c02d4007 user_id:long inviter_id:long date:int = ChatParticipant; chatParticipantCreator#e46bcee4 user_id:long = ChatParticipant; @@ -142,6 +143,7 @@ messageMediaDice#3f7ee58b value:int emoticon:string = MessageMedia; messageMediaStory#68cb6283 flags:# via_mention:flags.1?true peer:Peer id:int story:flags.0?StoryItem = MessageMedia; messageMediaGiveaway#daad85b0 flags:# only_new_subscribers:flags.0?true winners_are_visible:flags.2?true channels:Vector countries_iso2:flags.1?Vector prize_description:flags.3?string quantity:int months:int until_date:int = MessageMedia; messageMediaGiveawayResults#c6991068 flags:# only_new_subscribers:flags.0?true refunded:flags.2?true channel_id:long additional_peers_count:flags.3?int launch_msg_id:int winners_count:int unclaimed_count:int winners:Vector months:int prize_description:flags.1?string until_date:int = MessageMedia; +messageMediaPaidMedia#a8852491 stars_amount:long extended_media:Vector = MessageMedia; messageActionEmpty#b6aef7b0 = MessageAction; messageActionChatCreate#bd47cbad title:string users:Vector = MessageAction; @@ -393,7 +395,7 @@ updateUserEmojiStatus#28373599 user_id:long emoji_status:EmojiStatus = Update; updateRecentEmojiStatuses#30f443db = Update; updateRecentReactions#6f7863f4 = Update; updateMoveStickerSetToTop#86fccf85 flags:# masks:flags.0?true emojis:flags.1?true stickerset:long = Update; -updateMessageExtendedMedia#5a73a98c peer:Peer msg_id:int extended_media:MessageExtendedMedia = Update; +updateMessageExtendedMedia#d5a41724 peer:Peer msg_id:int extended_media:Vector = Update; updateChannelPinnedTopic#192efbe3 flags:# pinned:flags.0?true channel_id:long topic_id:int = Update; updateChannelPinnedTopics#fe198602 flags:# channel_id:long order:flags.0?Vector = Update; updateUser#20529438 user_id:long = Update; @@ -770,7 +772,7 @@ auth.sentCodeTypeMissedCall#82006484 prefix:string length:int = auth.SentCodeTyp auth.sentCodeTypeEmailCode#f450f59b flags:# apple_signin_allowed:flags.0?true google_signin_allowed:flags.1?true email_pattern:string length:int reset_available_period:flags.3?int reset_pending_date:flags.4?int = auth.SentCodeType; auth.sentCodeTypeSetUpEmailRequired#a5491dea flags:# apple_signin_allowed:flags.0?true google_signin_allowed:flags.1?true = auth.SentCodeType; auth.sentCodeTypeFragmentSms#d9565c39 url:string length:int = auth.SentCodeType; -auth.sentCodeTypeFirebaseSms#13c90f17 flags:# nonce:flags.0?bytes play_integrity_nonce:flags.2?bytes receipt:flags.1?string push_timeout:flags.1?int length:int = auth.SentCodeType; +auth.sentCodeTypeFirebaseSms#9fd736 flags:# nonce:flags.0?bytes play_integrity_project_id:flags.2?long play_integrity_nonce:flags.2?bytes receipt:flags.1?string push_timeout:flags.1?int length:int = auth.SentCodeType; auth.sentCodeTypeSmsWord#a416ac81 flags:# beginning:flags.0?string = auth.SentCodeType; auth.sentCodeTypeSmsPhrase#b37794af flags:# beginning:flags.0?string = auth.SentCodeType; @@ -1431,9 +1433,7 @@ attachMenuBots#3c4301c0 hash:long bots:Vector users:Vector attachMenuBotsBot#93bf667f bot:AttachMenuBot users:Vector = AttachMenuBotsBot; -webViewResultUrl#c14557c query_id:long url:string = WebViewResult; - -simpleWebViewResultUrl#882f76bb url:string = SimpleWebViewResult; +webViewResultUrl#4d22ff98 flags:# fullsize:flags.1?true query_id:flags.0?long url:string = WebViewResult; webViewMessageSent#c94511c flags:# msg_id:flags.0?InputBotInlineMessageID = WebViewMessageSent; @@ -1563,8 +1563,6 @@ botApp#95fcd1d6 flags:# id:long access_hash:long short_name:string title:string messages.botApp#eb50adf5 flags:# inactive:flags.0?true request_write_access:flags.1?true has_settings:flags.2?true app:BotApp = messages.BotApp; -appWebViewResultUrl#3c1b4f0d url:string = AppWebViewResult; - inlineBotWebView#b57295d5 text:string url:string = InlineBotWebView; readParticipantDate#4a4ff172 user_id:long date:int = ReadParticipantDate; @@ -1812,10 +1810,11 @@ starsTransactionPeerPlayMarket#7b560a0b = StarsTransactionPeer; starsTransactionPeerPremiumBot#250dbaf8 = StarsTransactionPeer; starsTransactionPeerFragment#e92fd902 = StarsTransactionPeer; starsTransactionPeer#d80da15d peer:Peer = StarsTransactionPeer; +starsTransactionPeerAds#60682812 = StarsTransactionPeer; starsTopupOption#bd915c0 flags:# extended:flags.1?true stars:long store_product:flags.0?string currency:string amount:long = StarsTopupOption; -starsTransaction#aa00c898 flags:# refund:flags.3?true pending:flags.4?true failed:flags.6?true id:string stars:long date:int peer:StarsTransactionPeer title:flags.0?string description:flags.1?string photo:flags.2?WebDocument transaction_date:flags.5?int transaction_url:flags.5?string = StarsTransaction; +starsTransaction#2db5418f flags:# refund:flags.3?true pending:flags.4?true failed:flags.6?true id:string stars:long date:int peer:StarsTransactionPeer title:flags.0?string description:flags.1?string photo:flags.2?WebDocument transaction_date:flags.5?int transaction_url:flags.5?string bot_payload:flags.7?bytes msg_id:flags.8?int extended_media:flags.9?Vector = StarsTransaction; payments.starsStatus#8cf4ee60 flags:# balance:long history:Vector next_offset:flags.0?string chats:Vector users:Vector = payments.StarsStatus; @@ -1831,6 +1830,10 @@ payments.starsRevenueStats#c92bb73b revenue_graph:StatsGraph status:StarsRevenue payments.starsRevenueWithdrawalUrl#1dab80b7 url:string = payments.StarsRevenueWithdrawalUrl; +payments.starsRevenueAdsAccountUrl#394e7f21 url:string = payments.StarsRevenueAdsAccountUrl; + +inputStarsTransaction#206ae6d1 flags:# refund:flags.0?true id:string = InputStarsTransaction; + ---functions--- invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X; @@ -2174,9 +2177,9 @@ messages.searchSentMedia#107e31a0 q:string filter:MessagesFilter limit:int = mes messages.getAttachMenuBots#16fcc2cb hash:long = AttachMenuBots; messages.getAttachMenuBot#77216192 bot:InputUser = AttachMenuBotsBot; messages.toggleBotInAttachMenu#69f59d69 flags:# write_allowed:flags.0?true bot:InputUser enabled:Bool = Bool; -messages.requestWebView#269dc2c1 flags:# from_bot_menu:flags.4?true silent:flags.5?true peer:InputPeer bot:InputUser url:flags.1?string start_param:flags.3?string theme_params:flags.2?DataJSON platform:string reply_to:flags.0?InputReplyTo send_as:flags.13?InputPeer = WebViewResult; +messages.requestWebView#269dc2c1 flags:# from_bot_menu:flags.4?true silent:flags.5?true compact:flags.7?true peer:InputPeer bot:InputUser url:flags.1?string start_param:flags.3?string theme_params:flags.2?DataJSON platform:string reply_to:flags.0?InputReplyTo send_as:flags.13?InputPeer = WebViewResult; messages.prolongWebView#b0d81a83 flags:# silent:flags.5?true peer:InputPeer bot:InputUser query_id:long reply_to:flags.0?InputReplyTo send_as:flags.13?InputPeer = Bool; -messages.requestSimpleWebView#1a46500a flags:# from_switch_webview:flags.1?true from_side_menu:flags.2?true bot:InputUser url:flags.3?string start_param:flags.4?string theme_params:flags.0?DataJSON platform:string = SimpleWebViewResult; +messages.requestSimpleWebView#413a3e73 flags:# from_switch_webview:flags.1?true from_side_menu:flags.2?true compact:flags.7?true bot:InputUser url:flags.3?string start_param:flags.4?string theme_params:flags.0?DataJSON platform:string = WebViewResult; messages.sendWebViewResultMessage#a4314f5 bot_query_id:string result:InputBotInlineResult = WebViewMessageSent; messages.sendWebViewData#dc0242c8 bot:InputUser random_id:long button_text:string data:string = Updates; messages.transcribeAudio#269e9a49 peer:InputPeer msg_id:int = messages.TranscribedAudio; @@ -2198,7 +2201,7 @@ messages.getEmojiProfilePhotoGroups#21a548f3 hash:int = messages.EmojiGroups; messages.searchCustomEmoji#2c11c0d7 emoticon:string hash:long = EmojiList; messages.togglePeerTranslations#e47cb579 flags:# disabled:flags.0?true peer:InputPeer = Bool; messages.getBotApp#34fdc5c3 app:InputBotApp hash:long = messages.BotApp; -messages.requestAppWebView#8c5a3b3c flags:# write_allowed:flags.0?true peer:InputPeer app:InputBotApp start_param:flags.1?string theme_params:flags.2?DataJSON platform:string = AppWebViewResult; +messages.requestAppWebView#53618bce flags:# write_allowed:flags.0?true compact:flags.7?true peer:InputPeer app:InputBotApp start_param:flags.1?string theme_params:flags.2?DataJSON platform:string = WebViewResult; messages.setChatWallPaper#8ffacae1 flags:# for_both:flags.3?true revert:flags.4?true peer:InputPeer wallpaper:flags.0?InputWallPaper settings:flags.2?WallPaperSettings id:flags.1?int = Updates; messages.searchEmojiStickerSets#92b4494c flags:# exclude_featured:flags.0?true q:string hash:long = messages.FoundStickerSets; messages.getSavedDialogs#5381d21a flags:# exclude_pinned:flags.0?true offset_date:int offset_id:int offset_peer:InputPeer limit:int hash:long = messages.SavedDialogs; @@ -2378,6 +2381,8 @@ payments.sendStarsForm#2bb731d flags:# form_id:long invoice:InputInvoice = payme payments.refundStarsCharge#25ae8f4a user_id:InputUser charge_id:string = Updates; payments.getStarsRevenueStats#d91ffad6 flags:# dark:flags.0?true peer:InputPeer = payments.StarsRevenueStats; payments.getStarsRevenueWithdrawalUrl#13bbe8b3 peer:InputPeer stars:long password:InputCheckPasswordSRP = payments.StarsRevenueWithdrawalUrl; +payments.getStarsRevenueAdsAccountUrl#d1d7efc5 peer:InputPeer = payments.StarsRevenueAdsAccountUrl; +payments.getStarsTransactionsByID#27842d2e peer:InputPeer id:Vector = payments.StarsStatus; stickers.createStickerSet#9021ab67 flags:# masks:flags.0?true emojis:flags.5?true text_color:flags.6?true user_id:InputUser title:string short_name:string thumb:flags.2?InputDocument stickers:Vector software:flags.3?string = messages.StickerSet; stickers.removeStickerFromSet#f7760f51 sticker:InputDocument = messages.StickerSet; @@ -2497,4 +2502,4 @@ smsjobs.finishJob#4f1ebf24 flags:# job_id:string error:flags.0?string = Bool; fragment.getCollectibleInfo#be1e85ba collectible:InputCollectible = fragment.CollectibleInfo; -// LAYER 182 +// LAYER 183 diff --git a/compiler/docs/compiler.py b/compiler/docs/compiler.py index 7cdd92a0..2e8b0421 100644 --- a/compiler/docs/compiler.py +++ b/compiler/docs/compiler.py @@ -168,6 +168,7 @@ def pyrogram_api(): forward_messages copy_message copy_media_group + send_paid_media send_photo send_audio send_document @@ -229,6 +230,9 @@ def pyrogram_api(): set_administrator_title set_chat_photo delete_chat_photo + delete_folder + export_folder_link + update_folder set_chat_title set_chat_description set_chat_permissions @@ -241,6 +245,7 @@ def pyrogram_api(): get_chat_members_count get_dialogs get_dialogs_count + get_folders get_forum_topics get_forum_topics_by_id set_chat_username @@ -271,6 +276,8 @@ def pyrogram_api(): reopen_forum_topic reopen_general_topic unhide_general_topic + update_color + update_folder """, users=""" Users @@ -453,6 +460,7 @@ def pyrogram_api(): ChatJoinedByRequest ChatJoiner Dialog + Folder Restriction EmojiStatus ForumTopic @@ -513,6 +521,8 @@ def pyrogram_api(): ReactionType MessageReactionUpdated MessageReactionCountUpdated + ExtendedMediaPreview + PaidMedia """, stories=""" Stories diff --git a/docs/source/_includes/usable-by/bots.rst b/docs/source/_includes/usable-by/bots.rst index a54a754d..82e7120e 100644 --- a/docs/source/_includes/usable-by/bots.rst +++ b/docs/source/_includes/usable-by/bots.rst @@ -1,5 +1,7 @@ .. raw:: html - Usable by - Users - Bots +
+ Usable by + Users + Bots +
diff --git a/docs/source/_includes/usable-by/users-bots.rst b/docs/source/_includes/usable-by/users-bots.rst index 50b87907..85f4cbba 100644 --- a/docs/source/_includes/usable-by/users-bots.rst +++ b/docs/source/_includes/usable-by/users-bots.rst @@ -1,5 +1,7 @@ .. raw:: html - Usable by - Users - Bots +
+ Usable by + Users + Bots +
diff --git a/docs/source/_includes/usable-by/users.rst b/docs/source/_includes/usable-by/users.rst index 5b09bd54..02241f22 100644 --- a/docs/source/_includes/usable-by/users.rst +++ b/docs/source/_includes/usable-by/users.rst @@ -1,5 +1,7 @@ .. raw:: html - Usable by - Users - Bots +
+ Usable by + Users + Bots +
diff --git a/docs/source/api/enums/FolderColor.rst b/docs/source/api/enums/FolderColor.rst new file mode 100644 index 00000000..0f727d28 --- /dev/null +++ b/docs/source/api/enums/FolderColor.rst @@ -0,0 +1,8 @@ +FolderColor +=========== + +.. autoclass:: pyrogram.enums.FolderColor() + :members: + +.. raw:: html + :file: ./cleanup.html diff --git a/docs/source/api/enums/index.rst b/docs/source/api/enums/index.rst index 03a61bfd..7eb92c3b 100644 --- a/docs/source/api/enums/index.rst +++ b/docs/source/api/enums/index.rst @@ -19,6 +19,7 @@ to apply only a valid value among the expected ones. ChatMemberStatus ChatMembersFilter ChatType + FolderColor ListenerTypes MessageEntityType MessageMediaType @@ -41,6 +42,7 @@ to apply only a valid value among the expected ones. ChatMemberStatus ChatMembersFilter ChatType + FolderColor ListenerTypes MessageEntityType MessageMediaType diff --git a/pyrogram/__init__.py b/pyrogram/__init__.py index 46b72ff5..1793472d 100644 --- a/pyrogram/__init__.py +++ b/pyrogram/__init__.py @@ -18,7 +18,7 @@ # along with Pyrofork. If not, see . __fork_name__ = "PyroFork" -__version__ = "2.3.26" +__version__ = "2.3.29" __license__ = "GNU Lesser General Public License v3.0 (LGPL-3.0)" __copyright__ = "Copyright (C) 2022-present Mayuri-Chan " diff --git a/pyrogram/enums/__init__.py b/pyrogram/enums/__init__.py index d066a0d0..6b149413 100644 --- a/pyrogram/enums/__init__.py +++ b/pyrogram/enums/__init__.py @@ -24,6 +24,7 @@ from .chat_member_status import ChatMemberStatus from .chat_members_filter import ChatMembersFilter from .chat_type import ChatType from .client_platform import ClientPlatform +from .folder_color import FolderColor from .listerner_types import ListenerTypes from .message_entity_type import MessageEntityType from .message_media_type import MessageMediaType @@ -47,7 +48,8 @@ __all__ = [ 'ChatMemberStatus', 'ChatMembersFilter', 'ChatType', - 'ClientPlatform', + 'ClientPlatform', + 'FolderColor', 'ListenerTypes', 'MessageEntityType', 'MessageMediaType', diff --git a/pyrogram/enums/folder_color.py b/pyrogram/enums/folder_color.py new file mode 100644 index 00000000..980c4a3b --- /dev/null +++ b/pyrogram/enums/folder_color.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 .auto_name import AutoName + + +class FolderColor(AutoName): + """Folder color enumeration used in :obj:`~pyrogram.types.Folder`.""" + + NO_COLOR = None + "No color." + + RED = 0 + "Red color." + + ORANGE = 1 + "Orange color." + + VIOLET = 2 + "Violet color." + + GREEN = 3 + "Green color." + + CYAN = 4 + "Cyan color." + + BLUE = 5 + "Blue color." + + PINK = 6 + "Pink color." diff --git a/pyrogram/enums/message_media_type.py b/pyrogram/enums/message_media_type.py index 10b11cc7..c9e3fa44 100644 --- a/pyrogram/enums/message_media_type.py +++ b/pyrogram/enums/message_media_type.py @@ -81,3 +81,6 @@ class MessageMediaType(AutoName): INVOICE = auto() "Invoice media" + + PAID_MEDIA = auto() + "Paid media" diff --git a/pyrogram/methods/chats/__init__.py b/pyrogram/methods/chats/__init__.py index a0619862..8788aea9 100644 --- a/pyrogram/methods/chats/__init__.py +++ b/pyrogram/methods/chats/__init__.py @@ -28,11 +28,13 @@ from .close_forum_topic import CloseForumTopic from .close_general_topic import CloseGeneralTopic from .delete_channel import DeleteChannel from .delete_chat_photo import DeleteChatPhoto +from .delete_folder import DeleteFolder from .delete_forum_topic import DeleteForumTopic from .delete_supergroup import DeleteSupergroup from .delete_user_history import DeleteUserHistory from .edit_forum_topic import EditForumTopic from .edit_general_topic import EditGeneralTopic +from .export_folder_link import ExportFolderLink from .reopen_forum_topic import ReopenForumTopic from .reopen_general_topic import ReopenGeneralTopic from .hide_general_topic import HideGeneralTopic @@ -45,6 +47,7 @@ from .get_chat_members_count import GetChatMembersCount from .get_chat_online_count import GetChatOnlineCount from .get_dialogs import GetDialogs from .get_dialogs_count import GetDialogsCount +from .get_folders import GetFolders from .get_forum_topics import GetForumTopics from .get_forum_topics_by_id import GetForumTopicsByID from .get_nearby_chats import GetNearbyChats @@ -69,6 +72,7 @@ from .unban_chat_member import UnbanChatMember from .unpin_all_chat_messages import UnpinAllChatMessages from .unpin_chat_message import UnpinChatMessage from .update_color import UpdateColor +from .update_folder import UpdateFolder class Chats( @@ -92,6 +96,7 @@ class Chats( SetChatUsername, SetChatPermissions, GetDialogsCount, + GetFolders, GetForumTopics, GetForumTopicsByID, ArchiveChats, @@ -104,10 +109,12 @@ class Chats( CloseGeneralTopic, AddChatMembers, DeleteChannel, + DeleteFolder, DeleteForumTopic, DeleteSupergroup, EditForumTopic, EditGeneralTopic, + ExportFolderLink, ReopenForumTopic, ReopenGeneralTopic, HideGeneralTopic, @@ -123,6 +130,7 @@ class Chats( GetSendAsChats, SetSendAsChat, SetChatProtectedContent, - UpdateColor + UpdateColor, + UpdateFolder ): pass diff --git a/pyrogram/methods/chats/delete_folder.py b/pyrogram/methods/chats/delete_folder.py new file mode 100644 index 00000000..1f08e3c3 --- /dev/null +++ b/pyrogram/methods/chats/delete_folder.py @@ -0,0 +1,52 @@ +# Pyrofork - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# Copyright (C) 2022-present Mayuri-Chan +# +# This file is part of Pyrofork. +# +# Pyrofork is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrofork is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrofork. If not, see . + +import pyrogram +from pyrogram import raw + + +class DeleteFolder: + async def delete_folder( + self: "pyrogram.Client", + folder_id: int + ) -> bool: + """Delete a user's folder. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + folder_id (``int``): + Unique identifier (int) of the target folder. + + Returns: + ``bool``: True, on success. + + Example: + .. code-block:: python + + # Delete folder + app.delete_folder(folder_id) + """ + r = await self.invoke( + raw.functions.messages.UpdateDialogFilter( + id=folder_id + ) + ) + + return r diff --git a/pyrogram/methods/chats/export_folder_link.py b/pyrogram/methods/chats/export_folder_link.py new file mode 100644 index 00000000..17141734 --- /dev/null +++ b/pyrogram/methods/chats/export_folder_link.py @@ -0,0 +1,70 @@ +# Pyrofork - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# Copyright (C) 2022-present Mayuri-Chan +# +# This file is part of Pyrofork. +# +# Pyrofork is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrofork is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrofork. If not, see . + +import pyrogram +from pyrogram import raw + + +class ExportFolderLink: + async def export_folder_link( + self: "pyrogram.Client", + folder_id: int + ) -> str: + """Export link to a user's folder. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + folder_id (``int``): + Unique identifier (int) of the target folder. + + Returns: + ``str``: On success, a link to the folder as string is returned. + + Example: + .. code-block:: python + + # Export folder link + app.export_folder_link(folder_id) + """ + folder = await self.get_folders(folder_id) + + if not folder: + return + + peers = [] + + if folder.included_chats: + peers.extend(iter(folder.included_chats)) + + if folder.excluded_chats: + peers.extend(iter(folder.included_chats)) + + if folder.pinned_chats: + peers.extend(iter(folder.included_chats)) + + r = await self.invoke( + raw.functions.chatlists.ExportChatlistInvite( + chatlist=raw.types.InputChatlistDialogFilter(filter_id=folder_id), + title=folder.title, + peers=[await self.resolve_peer(i.id) for i in peers], + ) + ) + + return r.invite.url diff --git a/pyrogram/methods/chats/get_folders.py b/pyrogram/methods/chats/get_folders.py new file mode 100644 index 00000000..cadfec50 --- /dev/null +++ b/pyrogram/methods/chats/get_folders.py @@ -0,0 +1,98 @@ +# Pyrofork - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# Copyright (C) 2022-present Mayuri-Chan +# +# This file is part of Pyrofork. +# +# Pyrofork is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrofork is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrofork. If not, see . + +from typing import Union, List, Iterable + +import pyrogram +from pyrogram import types, raw, utils + + +class GetFolders: + async def get_folders( + self: "pyrogram.Client", + folder_ids: Union[int, Iterable[int]] = None + ) -> Union["types.Folder", List["types.Folder"]]: + """Get one or more folders by using folder identifiers. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + folder_ids (``int`` | Iterable of ``int``, *optional*): + Pass a single folder identifier or an iterable of folder ids (as integers) to get the content of the + folders themselves. + By default all folders are returned. + + Returns: + :obj:`~pyrogram.types.Folder` | List of :obj:`~pyrogram.types.Folder`: In case *folder_ids* was not + a list, a single folder is returned, otherwise a list of folders is returned. + + Example: + .. code-block:: python + + # Get one folder + await app.get_folders(12345) + + # Get more than one folders (list of folders) + await app.get_folders([12345, 12346]) + + # Get all folders + await app.get_folders() + """ + is_iterable = hasattr(folder_ids, "__iter__") + ids = set(folder_ids) if is_iterable else {folder_ids} + + dialog_filters = await self.invoke(raw.functions.messages.GetDialogFilters()) + + raw_folders = [ + folder for folder in dialog_filters.filters + if not isinstance(folder, raw.types.DialogFilterDefault) and (is_iterable and folder.id in ids or not is_iterable) + ] + + raw_peers = {} + for folder in raw_folders: + for peer in folder.pinned_peers + folder.include_peers + getattr(folder, "exclude_peers", []): + raw_peers[utils.get_peer_id(peer)] = peer + + users = {} + chats = {} + for i in range(0, len(raw_peers), 100): + chunk = list(raw_peers.values())[i:i + 100] + r = await self.invoke( + raw.functions.messages.GetPeerDialogs( + peers=[raw.types.InputDialogPeer(peer=peer) for peer in chunk] + ) + ) + users.update({i.id: i for i in r.users}) + chats.update({i.id: i for i in r.chats}) + + folders = types.List(types.Folder._parse(self, folder, users, chats) for folder in raw_folders) + + if not folders: + return None + + if folder_ids: + if is_iterable: + return folders + else: + for folder in folders: + if folder.id == folder_ids: + return folder + return None + + return folders diff --git a/pyrogram/methods/chats/update_folder.py b/pyrogram/methods/chats/update_folder.py new file mode 100644 index 00000000..a641fb99 --- /dev/null +++ b/pyrogram/methods/chats/update_folder.py @@ -0,0 +1,149 @@ +# Pyrofork - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# Copyright (C) 2022-present Mayuri-Chan +# +# This file is part of Pyrofork. +# +# Pyrofork is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrofork is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrofork. If not, see . + +from typing import List, Union + +import pyrogram +from pyrogram import raw +from pyrogram import enums + + +class UpdateFolder: + async def update_folder( + self: "pyrogram.Client", + folder_id: int, + title: str, + included_chats: Union[Union[int, str], List[Union[int, str]]] = None, + excluded_chats: Union[Union[int, str], List[Union[int, str]]] = None, + pinned_chats: Union[Union[int, str], List[Union[int, str]]] = None, + contacts: bool = None, + non_contacts: bool = None, + groups: bool = None, + channels: bool = None, + bots: bool = None, + exclude_muted: bool = None, + exclude_read: bool = None, + exclude_archived: bool = None, + color: "enums.FolderColor" = None, + emoji: str = None + ) -> bool: + """Create or update a user's folder. + + .. include:: /_includes/usable-by/users.rst + + Parameters: + folder_id (``int``): + Unique folder identifier. + + title (``str``): + Folder title. + + included_chats (``int`` | ``str`` | List of ``int`` or ``str``, *optional*): + Users or chats that should added in the folder + You can pass an ID (int), username (str) or phone number (str). + Multiple users can be added by passing a list of IDs, usernames or phone numbers. + + excluded_chats (``int`` | ``str`` | List of ``int`` or ``str``, *optional*): + Users or chats that should excluded from the folder + You can pass an ID (int), username (str) or phone number (str). + Multiple users can be added by passing a list of IDs, usernames or phone numbers. + + pinned_chats (``int`` | ``str`` | List of ``int`` or ``str``, *optional*): + Users or chats that should pinned in the folder + You can pass an ID (int), username (str) or phone number (str). + Multiple users can be added by passing a list of IDs, usernames or phone numbers. + + contacts (``bool``, *optional*): + Pass True if folder should contain contacts. + + non_contacts (``bool``, *optional*): + Pass True if folder should contain non contacts. + + groups (``bool``, *optional*): + Pass True if folder should contain groups. + + channels (``bool``, *optional*): + Pass True if folder should contain channels. + + bots (``bool``, *optional*): + Pass True if folder should contain bots. + + exclude_muted (``bool``, *optional*): + Pass True if folder should exclude muted users. + + exclude_archived (``bool``, *optional*): + Pass True if folder should exclude archived users. + + emoji (``str``, *optional*): + Folder emoji. + Pass None to leave the folder icon as default. + + color (:obj:`~pyrogram.enums.FolderColor`, *optional*): + Color type. + Pass :obj:`~pyrogram.enums.FolderColor` to set folder color. + + Returns: + ``bool``: True, on success. + + Example: + .. code-block:: python + + # Create or update folder + app.update_folder(folder_id, title="New folder", included_chats="me") + """ + if not isinstance(included_chats, list): + included_chats = [included_chats] if included_chats else [] + if not isinstance(excluded_chats, list): + excluded_chats = [excluded_chats] if excluded_chats else [] + if not isinstance(pinned_chats, list): + pinned_chats = [pinned_chats] if pinned_chats else [] + + r = await self.invoke( + raw.functions.messages.UpdateDialogFilter( + id=folder_id, + filter=raw.types.DialogFilter( + id=folder_id, + title=title, + pinned_peers=[ + await self.resolve_peer(user_id) + for user_id in pinned_chats + ], + include_peers=[ + await self.resolve_peer(user_id) + for user_id in included_chats + ], + exclude_peers=[ + await self.resolve_peer(user_id) + for user_id in excluded_chats + ], + contacts=contacts, + non_contacts=non_contacts, + groups=groups, + broadcasts=channels, + bots=bots, + exclude_muted=exclude_muted, + exclude_read=exclude_read, + exclude_archived=exclude_archived, + emoticon=emoji, + color=color.value if color else None + ) + ) + ) + + return r diff --git a/pyrogram/methods/messages/__init__.py b/pyrogram/methods/messages/__init__.py index e7834bc7..0ad012a9 100644 --- a/pyrogram/methods/messages/__init__.py +++ b/pyrogram/methods/messages/__init__.py @@ -58,6 +58,7 @@ from .send_invoice import SendInvoice from .send_location import SendLocation from .send_media_group import SendMediaGroup from .send_message import SendMessage +from .send_paid_media import SendPaidMedia from .send_photo import SendPhoto from .send_poll import SendPoll from .send_reaction import SendReaction @@ -91,6 +92,7 @@ class Messages( SendLocation, SendMediaGroup, SendMessage, + SendPaidMedia, SendPhoto, SendSticker, SendVenue, diff --git a/pyrogram/methods/messages/search_global_hashtag_messages_count.py b/pyrogram/methods/messages/search_global_hashtag_messages_count.py index b953e4c4..59ae30f2 100644 --- a/pyrogram/methods/messages/search_global_hashtag_messages_count.py +++ b/pyrogram/methods/messages/search_global_hashtag_messages_count.py @@ -21,7 +21,7 @@ from pyrogram import raw class SearchGlobalHashtagMessagesCount: - async def search_golbal_hashtag_messages_count( + async def search_global_hashtag_messages_count( self: "pyrogram.Client", hashtag: str = "", ) -> int: diff --git a/pyrogram/methods/messages/send_paid_media.py b/pyrogram/methods/messages/send_paid_media.py new file mode 100644 index 00000000..a04f4655 --- /dev/null +++ b/pyrogram/methods/messages/send_paid_media.py @@ -0,0 +1,290 @@ +# Pyrofork - Telegram MTProto API Client Library for Python +# Copyright (C) 2022-present Mayuri-Chan +# +# This file is part of Pyrofork. +# +# Pyrofork is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrofork is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrofork. If not, see . + +import os +import re + +from datetime import datetime +from pymediainfo import MediaInfo +from typing import Union, List, Optional + +import pyrogram +from pyrogram import raw, utils, enums +from pyrogram import types +from pyrogram.file_id import FileType + + +class SendPaidMedia: + async def send_paid_media( + self: "pyrogram.Client", + chat_id: Union[int, str], + stars_amount: int, + media: List[Union["types.InputMediaAnimation", "types.InputMediaPhoto", "types.InputMediaVideo"]], + caption: str = "", + caption_entities: List["types.MessageEntity"] = None, + parse_mode: Optional["enums.ParseMode"] = None, + disable_notification: bool = None, + schedule_date: datetime = None, + protect_content: bool = None, + invert_media: bool = None + ) -> "types.Message": + """Send paid media. + Only for channels. + + .. include:: /_includes/usable-by/users-bots.rst + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier for the target chat or username of the target channel (in the format @channelusername). + + stars_amount (``int``): + Amount of stars. + + media (List of :obj:`~pyrogram.types.InputMediaAnimation` | :obj:`~pyrogram.types.InputMediaPhoto` | :obj:`~pyrogram.types.InputMediaVideo`): + A list of media to send. + + caption (``str``, *optional*): + Media caption, 0-1024 characters. + + caption_entities (List of :obj:`~pyrogram.types.MessageEntity`, *optional*): + Special entities that appear in the caption, which can be specified instead of parse_mode. + + parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*): + Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your bot's message. + + disable_notification (``bool``, *optional*): + Sends the message silently. Users will receive a notification with no sound. + + schedule_date (:obj:`~datetime.datetime`, *optional*): + Date when the message will be automatically sent. Pass a :obj:`~datetime.datetime` object. + + protect_content (``bool``, *optional*): + Protect content from being forwarded. + + invert_media (``bool``, *optional*): + Invert the media. + + Example: + .. code-block:: python + + app.send_paid_media( + chat_id="pyrogram", + stars_amount=100, + media=[ + types.InputMediaPhoto("/path/to/photo.jpg"), + types.InputMediaVideo("video_file_id") + ], + caption="This is a paid media message." + ) + """ + multi_media = [] + + for i in media: + if isinstance(i, types.InputMediaPhoto): + if isinstance(i.media, str): + if os.path.isfile(i.media): + media = await self.invoke( + raw.functions.messages.UploadMedia( + peer=await self.resolve_peer(chat_id), + media=raw.types.InputMediaUploadedPhoto( + file=await self.save_file(i.media), + spoiler=i.has_spoiler + ) + ) + ) + + media = raw.types.InputMediaPhoto( + id=raw.types.InputPhoto( + id=media.photo.id, + access_hash=media.photo.access_hash, + file_reference=media.photo.file_reference + ), + spoiler=i.has_spoiler + ) + elif re.match("^https?://", i.media): + media = await self.invoke( + raw.functions.messages.UploadMedia( + peer=await self.resolve_peer(chat_id), + media=raw.types.InputMediaPhotoExternal( + url=i.media, + spoiler=i.has_spoiler + ) + ) + ) + + media = raw.types.InputMediaPhoto( + id=raw.types.InputPhoto( + id=media.photo.id, + access_hash=media.photo.access_hash, + file_reference=media.photo.file_reference + ), + spoiler=i.has_spoiler + ) + else: + media = utils.get_input_media_from_file_id(i.media, FileType.PHOTO) + else: + media = await self.invoke( + raw.functions.messages.UploadMedia( + peer=await self.resolve_peer(chat_id), + media=raw.types.InputMediaUploadedPhoto( + file=await self.save_file(i.media), + spoiler=i.has_spoiler + ) + ) + ) + + media = raw.types.InputMediaPhoto( + id=raw.types.InputPhoto( + id=media.photo.id, + access_hash=media.photo.access_hash, + file_reference=media.photo.file_reference + ), + spoiler=i.has_spoiler + ) + elif ( + isinstance(i, types.InputMediaVideo) + or + isinstance(i, types.InputMediaAnimation) + ): + if isinstance(i.media, str): + is_animation = False + if os.path.isfile(i.media): + try: + videoInfo = MediaInfo.parse(i.media) + except OSError: + is_animation = True if isinstance(i, types.InputMediaAnimation) else False + else: + if not any([track.track_type == 'Audio' for track in videoInfo.tracks]): + is_animation = True + attributes = [ + raw.types.DocumentAttributeVideo( + supports_streaming=True if is_animation else (i.supports_streaming or None), + duration=i.duration, + w=i.width, + h=i.height + ), + raw.types.DocumentAttributeFilename(file_name=os.path.basename(i.media)) + ] + if is_animation: + attributes.append(raw.types.DocumentAttributeAnimated()) + media = await self.invoke( + raw.functions.messages.UploadMedia( + peer=await self.resolve_peer(chat_id), + media=raw.types.InputMediaUploadedDocument( + file=await self.save_file(i.media), + thumb=await self.save_file(i.thumb), + spoiler=i.has_spoiler, + mime_type=self.guess_mime_type(i.media) or "video/mp4", + nosound_video=is_animation, + attributes=attributes + ) + ) + ) + + media = raw.types.InputMediaDocument( + id=raw.types.InputDocument( + id=media.document.id, + access_hash=media.document.access_hash, + file_reference=media.document.file_reference + ), + spoiler=i.has_spoiler + ) + elif re.match("^https?://", i.media): + media = await self.invoke( + raw.functions.messages.UploadMedia( + peer=await self.resolve_peer(chat_id), + media=raw.types.InputMediaDocumentExternal( + url=i.media, + spoiler=i.has_spoiler + ) + ) + ) + + media = raw.types.InputMediaDocument( + id=raw.types.InputDocument( + id=media.document.id, + access_hash=media.document.access_hash, + file_reference=media.document.file_reference + ), + spoiler=i.has_spoiler + ) + else: + media = utils.get_input_media_from_file_id(i.media, FileType.VIDEO) + else: + media = await self.invoke( + raw.functions.messages.UploadMedia( + peer=await self.resolve_peer(chat_id), + media=raw.types.InputMediaUploadedDocument( + file=await self.save_file(i.media), + thumb=await self.save_file(i.thumb), + spoiler=i.has_spoiler, + mime_type=self.guess_mime_type(getattr(i.media, "name", "video.mp4")) or "video/mp4", + attributes=[ + raw.types.DocumentAttributeVideo( + supports_streaming=i.supports_streaming or None, + duration=i.duration, + w=i.width, + h=i.height + ), + raw.types.DocumentAttributeFilename(file_name=getattr(i.media, "name", "video.mp4")) + ] + ) + ) + ) + + media = raw.types.InputMediaDocument( + id=raw.types.InputDocument( + id=media.document.id, + access_hash=media.document.access_hash, + file_reference=media.document.file_reference + ), + spoiler=i.has_spoiler + ) + else: + raise ValueError(f"{i.__class__.__name__} is not a supported type for send_paid_media") + multi_media.append(media) + + rpc = raw.functions.messages.SendMedia( + peer=await self.resolve_peer(chat_id), + media=raw.types.InputMediaPaidMedia( + stars_amount=stars_amount, + extended_media=multi_media + ), + silent=disable_notification or None, + random_id=self.rnd_id(), + schedule_date=utils.datetime_to_timestamp(schedule_date), + noforwards=protect_content, + invert_media=invert_media, + **await utils.parse_text_entities(self, caption, parse_mode, caption_entities) + ) + r = await self.invoke(rpc, sleep_threshold=60) + + return await utils.parse_messages( + self, + raw.types.messages.Messages( + messages=[m.message for m in filter( + lambda u: isinstance(u, (raw.types.UpdateNewMessage, + raw.types.UpdateNewChannelMessage, + raw.types.UpdateNewScheduledMessage, + raw.types.UpdateBotNewBusinessMessage)), + r.updates + )], + users=r.users, + chats=r.chats + ) + ) diff --git a/pyrogram/methods/messages/send_poll.py b/pyrogram/methods/messages/send_poll.py index 0071fe5a..c4b049b0 100644 --- a/pyrogram/methods/messages/send_poll.py +++ b/pyrogram/methods/messages/send_poll.py @@ -172,7 +172,17 @@ class SendPoll: Example: .. code-block:: python - await app.send_poll(chat_id, "Is this a poll question?", ["Yes", "No", "Maybe"]) + from pyrogram.types import PollOption + + await app.send_poll( + chat_id, + "Is this a poll question?", + [ + PollOption("Yes"), + PollOption("No"), + PollOption("Maybe") + ] + ) """ reply_to = await utils.get_reply_to( diff --git a/pyrogram/parser/markdown.py b/pyrogram/parser/markdown.py index d00e7558..4daf27ce 100644 --- a/pyrogram/parser/markdown.py +++ b/pyrogram/parser/markdown.py @@ -57,6 +57,7 @@ OPENING_TAG = "<{}>" CLOSING_TAG = "" URL_MARKUP = '{}' FIXED_WIDTH_DELIMS = [CODE_DELIM, PRE_DELIM] +CODE_TAG_RE = re.compile(r".*?") class Markdown: @@ -108,6 +109,12 @@ class Markdown: delims = set() is_fixed_width = False + placeholders = {} + for i, code_section in enumerate(CODE_TAG_RE.findall(text)): + placeholder = f"{{CODE_SECTION_{i}}}" + placeholders[placeholder] = code_section + text = text.replace(code_section, placeholder, 1) + for i, match in enumerate(re.finditer(MARKDOWN_RE, text)): start, _ = match.span() delim, text_url, url = match.groups() @@ -155,6 +162,9 @@ class Markdown: text = utils.replace_once(text, delim, tag, start) + for placeholder, code_section in placeholders.items(): + text = text.replace(placeholder, code_section) + return await self.html.parse(text) @staticmethod diff --git a/pyrogram/types/messages_and_media/__init__.py b/pyrogram/types/messages_and_media/__init__.py index 5736b8ab..85a67a98 100644 --- a/pyrogram/types/messages_and_media/__init__.py +++ b/pyrogram/types/messages_and_media/__init__.py @@ -23,6 +23,7 @@ from .available_effect import AvailableEffect from .contact import Contact from .dice import Dice from .document import Document +from .extended_media_preview import ExtendedMediaPreview from .game import Game from .gifted_premium import GiftedPremium from .giveaway import Giveaway @@ -35,6 +36,7 @@ from .media_area_channel_post import MediaAreaChannelPost from .media_area_coordinates import MediaAreaCoordinates from .message import Message from .message_entity import MessageEntity +from .paid_media import PaidMedia from .photo import Photo from .poll import Poll from .poll_option import PollOption @@ -67,7 +69,7 @@ from .story_views import StoryViews from .exported_story_link import ExportedStoryLink __all__ = [ - "Animation", "Audio", "AvailableEffect", "Contact", "Document", "Game", "GiftedPremium", "Giveaway", "GiveawayLaunched", "GiveawayResult", "LabeledPrice", "Location", "MediaArea", "MediaAreaChannelPost", "MediaAreaCoordinates", "Message", "MessageEntity", "Photo", "Thumbnail", + "Animation", "Audio", "AvailableEffect", "Contact", "Document", "ExtendedMediaPreview", "Game", "GiftedPremium", "Giveaway", "GiveawayLaunched", "GiveawayResult", "LabeledPrice", "Location", "MediaArea", "MediaAreaChannelPost", "MediaAreaCoordinates", "Message", "MessageEntity", "PaidMedia", "Photo", "Thumbnail", "StrippedThumbnail", "Poll", "PollOption", "Sticker", "StickerSet", "Venue", "Video", "VideoNote", "Voice", "WebPage", "WebPageEmpty", "WebPagePreview", "Dice", "Reaction", "WebAppData", "MessageInvoice", "MessageReactions", "ReactionCount", "ReactionType", "MessageReactionUpdated", "MessageReactionCountUpdated", "MessageStory", "Story", "StoryDeleted", "StorySkipped", "StoryViews", "StoryForwardHeader", "StoriesPrivacyRules", "ExportedStoryLink" ] diff --git a/pyrogram/types/messages_and_media/extended_media_preview.py b/pyrogram/types/messages_and_media/extended_media_preview.py new file mode 100644 index 00000000..d1f66cb4 --- /dev/null +++ b/pyrogram/types/messages_and_media/extended_media_preview.py @@ -0,0 +1,66 @@ +# Pyrofork - Telegram MTProto API Client Library for Python +# Copyright (C) 2022-present Mayuri-Chan +# +# This file is part of Pyrofork. +# +# Pyrofork is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrofork is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrofork. If not, see . + +from pyrogram import raw +from pyrogram import types +from ..object import Object + + +class ExtendedMediaPreview(Object): + """A ExtendedMediaPreview. + + Parameters: + width (``int``, *optional*): + Media Width. + + height (``int``, *optional*): + Media Height. + + thumb (:obj:`~pyrogram.types.StrippedThumbnail`, *optional*): + Media Thumbnail. + + video_duration (``int``, *optional*): + Video duration. + """ + def __init__( + self, + *, + width: int = None, + height: int = None, + thumb: "types.Thumbnail" = None, + video_duration: int = None + ): + super().__init__() + + self.width = width + self.height = height + self.thumb = thumb + self.video_duration = video_duration + + @staticmethod + def _parse(client, media: "raw.types.MessageExtendedMediaPreview") -> "ExtendedMediaPreview": + thumb = None + if media.thumb: + thumb = types.StrippedThumbnail._parse(client, media.thumb) + + return ExtendedMediaPreview( + width=media.w, + height=media.h, + thumb=thumb, + video_duration=media.video_duration + ) diff --git a/pyrogram/types/messages_and_media/message.py b/pyrogram/types/messages_and_media/message.py index 12978db9..ee4e2187 100644 --- a/pyrogram/types/messages_and_media/message.py +++ b/pyrogram/types/messages_and_media/message.py @@ -208,6 +208,9 @@ class Message(Object, Update): photo (:obj:`~pyrogram.types.Photo`, *optional*): Message is a photo, information about the photo. + paid_media (:obj:`~pyrogram.types.PaidMedia`, *optional*): + Message is a paid media, information about the paid media. + sticker (:obj:`~pyrogram.types.Sticker`, *optional*): Message is a sticker, information about the sticker. @@ -472,6 +475,7 @@ class Message(Object, Update): audio: "types.Audio" = None, document: "types.Document" = None, photo: "types.Photo" = None, + paid_media: "types.PaidMedia" = None, sticker: "types.Sticker" = None, animation: "types.Animation" = None, game: "types.Game" = None, @@ -582,6 +586,7 @@ class Message(Object, Update): self.audio = audio self.document = document self.photo = photo + self.paid_media = paid_media self.sticker = sticker self.animation = animation self.game = game @@ -859,7 +864,6 @@ class Message(Object, Update): chat=types.Chat._parse(client, message, users, chats, is_chat=True), topics=None, from_user=from_user, - sender_chat=sender_chat, service=service_type, new_chat_members=new_chat_members, chat_joined_by_request=chat_joined_by_request, @@ -895,6 +899,8 @@ class Message(Object, Update): client=client # TODO: supergroup_chat_created ) + if parsed_message.chat.type is not enums.ChatType.CHANNEL: + parsed_message.sender_chat = sender_chat if isinstance(action, raw.types.MessageActionPinMessage): try: @@ -971,6 +977,7 @@ class Message(Object, Update): forward_sender_name = forward_header.from_name photo = None + paid_media = None location = None contact = None venue = None @@ -1077,6 +1084,9 @@ class Message(Object, Update): elif isinstance(media, raw.types.MessageMediaInvoice): invoice = types.MessageInvoice._parse(media) media = enums.MessageMediaType.INVOICE + elif isinstance(media, raw.types.MessageMediaPaidMedia): + paid_media = types.PaidMedia._parse(client, media) + media_type = enums.MessageMediaType.PAID_MEDIA else: media = None @@ -1110,7 +1120,6 @@ class Message(Object, Update): chat=types.Chat._parse(client, message, users, chats, is_chat=True), topics=None, from_user=from_user, - sender_chat=sender_chat, sender_business_bot=sender_business_bot, text=( Str(message.message).init(entities) or None @@ -1151,6 +1160,7 @@ class Message(Object, Update): media_group_id=message.grouped_id, invert_media=message.invert_media, photo=photo, + paid_media=paid_media, location=location, contact=contact, venue=venue, @@ -1179,6 +1189,8 @@ class Message(Object, Update): raw=message, client=client ) + if parsed_message.chat.type is not enums.ChatType.CHANNEL: + parsed_message.sender_chat = sender_chat if message.reply_to: if isinstance(message.reply_to, raw.types.MessageReplyHeader): @@ -4505,7 +4517,12 @@ class Message(Object, Update): return await self._client.send_poll( chat_id, question=self.poll.question, - options=[opt.text for opt in self.poll.options], + options=[ + types.PollOption( + text=opt.text, + entities=opt.entities + ) for opt in self.poll.options + ], disable_notification=disable_notification, message_thread_id=message_thread_id, schedule_date=schedule_date 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..aa7ccabd --- /dev/null +++ b/pyrogram/types/messages_and_media/paid_media.py @@ -0,0 +1,78 @@ +# Pyrofork - Telegram MTProto API Client Library for Python +# Copyright (C) 2022-present Mayuri-Chan +# +# This file is part of Pyrofork. +# +# Pyrofork is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Pyrofork is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Pyrofork. If not, see . + +from typing import List, Union + +from pyrogram import raw +from pyrogram import types +from ..object import Object + + +class PaidMedia(Object): + """A PaidMedia. + + Parameters: + stars_amount (``int``): + Amount of stars. + + extended_media (List of :obj:`~pyrogram.types.Animation` | :obj:`~pyrogram.types.ExtendedMediaPreview` | :obj:`~pyrogram.types.Photo` | :obj:`~pyrogram.types.Video`, *optional*): + Extended media. + """ + def __init__( + self, + *, + stars_amount: int, + extended_media: List[ + Union[ + "types.Animation", + "types.ExtendedMediaPreview", + "types.Photo", + "types.Video" + ] + ] = None + ): + super().__init__() + + self.stars_amount = stars_amount + self.extended_media = extended_media + + @staticmethod + def _parse(client, media: "raw.types.MessageMediaPaidMedia") -> "PaidMedia": + extended_media = [] + for m in media.extended_media: + if isinstance(m, raw.types.MessageExtendedMediaPreview): + extended_media.append(types.ExtendedMediaPreview._parse(client, m)) + elif isinstance(m.media, raw.types.MessageMediaPhoto): + extended_media.append(types.Photo._parse(client, m.media.photo, m.media.ttl_seconds)) + elif isinstance(m.media, raw.types.MessageMediaDocument): + attributes = {type(i): i for i in m.media.document.attributes} + file_name = getattr( + attributes.get( + raw.types.DocumentAttributeFilename, None + ), "file_name", None + ) + if raw.types.DocumentAttributeAnimated in attributes: + video_attributes = attributes.get(raw.types.DocumentAttributeVideo, None) + extended_media.append(types.Animation._parse(client, m.media.document, video_attributes, file_name)) + else: + video_attributes = attributes[raw.types.DocumentAttributeVideo] + extended_media.append(types.Video._parse(client, m.media.document, video_attributes, file_name, m.media.ttl_seconds)) + return PaidMedia( + stars_amount=media.stars_amount, + extended_media=extended_media + ) diff --git a/pyrogram/types/user_and_chats/__init__.py b/pyrogram/types/user_and_chats/__init__.py index cc286983..74483aa3 100644 --- a/pyrogram/types/user_and_chats/__init__.py +++ b/pyrogram/types/user_and_chats/__init__.py @@ -41,6 +41,7 @@ from .chat_privileges import ChatPrivileges from .chat_reactions import ChatReactions from .dialog import Dialog from .emoji_status import EmojiStatus +from .folder import Folder from .invite_link_importer import InviteLinkImporter from .restriction import Restriction from .user import User @@ -72,6 +73,7 @@ __all__ = [ "ChatPhoto", "ChatPreview", "Dialog", + "Folder", "User", "Username", "Restriction", diff --git a/pyrogram/types/user_and_chats/folder.py b/pyrogram/types/user_and_chats/folder.py new file mode 100644 index 00000000..85f9bec6 --- /dev/null +++ b/pyrogram/types/user_and_chats/folder.py @@ -0,0 +1,490 @@ +# Pyrogram - Telegram MTProto API Client Library for Python +# Copyright (C) 2017-present Dan +# Copyright (C) 2022-present Mayuri-Chan +# +# 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 enums +from pyrogram import raw +from pyrogram import types +from pyrogram import utils +from ..object import Object + + +class Folder(Object): + """A user's folder. + + Parameters: + id (``int``): + The folder id. + + title (``str``): + The folder title. + + included_chats (List of :obj:`~pyrogram.types.Chat`, *optional*): + A list of included chats in folder. + + excluded_chats (List of :obj:`~pyrogram.types.Chat`, *optional*): + A list of excluded chats in folder. + + pinned_chats (List of :obj:`~pyrogram.types.Chat`, *optional*): + A list of pinned chats in folder. + + contacts (``bool``, *optional*): + True, if the folder includes contacts. + + non_contacts (``bool``, *optional*): + True, if the folder includes non contacts. + + groups (``bool``, *optional*): + True, if the folder includes groups. + + channels (``bool``, *optional*): + True, if the folder includes channels. + + bots (``bool``, *optional*): + True, if the folder includes bots. + + exclude_muted (``bool``, *optional*): + True, if the folder exclude muted. + + exclude_read (``bool``, *optional*): + True, if the folder exclude read. + + exclude_archived (``bool``, *optional*): + True, if the folder exclude archived. + + emoji (``str``, *optional*): + Folder emoji. + + color (:obj:`~pyrogram.enums.FolderColor`, *optional*) + Chat reply color. + """ + + def __init__( + self, + *, + client: "pyrogram.Client" = None, + id: int, + title: str, + included_chats: List["types.Chat"] = None, + excluded_chats: List["types.Chat"] = None, + pinned_chats: List["types.Chat"] = None, + contacts: bool = None, + non_contacts: bool = None, + groups: bool = None, + channels: bool = None, + bots: bool = None, + exclude_muted: bool = None, + exclude_read: bool = None, + exclude_archived: bool = None, + emoji: str = None, + color: "enums.FolderColor" = None, + has_my_invites: bool = None + ): + super().__init__(client) + + self.id = id + self.title = title + self.included_chats = included_chats + self.excluded_chats = excluded_chats + self.pinned_chats = pinned_chats + self.contacts = contacts + self.non_contacts = non_contacts + self.groups = groups + self.channels = channels + self.bots = bots + self.exclude_muted = exclude_muted + self.exclude_read = exclude_read + self.exclude_archived = exclude_archived + self.emoji = emoji + self.color = color + self.has_my_invites = has_my_invites + + @staticmethod + def _parse(client, folder: "raw.types.DialogFilter", users, chats) -> "Folder": + included_chats = [] + excluded_chats = [] + pinned_chats = [] + + for peer in folder.include_peers: + try: + included_chats.append(types.Chat._parse_dialog(client, peer, users, chats)) + except KeyError: + pass + + if getattr(folder, "exclude_peers", None): + for peer in folder.exclude_peers: + try: + excluded_chats.append(types.Chat._parse_dialog(client, peer, users, chats)) + except KeyError: + pass + + for peer in folder.pinned_peers: + try: + pinned_chats.append(types.Chat._parse_dialog(client, peer, users, chats)) + except KeyError: + pass + + return Folder( + id=folder.id, + title=folder.title, + included_chats=types.List(included_chats) or None, + excluded_chats=types.List(excluded_chats) or None, + pinned_chats=types.List(pinned_chats) or None, + contacts=getattr(folder, "contacts", None), + non_contacts=getattr(folder, "non_contacts", None), + groups=getattr(folder, "groups", None), + channels=getattr(folder, "broadcasts", None), + bots=getattr(folder, "bots", None), + exclude_muted=getattr(folder, "exclude_muted", None), + exclude_read=getattr(folder, "exclude_read", None), + exclude_archived=getattr(folder, "exclude_archived", None), + emoji=folder.emoticon or None, + color=enums.FolderColor(getattr(folder, "color", None)), + has_my_invites=getattr(folder, "has_my_invites", None), + client=client + ) + + async def delete(self): + """Bound method *delete* of :obj:`~pyrogram.types.Folder`. + + Use as a shortcut for: + + .. code-block:: python + + await client.delete_folder(123456789) + + Example: + .. code-block:: python + + await folder.delete() + + Returns: + True on success. + """ + + return await self._client.delete_folder(self.id) + + async def update( + self, + included_chats: List[Union[int, str]] = None, + excluded_chats: List[Union[int, str]] = None, + pinned_chats: List[Union[int, str]] = None, + title: str = None, + contacts: bool = None, + non_contacts: bool = None, + groups: bool = None, + channels: bool = None, + bots: bool = None, + exclude_muted: bool = None, + exclude_read: bool = None, + exclude_archived: bool = None, + emoji: str = None, + color: "enums.FolderColor" = None + ): + """Bound method *update_peers* of :obj:`~pyrogram.types.Folder`. + + Use as a shortcut for: + + .. code-block:: python + + await client.update_folder( + folder_id, + title="New folder", + included_chats=["me"] + ) + + Example: + .. code-block:: python + + await folder.update(included_chats=["me"]) + + Parameters: + included_chats (``int`` | ``str`` | List of ``int`` or ``str``, *optional*): + Users or chats that should added in the folder + You can pass an ID (int), username (str) or phone number (str). + Multiple users can be added by passing a list of IDs, usernames or phone numbers. + + excluded_chats (``int`` | ``str`` | List of ``int`` or ``str``, *optional*): + Users or chats that should excluded from the folder + You can pass an ID (int), username (str) or phone number (str). + Multiple users can be added by passing a list of IDs, usernames or phone numbers. + + pinned_chats (``int`` | ``str`` | List of ``int`` or ``str``, *optional*): + Users or chats that should pinned in the folder + You can pass an ID (int), username (str) or phone number (str). + Multiple users can be added by passing a list of IDs, usernames or phone numbers. + + title (``str``, *optional*): + A folder title was changed to this value. + + contacts (``bool``, *optional*): + Pass True if folder should contain contacts. + + non_contacts (``bool``, *optional*): + Pass True if folder should contain non contacts. + + groups (``bool``, *optional*): + Pass True if folder should contain groups. + + channels (``bool``, *optional*): + Pass True if folder should contain channels. + + bots (``bool``, *optional*): + Pass True if folder should contain bots. + + exclude_muted (``bool``, *optional*): + Pass True if folder should exclude muted users. + + exclude_archived (``bool``, *optional*): + Pass True if folder should exclude archived users. + + emoji (``str``, *optional*): + Folder emoji. + Pass None to leave the folder icon as default. + + color (:obj:`~pyrogram.enums.FolderColor`, *optional*): + Color type. + Pass :obj:`~pyrogram.enums.FolderColor` to set folder color. + + Returns: + True on success. + """ + if not included_chats: + included_chats = [i.id for i in self.included_chats or []] + + if not included_chats: + excluded_chats = [i.id for i in self.excluded_chats or []] + + if not included_chats: + pinned_chats = [i.id for i in self.pinned_chats or []] + + return await self._client.update_folder( + folder_id=self.id, + title=title or self.title, + included_chats=included_chats, + excluded_chats=excluded_chats, + pinned_chats=pinned_chats, + contacts=contacts or self.contacts, + non_contacts=non_contacts or self.non_contacts, + groups=groups or self.groups, + channels=channels or self.channels, + bots=bots or self.bots, + exclude_muted=exclude_muted or self.exclude_muted, + exclude_read=exclude_read or self.exclude_read, + exclude_archived=exclude_archived or self.exclude_archived, + emoji=emoji or self.emoji, + color=color or self.color + ) + + async def include_chat(self, chat_id: Union[int, str]): + """Bound method *include_chat* of :obj:`~pyrogram.types.Folder`. + + Use as a shortcut for: + + .. code-block:: python + + await client.update_folder( + folder_id=123456789, + included_chats=[chat_id], + excluded_chats=[...], + pinned_chats=[...] + ) + + Example: + .. code-block:: python + + await folder.include_chat(chat_id) + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier for the target chat or username of the target user/channel/supergroup + (in the format @username). + + Returns: + True on success. + """ + + return await self.update( + included_chats=[i.id for i in self.included_chats or []] + [chat_id], + excluded_chats=[i.id for i in self.excluded_chats or []], + pinned_chats=[i.id for i in self.pinned_chats or []] + ) + + async def exclude_chat(self, chat_id: Union[int, str]): + """Bound method *exclude_chat* of :obj:`~pyrogram.types.Folder`. + + Use as a shortcut for: + + .. code-block:: python + + await client.update_folder( + folder_id=123456789, + included_chats=[...], + excluded_chats=[chat_id], + pinned_chats=[...] + ) + + Example: + .. code-block:: python + + await folder.exclude_chat(chat_id) + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier for the target chat or username of the target user/channel/supergroup + (in the format @username). + + Returns: + True on success. + """ + + return await self.update( + included_chats=[i.id for i in self.included_chats or []], + excluded_chats=[i.id for i in self.excluded_chats or []] + [chat_id], + pinned_chats=[i.id for i in self.pinned_chats or []], + ) + + async def update_color(self, color: "enums.FolderColor"): + """Bound method *update_color* of :obj:`~pyrogram.types.Folder`. + + Use as a shortcut for: + + .. code-block:: python + + await client.update_folder( + folder_id=123456789, + included_chats=[chat_id], + excluded_chats=[chat_id], + pinned_chats=[...], + color=color + ) + + Example: + .. code-block:: python + + await folder.update_color(enums.FolderColor.RED) + + Parameters: + color (:obj:`~pyrogram.enums.FolderColor`, *optional*): + Color type. + Pass :obj:`~pyrogram.enums.FolderColor` to set folder color. + + Returns: + True on success. + """ + + return await self.update( + color=color + ) + + async def pin_chat(self, chat_id: Union[int, str]): + """Bound method *pin_chat* of :obj:`~pyrogram.types.Folder`. + + Use as a shortcut for: + + .. code-block:: python + + await client.update_folder( + folder_id=123456789, + included_chats=[chat_id], + excluded_chats=[chat_id], + pinned_chats=[...] + ) + + Example: + .. code-block:: python + + await folder.pin_chat(chat_id) + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier for the target chat or username of the target user/channel/supergroup + (in the format @username). + + Returns: + True on success. + """ + + return await self.update( + included_chats=[i.id for i in self.included_chats or []] + [chat_id], + excluded_chats=[i.id for i in self.excluded_chats or []], + pinned_chats=[i.id for i in self.pinned_chats or []] + [chat_id] + ) + + async def remove_chat(self, chat_id: Union[int, str]): + """Bound method *remove_chat* of :obj:`~pyrogram.types.Folder`. + + Remove chat from included, excluded and pinned chats. + + Use as a shortcut for: + + .. code-block:: python + + await client.update_folder( + folder_id=123456789, + included_chats=[...], + excluded_chats=[...], + pinned_chats=[...] + ) + + Example: + .. code-block:: python + + await folder.remove_chat(chat_id) + + Parameters: + chat_id (``int`` | ``str``): + Unique identifier for the target chat or username of the target user/channel/supergroup + (in the format @username). + + Returns: + True on success. + """ + peer = await self._client.resolve_peer(chat_id) + peer_id = utils.get_peer_id(peer) + + return await self.update( + included_chats=[i.id for i in self.included_chats or [] if peer_id != i.id], + excluded_chats=[i.id for i in self.excluded_chats or [] if peer_id != i.id], + pinned_chats=[i.id for i in self.pinned_chats or [] if peer_id != i.id] + ) + + async def export_link(self): + """Bound method *export_link* of :obj:`~pyrogram.types.Folder`. + + Use as a shortcut for: + + .. code-block:: python + + await client.export_folder_link(123456789) + + Example: + .. code-block:: python + + await folder.export_link() + + Returns: + ``str``: On success, a link to the folder as string is returned. + """ + + return await self._client.export_folder_link( + folder_id=self.id + )