This commit is contained in:
Yasir Aris M 2024-12-23 07:36:46 +07:00
commit 56e9173579
23 changed files with 748 additions and 103 deletions

View file

@ -26,6 +26,14 @@ inputPeerChannel#27bcbbfc channel_id:long access_hash:long = InputPeer;
inputPeerUserFromMessage#a87b0a1c peer:InputPeer msg_id:int user_id:long = InputPeer; inputPeerUserFromMessage#a87b0a1c peer:InputPeer msg_id:int user_id:long = InputPeer;
inputPeerChannelFromMessage#bd2a0840 peer:InputPeer msg_id:int channel_id:long = InputPeer; inputPeerChannelFromMessage#bd2a0840 peer:InputPeer msg_id:int channel_id:long = InputPeer;
inputPeerEmpty#7f3b18ea = InputPeer;
inputPeerSelf#7da07ec9 = InputPeer;
inputPeerChat#35a95cb9 chat_id:long = InputPeer;
inputPeerUser#dde8a54c user_id:long access_hash:long = InputPeer;
inputPeerChannel#27bcbbfc channel_id:long access_hash:long = InputPeer;
inputPeerUserFromMessage#a87b0a1c peer:InputPeer msg_id:int user_id:long = InputPeer;
inputPeerChannelFromMessage#bd2a0840 peer:InputPeer msg_id:int channel_id:long = InputPeer;
inputUserEmpty#b98886cf = InputUser; inputUserEmpty#b98886cf = InputUser;
inputUserSelf#f7c1b13f = InputUser; inputUserSelf#f7c1b13f = InputUser;
inputUser#f21158c6 user_id:long access_hash:long = InputUser; inputUser#f21158c6 user_id:long access_hash:long = InputUser;
@ -244,7 +252,7 @@ inputReportReasonFake#f5ddd6e7 = ReportReason;
inputReportReasonIllegalDrugs#a8eb2be = ReportReason; inputReportReasonIllegalDrugs#a8eb2be = ReportReason;
inputReportReasonPersonalDetails#9ec7863d = ReportReason; inputReportReasonPersonalDetails#9ec7863d = ReportReason;
userFull#1f58e369 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true voice_messages_forbidden:flags.20?true translations_disabled:flags.23?true stories_pinned_available:flags.26?true blocked_my_stories_from:flags.27?true wallpaper_overridden:flags.28?true contact_require_premium:flags.29?true read_dates_private:flags.30?true flags2:# sponsored_enabled:flags2.7?true can_view_revenue:flags2.9?true bot_can_manage_emoji_status:flags2.10?true id:long about:flags.1?string settings:PeerSettings personal_photo:flags.21?Photo profile_photo:flags.2?Photo fallback_photo:flags.22?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int theme_emoticon:flags.15?string private_forward_name:flags.16?string bot_group_admin_rights:flags.17?ChatAdminRights bot_broadcast_admin_rights:flags.18?ChatAdminRights premium_gifts:flags.19?Vector<PremiumGiftOption> wallpaper:flags.24?WallPaper stories:flags.25?PeerStories business_work_hours:flags2.0?BusinessWorkHours business_location:flags2.1?BusinessLocation business_greeting_message:flags2.2?BusinessGreetingMessage business_away_message:flags2.3?BusinessAwayMessage business_intro:flags2.4?BusinessIntro birthday:flags2.5?Birthday personal_channel_id:flags2.6?long personal_channel_message:flags2.6?int stargifts_count:flags2.8?int = UserFull; userFull#979d2376 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true voice_messages_forbidden:flags.20?true translations_disabled:flags.23?true stories_pinned_available:flags.26?true blocked_my_stories_from:flags.27?true wallpaper_overridden:flags.28?true contact_require_premium:flags.29?true read_dates_private:flags.30?true flags2:# sponsored_enabled:flags2.7?true can_view_revenue:flags2.9?true bot_can_manage_emoji_status:flags2.10?true id:long about:flags.1?string settings:PeerSettings personal_photo:flags.21?Photo profile_photo:flags.2?Photo fallback_photo:flags.22?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int theme_emoticon:flags.15?string private_forward_name:flags.16?string bot_group_admin_rights:flags.17?ChatAdminRights bot_broadcast_admin_rights:flags.18?ChatAdminRights premium_gifts:flags.19?Vector<PremiumGiftOption> wallpaper:flags.24?WallPaper stories:flags.25?PeerStories business_work_hours:flags2.0?BusinessWorkHours business_location:flags2.1?BusinessLocation business_greeting_message:flags2.2?BusinessGreetingMessage business_away_message:flags2.3?BusinessAwayMessage business_intro:flags2.4?BusinessIntro birthday:flags2.5?Birthday personal_channel_id:flags2.6?long personal_channel_message:flags2.6?int stargifts_count:flags2.8?int starref_program:flags2.11?StarRefProgram = UserFull;
contact#145ade0b user_id:long mutual:Bool = Contact; contact#145ade0b user_id:long mutual:Bool = Contact;
@ -430,7 +438,7 @@ updateBotEditBusinessMessage#7df587c flags:# connection_id:string message:Messag
updateBotDeleteBusinessMessage#a02a982e connection_id:string peer:Peer messages:Vector<int> qts:int = Update; updateBotDeleteBusinessMessage#a02a982e connection_id:string peer:Peer messages:Vector<int> qts:int = Update;
updateNewStoryReaction#1824e40b story_id:int peer:Peer reaction:Reaction = Update; updateNewStoryReaction#1824e40b story_id:int peer:Peer reaction:Reaction = Update;
updateBroadcastRevenueTransactions#dfd961f5 peer:Peer balances:BroadcastRevenueBalances = Update; updateBroadcastRevenueTransactions#dfd961f5 peer:Peer balances:BroadcastRevenueBalances = Update;
updateStarsBalance#fb85198 balance:long = Update; updateStarsBalance#4e80a379 balance:StarsAmount = Update;
updateBusinessBotCallbackQuery#1ea2fda7 flags:# query_id:long user_id:long connection_id:string message:Message reply_to_message:flags.2?Message chat_instance:long data:flags.0?bytes = Update; updateBusinessBotCallbackQuery#1ea2fda7 flags:# query_id:long user_id:long connection_id:string message:Message reply_to_message:flags.2?Message chat_instance:long data:flags.0?bytes = Update;
updateStarsRevenueStatus#a584b019 peer:Peer status:StarsRevenueStatus = Update; updateStarsRevenueStatus#a584b019 peer:Peer status:StarsRevenueStatus = Update;
updateBotPurchasedPaidMedia#283bd312 user_id:long payload:string qts:int = Update; updateBotPurchasedPaidMedia#283bd312 user_id:long payload:string qts:int = Update;
@ -1840,9 +1848,9 @@ starsTransactionPeerAPI#f9677aad = StarsTransactionPeer;
starsTopupOption#bd915c0 flags:# extended:flags.1?true stars:long store_product:flags.0?string currency:string amount:long = StarsTopupOption; starsTopupOption#bd915c0 flags:# extended:flags.1?true stars:long store_product:flags.0?string currency:string amount:long = StarsTopupOption;
starsTransaction#35d4f276 flags:# refund:flags.3?true pending:flags.4?true failed:flags.6?true gift:flags.10?true reaction:flags.11?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<MessageMedia> subscription_period:flags.12?int giveaway_post_id:flags.13?int stargift:flags.14?StarGift floodskip_number:flags.15?int = StarsTransaction; starsTransaction#64dfc926 flags:# refund:flags.3?true pending:flags.4?true failed:flags.6?true gift:flags.10?true reaction:flags.11?true id:string stars:StarsAmount 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<MessageMedia> subscription_period:flags.12?int giveaway_post_id:flags.13?int stargift:flags.14?StarGift floodskip_number:flags.15?int starref_commission_permille:flags.16?int starref_peer:flags.17?Peer starref_amount:flags.17?StarsAmount = StarsTransaction;
payments.starsStatus#bbfa316c flags:# balance:long subscriptions:flags.1?Vector<StarsSubscription> subscriptions_next_offset:flags.2?string subscriptions_missing_balance:flags.4?long history:flags.3?Vector<StarsTransaction> next_offset:flags.0?string chats:Vector<Chat> users:Vector<User> = payments.StarsStatus; payments.starsStatus#6c9ce8ed flags:# balance:StarsAmount subscriptions:flags.1?Vector<StarsSubscription> subscriptions_next_offset:flags.2?string subscriptions_missing_balance:flags.4?long history:flags.3?Vector<StarsTransaction> next_offset:flags.0?string chats:Vector<Chat> users:Vector<User> = payments.StarsStatus;
foundStory#e87acbc0 peer:Peer story:StoryItem = FoundStory; foundStory#e87acbc0 peer:Peer story:StoryItem = FoundStory;
@ -1850,7 +1858,7 @@ stories.foundStories#e2de7737 flags:# count:int stories:Vector<FoundStory> next_
geoPointAddress#de4c5d93 flags:# country_iso2:string state:flags.0?string city:flags.1?string street:flags.2?string = GeoPointAddress; geoPointAddress#de4c5d93 flags:# country_iso2:string state:flags.0?string city:flags.1?string street:flags.2?string = GeoPointAddress;
starsRevenueStatus#79342946 flags:# withdrawal_enabled:flags.0?true current_balance:long available_balance:long overall_revenue:long next_withdrawal_at:flags.1?int = StarsRevenueStatus; starsRevenueStatus#febe5491 flags:# withdrawal_enabled:flags.0?true current_balance:StarsAmount available_balance:StarsAmount overall_revenue:StarsAmount next_withdrawal_at:flags.1?int = StarsRevenueStatus;
payments.starsRevenueStats#c92bb73b revenue_graph:StatsGraph status:StarsRevenueStatus usd_rate:double = payments.StarsRevenueStats; payments.starsRevenueStats#c92bb73b revenue_graph:StatsGraph status:StarsRevenueStatus usd_rate:double = payments.StarsRevenueStats;
@ -1882,18 +1890,36 @@ starGift#49c577cd flags:# limited:flags.0?true sold_out:flags.1?true birthday:fl
payments.starGiftsNotModified#a388a368 = payments.StarGifts; payments.starGiftsNotModified#a388a368 = payments.StarGifts;
payments.starGifts#901689ea hash:int gifts:Vector<StarGift> = payments.StarGifts; payments.starGifts#901689ea hash:int gifts:Vector<StarGift> = payments.StarGifts;
userStarGift#eea49a6e flags:# name_hidden:flags.0?true unsaved:flags.5?true from_id:flags.1?long date:int gift:StarGift message:flags.2?TextWithEntities msg_id:flags.3?int convert_stars:flags.4?long = UserStarGift; userStarGift#eea49a6e flags:# name_hidden:flags.0?true unsaved:flags.5?true from_id:flags.1?long date:int gift:StarGift message:flags.2?TextWithEntities msg_id:flags.3?int convert_stars:flags.4?long = UserStarGift;
payments.userStarGifts#6b65b517 flags:# count:int gifts:Vector<UserStarGift> next_offset:flags.0?string users:Vector<User> = payments.UserStarGifts; payments.userStarGifts#6b65b517 flags:# count:int gifts:Vector<UserStarGift> next_offset:flags.0?string users:Vector<User> = payments.UserStarGifts;
messageReportOption#7903e3d9 text:string option:bytes = MessageReportOption; messageReportOption#7903e3d9 text:string option:bytes = MessageReportOption;
reportResultChooseOption#f0e4e0b6 title:string options:Vector<MessageReportOption> = ReportResult; reportResultChooseOption#f0e4e0b6 title:string options:Vector<MessageReportOption> = ReportResult;
reportResultAddComment#6f09ac31 flags:# optional:flags.0?true option:bytes = ReportResult; reportResultAddComment#6f09ac31 flags:# optional:flags.0?true option:bytes = ReportResult;
reportResultReported#8db33c4b = ReportResult; reportResultReported#8db33c4b = ReportResult;
messages.botPreparedInlineMessage#8ecf0511 id:string expire_date:int = messages.BotPreparedInlineMessage; messages.botPreparedInlineMessage#8ecf0511 id:string expire_date:int = messages.BotPreparedInlineMessage;
messages.preparedInlineMessage#ff57708d query_id:long result:BotInlineResult peer_types:Vector<InlineQueryPeerType> cache_time:int users:Vector<User> = messages.PreparedInlineMessage; messages.preparedInlineMessage#ff57708d query_id:long result:BotInlineResult peer_types:Vector<InlineQueryPeerType> cache_time:int users:Vector<User> = messages.PreparedInlineMessage;
botAppSettings#c99b1950 flags:# placeholder_path:flags.0?bytes background_color:flags.1?int background_dark_color:flags.2?int header_color:flags.3?int header_dark_color:flags.4?int = BotAppSettings; botAppSettings#c99b1950 flags:# placeholder_path:flags.0?bytes background_color:flags.1?int background_dark_color:flags.2?int header_color:flags.3?int header_dark_color:flags.4?int = BotAppSettings;
starRefProgram#dd0c66f2 flags:# bot_id:long commission_permille:int duration_months:flags.0?int end_date:flags.1?int daily_revenue_per_user:flags.2?StarsAmount = StarRefProgram;
connectedBotStarRef#19a13f71 flags:# revoked:flags.1?true url:string date:int bot_id:long commission_permille:int duration_months:flags.0?int participants:long revenue:long = ConnectedBotStarRef;
payments.connectedStarRefBots#98d5ea1d count:int connected_bots:Vector<ConnectedBotStarRef> users:Vector<User> = payments.ConnectedStarRefBots;
payments.suggestedStarRefBots#b4d5d859 flags:# count:int suggested_bots:Vector<StarRefProgram> users:Vector<User> next_offset:flags.0?string = payments.SuggestedStarRefBots;
starsAmount#bbb6b4a3 amount:long nanos:int = StarsAmount;
messages.foundStickersNotModified#6010c534 flags:# next_offset:flags.0?int = messages.FoundStickers;
messages.foundStickers#82c9e290 flags:# next_offset:flags.0?int hash:long stickers:Vector<Document> = messages.FoundStickers;
---functions--- ---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X; invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
@ -2059,7 +2085,7 @@ contacts.block#2e2e8734 flags:# my_stories_from:flags.0?true id:InputPeer = Bool
contacts.unblock#b550d328 flags:# my_stories_from:flags.0?true id:InputPeer = Bool; contacts.unblock#b550d328 flags:# my_stories_from:flags.0?true id:InputPeer = Bool;
contacts.getBlocked#9a868f80 flags:# my_stories_from:flags.0?true offset:int limit:int = contacts.Blocked; contacts.getBlocked#9a868f80 flags:# my_stories_from:flags.0?true offset:int limit:int = contacts.Blocked;
contacts.search#11f812d8 q:string limit:int = contacts.Found; contacts.search#11f812d8 q:string limit:int = contacts.Found;
contacts.resolveUsername#f93ccba3 username:string = contacts.ResolvedPeer; contacts.resolveUsername#725afbbc flags:# username:string referer:flags.0?string = contacts.ResolvedPeer;
contacts.getTopPeers#973478b6 flags:# correspondents:flags.0?true bots_pm:flags.1?true bots_inline:flags.2?true phone_calls:flags.3?true forward_users:flags.4?true forward_chats:flags.5?true groups:flags.10?true channels:flags.15?true bots_app:flags.16?true offset:int limit:int hash:long = contacts.TopPeers; contacts.getTopPeers#973478b6 flags:# correspondents:flags.0?true bots_pm:flags.1?true bots_inline:flags.2?true phone_calls:flags.3?true forward_users:flags.4?true forward_chats:flags.5?true groups:flags.10?true channels:flags.15?true bots_app:flags.16?true offset:int limit:int hash:long = contacts.TopPeers;
contacts.resetTopPeerRating#1ae373ac category:TopPeerCategory peer:InputPeer = Bool; contacts.resetTopPeerRating#1ae373ac category:TopPeerCategory peer:InputPeer = Bool;
contacts.resetSaved#879537f1 = Bool; contacts.resetSaved#879537f1 = Bool;
@ -2299,6 +2325,7 @@ messages.reportSponsoredMessage#1af3dbb8 peer:InputPeer random_id:bytes option:b
messages.getSponsoredMessages#9bd2f439 peer:InputPeer = messages.SponsoredMessages; messages.getSponsoredMessages#9bd2f439 peer:InputPeer = messages.SponsoredMessages;
messages.savePreparedInlineMessage#f21f7f2f flags:# result:InputBotInlineResult user_id:InputUser peer_types:flags.0?Vector<InlineQueryPeerType> = messages.BotPreparedInlineMessage; messages.savePreparedInlineMessage#f21f7f2f flags:# result:InputBotInlineResult user_id:InputUser peer_types:flags.0?Vector<InlineQueryPeerType> = messages.BotPreparedInlineMessage;
messages.getPreparedInlineMessage#857ebdb8 bot:InputUser id:string = messages.PreparedInlineMessage; messages.getPreparedInlineMessage#857ebdb8 bot:InputUser id:string = messages.PreparedInlineMessage;
messages.searchStickers#29b1c66a flags:# emojis:flags.0?true q:string emoticon:string lang_code:Vector<string> offset:int limit:int hash:long = messages.FoundStickers;
updates.getState#edd4882a = updates.State; 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; 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;
@ -2433,6 +2460,8 @@ bots.getPreviewMedias#a2a5594d bot:InputUser = Vector<BotPreviewMedia>;
bots.updateUserEmojiStatus#ed9f30c5 user_id:InputUser emoji_status:EmojiStatus = Bool; bots.updateUserEmojiStatus#ed9f30c5 user_id:InputUser emoji_status:EmojiStatus = Bool;
bots.toggleUserEmojiStatusPermission#6de6392 bot:InputUser enabled:Bool = Bool; bots.toggleUserEmojiStatusPermission#6de6392 bot:InputUser enabled:Bool = Bool;
bots.checkDownloadFileParams#50077589 bot:InputUser file_name:string url:string = Bool; bots.checkDownloadFileParams#50077589 bot:InputUser file_name:string url:string = Bool;
bots.getAdminedBots#b0711d83 = Vector<User>;
bots.updateStarRefProgram#778b5ab3 flags:# bot:InputUser commission_permille:int duration_months:flags.0?int = StarRefProgram;
payments.getPaymentForm#37148dbb flags:# invoice:InputInvoice theme_params:flags.0?DataJSON = payments.PaymentForm; payments.getPaymentForm#37148dbb flags:# invoice:InputInvoice theme_params:flags.0?DataJSON = payments.PaymentForm;
payments.getPaymentReceipt#2478d1cc peer:InputPeer msg_id:int = payments.PaymentReceipt; payments.getPaymentReceipt#2478d1cc peer:InputPeer msg_id:int = payments.PaymentReceipt;
@ -2468,7 +2497,12 @@ payments.getStarGifts#c4563590 hash:int = payments.StarGifts;
payments.getUserStarGifts#5e72c7e1 user_id:InputUser offset:string limit:int = payments.UserStarGifts; payments.getUserStarGifts#5e72c7e1 user_id:InputUser offset:string limit:int = payments.UserStarGifts;
payments.saveStarGift#87acf08e flags:# unsave:flags.0?true user_id:InputUser msg_id:int = Bool; payments.saveStarGift#87acf08e flags:# unsave:flags.0?true user_id:InputUser msg_id:int = Bool;
payments.convertStarGift#421e027 user_id:InputUser msg_id:int = Bool; payments.convertStarGift#421e027 user_id:InputUser msg_id:int = Bool;
payments.botCancelStarsSubscription#57f9ece6 flags:# restore:flags.0?true user_id:InputUser invoice_slug:flags.1?string charge_id:flags.2?string = Bool; payments.botCancelStarsSubscription#6dfa0622 flags:# restore:flags.0?true user_id:InputUser charge_id:string = Bool;
payments.getConnectedStarRefBots#5869a553 flags:# peer:InputPeer offset_date:flags.2?int offset_link:flags.2?string limit:int = payments.ConnectedStarRefBots;
payments.getConnectedStarRefBot#b7d998f0 peer:InputPeer bot:InputUser = payments.ConnectedStarRefBots;
payments.getSuggestedStarRefBots#d6b48f7 flags:# order_by_revenue:flags.0?true order_by_date:flags.1?true peer:InputPeer offset:string limit:int = payments.SuggestedStarRefBots;
payments.connectStarRefBot#7ed5348a peer:InputPeer bot:InputUser = payments.ConnectedStarRefBots;
payments.editConnectedStarRefBot#e4fca4a3 flags:# revoked:flags.0?true peer:InputPeer link:string = payments.ConnectedStarRefBots;
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<InputStickerSetItem> software:flags.3?string = messages.StickerSet; 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<InputStickerSetItem> software:flags.3?string = messages.StickerSet;
stickers.removeStickerFromSet#f7760f51 sticker:InputDocument = messages.StickerSet; stickers.removeStickerFromSet#f7760f51 sticker:InputDocument = messages.StickerSet;
@ -2588,4 +2622,4 @@ smsjobs.finishJob#4f1ebf24 flags:# job_id:string error:flags.0?string = Bool;
fragment.getCollectibleInfo#be1e85ba collectible:InputCollectible = fragment.CollectibleInfo; fragment.getCollectibleInfo#be1e85ba collectible:InputCollectible = fragment.CollectibleInfo;
// LAYER 194 // LAYER 195

View file

@ -165,6 +165,7 @@ def pyrogram_api():
messages=""" messages="""
Messages Messages
send_message send_message
forward_media_group
forward_messages forward_messages
copy_message copy_message
copy_media_group copy_media_group
@ -397,6 +398,7 @@ def pyrogram_api():
sell_gift sell_gift
send_gift send_gift
toggle_gift_is_saved toggle_gift_is_saved
get_owned_bots
""", """,
business=""" business="""
Telegram Business Telegram Business
@ -511,6 +513,7 @@ def pyrogram_api():
Audio Audio
AvailableEffect AvailableEffect
Document Document
AlternativeVideo
Animation Animation
Video Video
Voice Voice

View file

@ -18,7 +18,7 @@
# along with Pyrofork. If not, see <http://www.gnu.org/licenses/>. # along with Pyrofork. If not, see <http://www.gnu.org/licenses/>.
__fork_name__ = "PyroFork" __fork_name__ = "PyroFork"
__version__ = "2.3.52" __version__ = "2.3.53"
__license__ = "GNU Lesser General Public License v3.0 (LGPL-3.0)" __license__ = "GNU Lesser General Public License v3.0 (LGPL-3.0)"
__copyright__ = "Copyright (C) 2022-present Mayuri-Chan <https://github.com/Mayuri-Chan>" __copyright__ = "Copyright (C) 2022-present Mayuri-Chan <https://github.com/Mayuri-Chan>"

View file

@ -23,9 +23,8 @@ import logging
from collections import OrderedDict from collections import OrderedDict
import pyrogram import pyrogram
from pyrogram import errors from pyrogram import errors, raw, types, utils
from pyrogram import utils from pyrogram.handlers.handler import Handler
from pyrogram import raw
from pyrogram.handlers import ( from pyrogram.handlers import (
BotBusinessConnectHandler, BotBusinessConnectHandler,
BotBusinessMessageHandler, BotBusinessMessageHandler,
@ -33,6 +32,7 @@ from pyrogram.handlers import (
MessageHandler, MessageHandler,
EditedMessageHandler, EditedMessageHandler,
EditedBotBusinessMessageHandler, EditedBotBusinessMessageHandler,
ErrorHandler,
DeletedMessagesHandler, DeletedMessagesHandler,
DeletedBotBusinessMessagesHandler, DeletedBotBusinessMessagesHandler,
MessageReactionUpdatedHandler, MessageReactionUpdatedHandler,
@ -97,6 +97,7 @@ class Dispatcher:
self.handler_worker_tasks = [] self.handler_worker_tasks = []
self.locks_list = [] self.locks_list = []
self.error_handlers = []
self.updates_queue = asyncio.Queue() self.updates_queue = asyncio.Queue()
self.groups = OrderedDict() self.groups = OrderedDict()
@ -286,6 +287,7 @@ class Dispatcher:
self.handler_worker_tasks.clear() self.handler_worker_tasks.clear()
self.groups.clear() self.groups.clear()
self.error_handlers.clear()
log.info("Stopped %s HandlerTasks", self.client.workers) log.info("Stopped %s HandlerTasks", self.client.workers)
@ -295,11 +297,14 @@ class Dispatcher:
await lock.acquire() await lock.acquire()
try: try:
if group not in self.groups: if isinstance(handler, ErrorHandler):
self.groups[group] = [] if handler not in self.error_handlers:
self.groups = OrderedDict(sorted(self.groups.items())) self.error_handlers.append(handler)
else:
self.groups[group].append(handler) if group not in self.groups:
self.groups[group] = []
self.groups = OrderedDict(sorted(self.groups.items()))
self.groups[group].append(handler)
finally: finally:
for lock in self.locks_list: for lock in self.locks_list:
lock.release() lock.release()
@ -312,71 +317,94 @@ class Dispatcher:
await lock.acquire() await lock.acquire()
try: try:
if group not in self.groups: if isinstance(handler, ErrorHandler):
raise ValueError(f"Group {group} does not exist. Handler was not removed.") if handler not in self.error_handlers:
raise ValueError(
self.groups[group].remove(handler) f"Error handler {handler} does not exist. Handler was not removed."
)
self.error_handlers.remove(handler)
else:
if group not in self.groups:
raise ValueError(f"Group {group} does not exist. Handler was not removed.")
self.groups[group].remove(handler)
finally: finally:
for lock in self.locks_list: for lock in self.locks_list:
lock.release() lock.release()
self.loop.create_task(fn()) self.loop.create_task(fn())
async def handler_worker(self, lock): async def handler_worker(self, lock: asyncio.Lock):
while True: while True:
packet = await self.updates_queue.get() packet = await self.updates_queue.get()
if packet is None: if packet is None:
break break
await self._process_packet(packet, lock)
try: async def _process_packet(
update, users, chats = packet self,
parser = self.update_parsers.get(type(update), None) packet: tuple[raw.core.TLObject, dict[int, types.Update], dict[int, types.Update]],
lock: asyncio.Lock,
):
try:
update, users, chats = packet
parser = self.update_parsers.get(type(update))
parsed_update, handler_type = ( if parser is not None:
await parser(update, users, chats) parsed_result = parser(update, users, chats)
if parser is not None if inspect.isawaitable(parsed_result):
else (None, type(None)) parsed_update, handler_type = await parsed_result
) else:
parsed_update, handler_type = parsed_result
async with lock: else:
for group in self.groups.values(): parsed_update, handler_type = (None, type(None))
for handler in group:
args = None
if isinstance(handler, handler_type):
try:
if await handler.check(self.client, parsed_update):
args = (parsed_update,)
except Exception as e:
log.exception(e)
continue
async with lock:
for group in self.groups.values():
for handler in group:
try:
if parsed_update is not None:
if isinstance(handler, handler_type) and await handler.check(
self.client, parsed_update
):
await self._execute_callback(handler, parsed_update)
break
elif isinstance(handler, RawUpdateHandler): elif isinstance(handler, RawUpdateHandler):
args = (update, users, chats) await self._execute_callback(handler, update, users, chats)
break
if args is None: except (pyrogram.StopPropagation, pyrogram.ContinuePropagation) as e:
continue if isinstance(e, pyrogram.StopPropagation):
try:
if inspect.iscoroutinefunction(handler.callback):
await handler.callback(self.client, *args)
else:
await self.loop.run_in_executor(
self.client.executor,
handler.callback,
self.client,
*args
)
except pyrogram.StopPropagation:
raise raise
except pyrogram.ContinuePropagation: except Exception as exception:
continue if parsed_update is not None:
except Exception as e: await self._handle_exception(parsed_update, exception)
log.exception(e) except pyrogram.StopPropagation:
pass
except Exception as e:
log.exception(e)
finally:
self.updates_queue.task_done()
break async def _handle_exception(self, parsed_update: types.Update, exception: Exception):
handled_error = False
for error_handler in self.error_handlers:
try:
if await error_handler.check(self.client, parsed_update, exception):
handled_error = True
break
except pyrogram.StopPropagation: except pyrogram.StopPropagation:
pass raise
except Exception as e: except pyrogram.ContinuePropagation:
log.exception(e) continue
except Exception as inner_exception:
log.exception("Error in error handler: %s", inner_exception)
if not handled_error:
log.exception("Unhandled exception: %s", exception)
async def _execute_callback(self, handler: Handler, *args):
if inspect.iscoroutinefunction(handler.callback):
await handler.callback(self.client, *args)
else:
await self.client.loop.run_in_executor(
self.client.executor, handler.callback, self.client, *args
)

View file

@ -29,6 +29,7 @@ from .deleted_bot_business_messages_handler import DeletedBotBusinessMessagesHan
from .disconnect_handler import DisconnectHandler from .disconnect_handler import DisconnectHandler
from .edited_message_handler import EditedMessageHandler from .edited_message_handler import EditedMessageHandler
from .edited_bot_business_message_handler import EditedBotBusinessMessageHandler from .edited_bot_business_message_handler import EditedBotBusinessMessageHandler
from .error_handler import ErrorHandler
from .inline_query_handler import InlineQueryHandler from .inline_query_handler import InlineQueryHandler
from .message_handler import MessageHandler from .message_handler import MessageHandler
from .poll_handler import PollHandler from .poll_handler import PollHandler

View file

@ -0,0 +1,79 @@
# Pyrofork - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-present Dan <https://github.com/delivrance>
# Copyright (C) 2022-present Mayuri-Chan <https://github.com/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 <http://www.gnu.org/licenses/>.
from __future__ import annotations
from collections.abc import Iterable
from typing import Callable
from .handler import Handler
import pyrogram
from pyrogram.types import Update
class ErrorHandler(Handler):
"""The Error handler class. Used to handle errors.
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_error` decorator.
Parameters:
callback (``Callable``):
Pass a function that will be called when a new Error arrives. It takes *(client, error)*
as positional arguments (look at the section below for a detailed description).
exceptions (``Exception`` | Iterable of ``Exception``, *optional*):
Pass one or more exception classes to allow only a subset of errors 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 error handler.
update (:obj:`~pyrogram.Update`):
The update that caused the error.
error (``Exception``):
The error that was raised.
"""
def __init__(
self,
callback: Callable,
exceptions: type[Exception] | Iterable[type[Exception]] | None = None,
):
self.exceptions = (
tuple(exceptions)
if isinstance(exceptions, Iterable)
else (exceptions,)
if exceptions
else (Exception,)
)
super().__init__(callback)
async def check(self, client: pyrogram.Client, update: Update, exception: Exception) -> bool:
if isinstance(exception, self.exceptions):
await self.callback(client, update, exception)
return True
return False
def check_remove(self, error: type[Exception] | Iterable[type[Exception]]) -> bool:
return isinstance(error, self.exceptions)

View file

@ -35,6 +35,10 @@ class RawUpdateHandler(Handler):
*(client, update, users, chats)* as positional arguments (look at the section below for *(client, update, users, chats)* as positional arguments (look at the section below for
a detailed description). a detailed description).
filters (:obj:`Filters`):
Pass one or more filters to allow only a subset of callback queries to be passed
in your callback function.
Other Parameters: Other Parameters:
client (:obj:`~pyrogram.Client`): client (:obj:`~pyrogram.Client`):
The Client itself, useful when you want to call other API methods inside the update handler. The Client itself, useful when you want to call other API methods inside the update handler.
@ -64,5 +68,5 @@ class RawUpdateHandler(Handler):
- :obj:`~pyrogram.raw.types.ChannelForbidden` - :obj:`~pyrogram.raw.types.ChannelForbidden`
""" """
def __init__(self, callback: Callable): def __init__(self, callback: Callable, filters=None):
super().__init__(callback) super().__init__(callback, filters)

View file

@ -36,6 +36,7 @@ from .set_bot_default_privileges import SetBotDefaultPrivileges
from .set_bot_info import SetBotInfo from .set_bot_info import SetBotInfo
from .set_chat_menu_button import SetChatMenuButton from .set_chat_menu_button import SetChatMenuButton
from .set_game_score import SetGameScore from .set_game_score import SetGameScore
from .get_owned_bots import GetOwnedBots
class Bots( class Bots(
@ -57,6 +58,7 @@ class Bots(
SetChatMenuButton, SetChatMenuButton,
GetChatMenuButton, GetChatMenuButton,
AnswerWebAppQuery, AnswerWebAppQuery,
GetCollectibleItemInfo GetCollectibleItemInfo,
GetOwnedBots,
): ):
pass pass

View file

@ -0,0 +1,46 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-present Dan <https://github.com/delivrance>
#
# This file is part of Pyrogram.
#
# Pyrogram is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Pyrogram is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
from typing import List
import pyrogram
from pyrogram import raw, types
class GetOwnedBots:
async def get_owned_bots(
self: "pyrogram.Client",
) -> List["types.User"]:
"""Returns the list of bots owned by the current user.
.. include:: /_includes/usable-by/users.rst
Returns:
List of :obj:`~pyrogram.types.User`: On success.
Example:
.. code-block:: python
bots = await app.get_owned_bots()
"""
bots = await self.invoke(raw.functions.bots.GetAdminedBots())
return types.List([
types.User._parse(self, b)
for b in bots
])

View file

@ -22,14 +22,18 @@ from typing import Union
import pyrogram import pyrogram
from pyrogram import raw, types from pyrogram import raw, types
from ..messages.inline_session import get_session
class PinChatMessage: class PinChatMessage:
async def pin_chat_message( async def pin_chat_message(
self: "pyrogram.Client", self: "pyrogram.Client",
chat_id: Union[int, str], chat_id: Union[int, str],
message_id: int, message_id: int,
disable_notification: bool = False, disable_notification: bool = False,
both_sides: bool = False, both_sides: bool = False,
business_connection_id: str = None,
) -> "types.Message": ) -> "types.Message":
"""Pin a message in a group, channel or your own chat. """Pin a message in a group, channel or your own chat.
You must be an administrator in the chat for this to work and must have the "can_pin_messages" admin right in You must be an administrator in the chat for this to work and must have the "can_pin_messages" admin right in
@ -52,6 +56,9 @@ class PinChatMessage:
both_sides (``bool``, *optional*): both_sides (``bool``, *optional*):
Pass True to pin the message for both sides (you and recipient). Pass True to pin the message for both sides (you and recipient).
Applicable to private chats only. Defaults to False. Applicable to private chats only. Defaults to False.
business_connection_id (``str``, *optional*):
Unique identifier of the business connection on behalf of which the message will be pinned.
Returns: Returns:
:obj:`~pyrogram.types.Message`: On success, the service message is returned. :obj:`~pyrogram.types.Message`: On success, the service message is returned.
@ -65,19 +72,39 @@ class PinChatMessage:
# Pin without notification # Pin without notification
await app.pin_chat_message(chat_id, message_id, disable_notification=True) await app.pin_chat_message(chat_id, message_id, disable_notification=True)
""" """
r = await self.invoke( rpc = raw.functions.messages.UpdatePinnedMessage(
raw.functions.messages.UpdatePinnedMessage( peer=await self.resolve_peer(chat_id),
peer=await self.resolve_peer(chat_id), id=message_id,
id=message_id, silent=disable_notification or None,
silent=disable_notification or None, pm_oneside=not both_sides or None
pm_oneside=not both_sides or None
)
) )
session = None
business_connection = None
if business_connection_id:
business_connection = self.business_user_connection_cache[business_connection_id]
if not business_connection:
business_connection = await self.get_business_connection(business_connection_id)
session = await get_session(
self,
business_connection._raw.connection.dc_id
)
if business_connection_id:
r = await session.invoke(
raw.functions.InvokeWithBusinessConnection(
query=rpc,
connection_id=business_connection_id
)
)
else:
r = await self.invoke(rpc)
users = {u.id: u for u in r.users} users = {u.id: u for u in r.users}
chats = {c.id: c for c in r.chats} chats = {c.id: c for c in r.chats}
for i in r.updates: for i in r.updates:
if isinstance(i, (raw.types.UpdateNewMessage, if isinstance(i, (raw.types.UpdateNewMessage,
raw.types.UpdateNewChannelMessage)): raw.types.UpdateNewChannelMessage,
return await types.Message._parse(self, i.message, users, chats) raw.types.UpdateBotNewBusinessMessage)):
return await types.Message._parse(self, i.message, users, chats, business_connection_id=business_connection_id)

View file

@ -27,6 +27,7 @@ class UnpinAllChatMessages:
async def unpin_all_chat_messages( async def unpin_all_chat_messages(
self: "pyrogram.Client", self: "pyrogram.Client",
chat_id: Union[int, str], chat_id: Union[int, str],
message_thread_id: int = None,
) -> bool: ) -> bool:
"""Use this method to clear the list of pinned messages in a chat. """Use this method to clear the list of pinned messages in a chat.
If the chat is not a private chat, the bot must be an administrator in the chat for this to work and must have If the chat is not a private chat, the bot must be an administrator in the chat for this to work and must have
@ -39,6 +40,9 @@ class UnpinAllChatMessages:
Unique identifier (int) or username (str) of the target chat. Unique identifier (int) or username (str) of the target chat.
You can also use chat public link in form of *t.me/<username>* (str). You can also use chat public link in form of *t.me/<username>* (str).
message_thread_id (``int``, *optional*):
Unique identifier for the target message thread of the forum topic
Returns: Returns:
``bool``: True on success. ``bool``: True on success.
@ -48,10 +52,10 @@ class UnpinAllChatMessages:
# Unpin all chat messages # Unpin all chat messages
await app.unpin_all_chat_messages(chat_id) await app.unpin_all_chat_messages(chat_id)
""" """
await self.invoke( r = await self.invoke(
raw.functions.messages.UnpinAllMessages( raw.functions.messages.UnpinAllMessages(
peer=await self.resolve_peer(chat_id) peer=await self.resolve_peer(chat_id),
top_msg_id=message_thread_id
) )
) )
return r.pts_count
return True

View file

@ -22,12 +22,15 @@ from typing import Union
import pyrogram import pyrogram
from pyrogram import raw from pyrogram import raw
from ..messages.inline_session import get_session
class UnpinChatMessage: class UnpinChatMessage:
async def unpin_chat_message( async def unpin_chat_message(
self: "pyrogram.Client", self: "pyrogram.Client",
chat_id: Union[int, str], chat_id: Union[int, str],
message_id: int = 0 message_id: int = 0,
business_connection_id: str = None,
) -> bool: ) -> bool:
"""Unpin a message in a group, channel or your own chat. """Unpin a message in a group, channel or your own chat.
You must be an administrator in the chat for this to work and must have the "can_pin_messages" admin You must be an administrator in the chat for this to work and must have the "can_pin_messages" admin
@ -38,12 +41,15 @@ class UnpinChatMessage:
Parameters: Parameters:
chat_id (``int`` | ``str``): chat_id (``int`` | ``str``):
Unique identifier (int) or username (str) of the target chat. Unique identifier (int) or username (str) of the target chat.
You can also use chat public link in form of *t.me/<username>* (str).
message_id (``int``, *optional*): message_id (``int``, *optional*):
Identifier of a message to unpin. Identifier of a message to unpin.
Required if ``business_connection_id`` is specified.
If not specified, the most recent pinned message (by sending date) will be unpinned. If not specified, the most recent pinned message (by sending date) will be unpinned.
business_connection_id (``str``, *optional*):
Unique identifier of the business connection on behalf of which the message will be unpinned.
Returns: Returns:
``bool``: True on success. ``bool``: True on success.
@ -52,12 +58,32 @@ class UnpinChatMessage:
await app.unpin_chat_message(chat_id, message_id) await app.unpin_chat_message(chat_id, message_id)
""" """
await self.invoke( rpc = raw.functions.messages.UpdatePinnedMessage(
raw.functions.messages.UpdatePinnedMessage( peer=await self.resolve_peer(chat_id),
peer=await self.resolve_peer(chat_id), id=message_id,
id=message_id, unpin=True
unpin=True
)
) )
session = None
business_connection = None
if business_connection_id:
business_connection = self.business_user_connection_cache[business_connection_id]
if not business_connection:
business_connection = await self.get_business_connection(business_connection_id)
session = await get_session(
self,
business_connection._raw.connection.dc_id
)
if business_connection_id:
await session.invoke(
raw.functions.InvokeWithBusinessConnection(
query=rpc,
connection_id=business_connection_id
)
)
else:
await self.invoke(rpc)
return True return True

View file

@ -28,6 +28,7 @@ from .on_deleted_bot_business_messages import OnDeletedBotBusinessMessages
from .on_disconnect import OnDisconnect from .on_disconnect import OnDisconnect
from .on_edited_message import OnEditedMessage from .on_edited_message import OnEditedMessage
from .on_edited_bot_business_message import OnEditedBotBusinessMessage from .on_edited_bot_business_message import OnEditedBotBusinessMessage
from .on_error import OnError
from .on_inline_query import OnInlineQuery from .on_inline_query import OnInlineQuery
from .on_message import OnMessage from .on_message import OnMessage
from .on_poll import OnPoll from .on_poll import OnPoll
@ -47,6 +48,7 @@ class Decorators(
OnBotBusinessMessage, OnBotBusinessMessage,
OnEditedMessage, OnEditedMessage,
OnEditedBotBusinessMessage, OnEditedBotBusinessMessage,
OnError,
OnDeletedMessages, OnDeletedMessages,
OnDeletedBotBusinessMessages, OnDeletedBotBusinessMessages,
OnCallbackQuery, OnCallbackQuery,

View file

@ -0,0 +1,50 @@
# Pyrofork - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-present Dan <https://github.com/delivrance>
# Copyright (C) 2022-present Mayuri-Chan <https://github.com/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 <http://www.gnu.org/licenses/>.
from typing import Callable
import pyrogram
from pyrogram.filters import Filter
class OnError:
def on_error(self=None, errors=None) -> Callable:
"""Decorator for handling new errors.
This does the same thing as :meth:`~pyrogram.Client.add_handler` using the
:obj:`~pyrogram.handlers.MessageHandler`.
Parameters:
errors (:obj:`~Exception`, *optional*):
Pass one or more errors to allow only a subset of errors to be passed
in your function.
"""
def decorator(func: Callable) -> Callable:
if isinstance(self, pyrogram.Client):
self.add_handler(pyrogram.handlers.ErrorHandler(func, errors), 0)
elif isinstance(self, Filter) or self is None:
if not hasattr(func, "handlers"):
func.handlers = []
func.handlers.append((pyrogram.handlers.ErrorHandler(func, self), 0))
return func
return decorator

View file

@ -17,15 +17,17 @@
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with Pyrofork. If not, see <http://www.gnu.org/licenses/>. # along with Pyrofork. If not, see <http://www.gnu.org/licenses/>.
from typing import Callable from typing import Callable, Optional
import pyrogram import pyrogram
from pyrogram.filters import Filter
class OnRawUpdate: class OnRawUpdate:
def on_raw_update( def on_raw_update(
self=None, self: Optional["OnRawUpdate"] = None,
group: int = 0 filters=None,
group: int = 0,
) -> Callable: ) -> Callable:
"""Decorator for handling raw updates. """Decorator for handling raw updates.
@ -33,24 +35,28 @@ class OnRawUpdate:
:obj:`~pyrogram.handlers.RawUpdateHandler`. :obj:`~pyrogram.handlers.RawUpdateHandler`.
Parameters: Parameters:
filters (:obj:`~pyrogram.filters`, *optional*):
Pass one or more filters to allow only a subset of callback queries to be passed
in your function.
group (``int``, *optional*): group (``int``, *optional*):
The group identifier, defaults to 0. The group identifier, defaults to 0.
""" """
def decorator(func: Callable) -> Callable: def decorator(func: Callable) -> Callable:
if isinstance(self, pyrogram.Client): if isinstance(self, pyrogram.Client):
self.add_handler(pyrogram.handlers.RawUpdateHandler(func), group) self.add_handler(pyrogram.handlers.RawUpdateHandler(func, filters), group)
else: elif isinstance(self, Filter) or self is None:
if not hasattr(func, "handlers"): if not hasattr(func, "handlers"):
func.handlers = [] func.handlers = []
func.handlers.append( func.handlers.append(
( (
pyrogram.handlers.RawUpdateHandler(func), pyrogram.handlers.RawUpdateHandler(func, self),
group group if filters is None else filters
) )
) )
return func return func
return decorator return decorator

View file

@ -30,6 +30,7 @@ from .edit_message_caption import EditMessageCaption
from .edit_message_media import EditMessageMedia from .edit_message_media import EditMessageMedia
from .edit_message_reply_markup import EditMessageReplyMarkup from .edit_message_reply_markup import EditMessageReplyMarkup
from .edit_message_text import EditMessageText from .edit_message_text import EditMessageText
from .forward_media_group import ForwardMediaGroup
from .forward_messages import ForwardMessages from .forward_messages import ForwardMessages
from .get_available_effects import GetAvailableEffects from .get_available_effects import GetAvailableEffects
from .get_chat_history import GetChatHistory from .get_chat_history import GetChatHistory
@ -81,6 +82,7 @@ class Messages(
EditMessageReplyMarkup, EditMessageReplyMarkup,
EditMessageMedia, EditMessageMedia,
EditMessageText, EditMessageText,
ForwardMediaGroup,
ForwardMessages, ForwardMessages,
GetAvailableEffects, GetAvailableEffects,
GetMediaGroup, GetMediaGroup,

View file

@ -0,0 +1,120 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-present Dan <https://github.com/delivrance>
#
# This file is part of Pyrogram.
#
# Pyrogram is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Pyrogram is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
from datetime import datetime
from typing import Union, List
import pyrogram
from pyrogram import raw, utils
from pyrogram import types
class ForwardMediaGroup:
async def forward_media_group(
self: "pyrogram.Client",
chat_id: Union[int, str],
from_chat_id: Union[int, str],
message_id: int,
message_thread_id: int = None,
disable_notification: bool = None,
schedule_date: datetime = None,
hide_sender_name: bool = None,
hide_captions: bool = None,
protect_content: bool = None
) -> List["types.Message"]:
"""Forward a media group by providing one of the message ids.
.. include:: /_includes/usable-by/users-bots.rst
Parameters:
chat_id (``int`` | ``str``):
Unique identifier (int) or username (str) of the target chat.
For your personal cloud (Saved Messages) you can simply use "me" or "self".
For a contact that exists in your Telegram address book you can use his phone number (str).
from_chat_id (``int`` | ``str``):
Unique identifier (int) or username (str) of the source chat where the original message was sent.
For your personal cloud (Saved Messages) you can simply use "me" or "self".
For a contact that exists in your Telegram address book you can use his phone number (str).
message_id (``int``):
Message identifier in the chat specified in *from_chat_id*.
message_thread_id (``int``, *optional*):
Unique identifier of a message thread to which the message belongs.
For supergroups only.
disable_notification (``bool``, *optional*):
Sends the message silently.
Users will receive a notification with no sound.
schedule_date (:py:obj:`~datetime.datetime`, *optional*):
Date when the message will be automatically sent.
hide_sender_name (``bool``, *optional*):
If True, the original author of the message will not be shown.
hide_captions (``bool``, *optional*):
If True, the original media captions will be removed.
protect_content (``bool``, *optional*):
Protects the contents of the sent message from forwarding and saving.
Returns:
List of :obj:`~pyrogram.types.Message`: On success, a list of forwarded messages is returned.
Example:
.. code-block:: python
# Forward a media group
await app.forward_media_group(to_chat, from_chat, 123)
"""
message_ids = [i.id for i in await self.get_media_group(from_chat_id, message_id)]
r = await self.invoke(
raw.functions.messages.ForwardMessages(
to_peer=await self.resolve_peer(chat_id),
from_peer=await self.resolve_peer(from_chat_id),
id=message_ids,
silent=disable_notification or None,
random_id=[self.rnd_id() for _ in message_ids],
schedule_date=utils.datetime_to_timestamp(schedule_date),
drop_author=hide_sender_name,
drop_media_captions=hide_captions,
noforwards=protect_content,
top_msg_id=message_thread_id
)
)
forwarded_messages = []
users = {i.id: i for i in r.users}
chats = {i.id: i for i in r.chats}
for i in r.updates:
if isinstance(i, (raw.types.UpdateNewMessage,
raw.types.UpdateNewChannelMessage,
raw.types.UpdateNewScheduledMessage)):
forwarded_messages.append(
await types.Message._parse(
self, i.message,
users, chats
)
)
return types.List(forwarded_messages)

View file

@ -20,6 +20,7 @@
from .add_handler import AddHandler from .add_handler import AddHandler
from .export_session_string import ExportSessionString from .export_session_string import ExportSessionString
from .remove_handler import RemoveHandler from .remove_handler import RemoveHandler
from .remove_error_handler import RemoveErrorHandler
from .restart import Restart from .restart import Restart
from .run import Run from .run import Run
from .run_sync import RunSync from .run_sync import RunSync
@ -32,6 +33,7 @@ class Utilities(
AddHandler, AddHandler,
ExportSessionString, ExportSessionString,
RemoveHandler, RemoveHandler,
RemoveErrorHandler,
Restart, Restart,
Run, Run,
RunSync, RunSync,

View file

@ -0,0 +1,42 @@
# Pyrofork - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-present Dan <https://github.com/delivrance>
# Copyright (C) 2022-present Mayuri-Chan <https://github.com/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 <http://www.gnu.org/licenses/>.
from __future__ import annotations
from collections.abc import Iterable
import pyrogram
class RemoveErrorHandler:
def remove_error_handler(
self: pyrogram.Client,
exception: type[Exception] | Iterable[type[Exception]] = Exception,
):
"""Remove a previously registered error handler using exception classes.
Parameters:
exception (``Exception`` | Iterable of ``Exception``, *optional*):
The error(s) for handlers to be removed. Defaults to Exception.
"""
to_remove = [
handler
for handler in self.dispatcher.error_handlers
if handler.check_remove(exception)
]
for handler in to_remove:
self.dispatcher.error_handlers.remove(handler)

View file

@ -37,7 +37,7 @@ PRE_DELIM = "```"
BLOCKQUOTE_DELIM = ">" BLOCKQUOTE_DELIM = ">"
BLOCKQUOTE_EXPANDABLE_DELIM = "**>" BLOCKQUOTE_EXPANDABLE_DELIM = "**>"
MARKDOWN_RE = re.compile(r"({d})|\[(.+?)\]\((.+?)\)".format( MARKDOWN_RE = re.compile(r"({d})|(!?)\[(.+?)\]\((.+?)\)".format(
d="|".join( d="|".join(
["".join(i) for i in [ ["".join(i) for i in [
[rf"\{j}" for j in i] [rf"\{j}" for j in i]
@ -56,6 +56,7 @@ MARKDOWN_RE = re.compile(r"({d})|\[(.+?)\]\((.+?)\)".format(
OPENING_TAG = "<{}>" OPENING_TAG = "<{}>"
CLOSING_TAG = "</{}>" CLOSING_TAG = "</{}>"
URL_MARKUP = '<a href="{}">{}</a>' URL_MARKUP = '<a href="{}">{}</a>'
EMOJI_MARKUP = '<emoji id={}>{}</emoji>'
FIXED_WIDTH_DELIMS = [CODE_DELIM, PRE_DELIM] FIXED_WIDTH_DELIMS = [CODE_DELIM, PRE_DELIM]
CODE_TAG_RE = re.compile(r"<code>.*?</code>") CODE_TAG_RE = re.compile(r"<code>.*?</code>")
@ -117,7 +118,7 @@ class Markdown:
for i, match in enumerate(re.finditer(MARKDOWN_RE, text)): for i, match in enumerate(re.finditer(MARKDOWN_RE, text)):
start, _ = match.span() start, _ = match.span()
delim, text_url, url = match.groups() delim, is_emoji, text_url, url = match.groups()
full = match.group(0) full = match.group(0)
if delim in FIXED_WIDTH_DELIMS: if delim in FIXED_WIDTH_DELIMS:
@ -126,10 +127,16 @@ class Markdown:
if is_fixed_width and delim not in FIXED_WIDTH_DELIMS: if is_fixed_width and delim not in FIXED_WIDTH_DELIMS:
continue continue
if text_url: if not is_emoji and text_url:
text = utils.replace_once(text, full, URL_MARKUP.format(url, text_url), start) text = utils.replace_once(text, full, URL_MARKUP.format(url, text_url), start)
continue continue
if is_emoji:
emoji = text_url
emoji_id = url.lstrip("tg://emoji?id=")
text = utils.replace_once(text, full, EMOJI_MARKUP.format(emoji_id, emoji), start)
continue
if delim == BOLD_DELIM: if delim == BOLD_DELIM:
tag = "b" tag = "b"
elif delim == ITALIC_DELIM: elif delim == ITALIC_DELIM:
@ -221,6 +228,10 @@ class Markdown:
user = entity.user user = entity.user
start_tag = "[" start_tag = "["
end_tag = f"](tg://user?id={user.id})" end_tag = f"](tg://user?id={user.id})"
elif entity_type == MessageEntityType.CUSTOM_EMOJI:
emoji_id = entity.custom_emoji_id
start_tag = "!["
end_tag = f"](tg://emoji?id={emoji_id})"
else: else:
continue continue

View file

@ -17,6 +17,7 @@
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with PyroFork. If not, see <http://www.gnu.org/licenses/>. # along with PyroFork. If not, see <http://www.gnu.org/licenses/>.
from .alternative_video import AlternativeVideo
from .animation import Animation from .animation import Animation
from .audio import Audio from .audio import Audio
from .available_effect import AvailableEffect from .available_effect import AvailableEffect
@ -71,6 +72,7 @@ from .wallpaper_settings import WallpaperSettings
from .translated_text import TranslatedText from .translated_text import TranslatedText
__all__ = [ __all__ = [
"AlternativeVideo",
"Animation", "Animation",
"Audio", "Audio",
"AvailableEffect", "AvailableEffect",

View file

@ -0,0 +1,134 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-present Dan <https://github.com/delivrance>
#
# This file is part of Pyrogram.
#
# Pyrogram is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Pyrogram is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
from datetime import datetime
from typing import List
import pyrogram
from pyrogram import raw, utils
from pyrogram import types
from pyrogram.file_id import FileId, FileType, FileUniqueId, FileUniqueType, ThumbnailSource
from ..object import Object
class AlternativeVideo(Object):
"""Describes an alternative reencoded quality of a video file.
Parameters:
file_id (``str``):
Identifier for this file, which can be used to download or reuse the file.
file_unique_id (``str``):
Unique identifier for this file, which is supposed to be the same over time and for different accounts.
Can't be used to download or reuse the file.
width (``int``):
Video width as defined by sender.
height (``int``):
Video height as defined by sender.
codec (``str``):
Codec used for video file encoding, for example, "h264", "h265", or "av1".
duration (``int``):
Duration of the video in seconds as defined by sender.
file_name (``str``, *optional*):
Video file name.
mime_type (``str``, *optional*):
Mime type of a file as defined by sender.
file_size (``int``, *optional*):
File size.
supports_streaming (``bool``, *optional*):
True, if the video was uploaded with streaming support.
date (:py:obj:`~datetime.datetime`, *optional*):
Date the video was sent.
thumbs (List of :obj:`~pyrogram.types.Thumbnail`, *optional*):
Video thumbnails.
"""
def __init__(
self,
*,
client: "pyrogram.Client" = None,
file_id: str,
file_unique_id: str,
width: int,
height: int,
codec: str,
duration: int,
file_name: str = None,
mime_type: str = None,
file_size: int = None,
supports_streaming: bool = None,
date: datetime = None,
thumbs: List["types.Thumbnail"] = None
):
super().__init__(client)
self.file_id = file_id
self.file_unique_id = file_unique_id
self.width = width
self.height = height
self.codec = codec
self.duration = duration
self.file_name = file_name
self.mime_type = mime_type
self.file_size = file_size
self.supports_streaming = supports_streaming
self.date = date
self.thumbs = thumbs
@staticmethod
def _parse(
client,
video: "raw.types.Document",
video_attributes: "raw.types.DocumentAttributeVideo",
file_name: str
) -> "AlternativeVideo":
return AlternativeVideo(
file_id=FileId(
file_type=FileType.VIDEO,
dc_id=video.dc_id,
media_id=video.id,
access_hash=video.access_hash,
file_reference=video.file_reference
).encode() if video else None,
file_unique_id=FileUniqueId(
file_unique_type=FileUniqueType.DOCUMENT,
media_id=video.id
).encode() if video else None,
width=video_attributes.w if video_attributes else None,
height=video_attributes.h if video_attributes else None,
codec=video_attributes.video_codec if video_attributes else None,
duration=video_attributes.duration if video_attributes else None,
file_name=file_name,
mime_type=video.mime_type if video else None,
supports_streaming=video_attributes.supports_streaming if video_attributes else None,
file_size=video.size if video else None,
date=utils.timestamp_to_datetime(video.date) if video else None,
thumbs=types.Thumbnail._parse(client, video) if video else None,
client=client
)

View file

@ -506,6 +506,7 @@ class Message(Object, Update):
screenshot_taken: "types.ScreenshotTaken" = None, screenshot_taken: "types.ScreenshotTaken" = None,
invoice: "types.Invoice" = None, invoice: "types.Invoice" = None,
story: Union["types.MessageStory", "types.Story"] = None, story: Union["types.MessageStory", "types.Story"] = None,
alternative_videos: List["types.AlternativeVideo"] = None,
video: "types.Video" = None, video: "types.Video" = None,
voice: "types.Voice" = None, voice: "types.Voice" = None,
video_note: "types.VideoNote" = None, video_note: "types.VideoNote" = None,
@ -628,6 +629,7 @@ class Message(Object, Update):
self.invoice = invoice self.invoice = invoice
self.story = story self.story = story
self.video = video self.video = video
self.alternative_videos = alternative_videos
self.voice = voice self.voice = voice
self.video_note = video_note self.video_note = video_note
self.web_page_preview = web_page_preview self.web_page_preview = web_page_preview
@ -1055,6 +1057,7 @@ class Message(Object, Update):
voice = None voice = None
animation = None animation = None
video = None video = None
alternative_videos = []
video_note = None video_note = None
web_page_preview = None web_page_preview = None
sticker = None sticker = None
@ -1122,6 +1125,22 @@ class Message(Object, Update):
video = types.Video._parse(client, doc, video_attributes, file_name, media.ttl_seconds) video = types.Video._parse(client, doc, video_attributes, file_name, media.ttl_seconds)
media_type = enums.MessageMediaType.VIDEO media_type = enums.MessageMediaType.VIDEO
has_media_spoiler = media.spoiler has_media_spoiler = media.spoiler
altdocs = media.alt_documents or []
for altdoc in altdocs:
if isinstance(altdoc, raw.types.Document):
altdoc_attributes = {type(i): i for i in altdoc.attributes}
altdoc_file_name = getattr(
altdoc_attributes.get(
raw.types.DocumentAttributeFilename, None
), "file_name", None
)
altdoc_video_attribute = altdoc_attributes.get(raw.types.DocumentAttributeVideo, None)
if altdoc_video_attribute:
alternative_videos.append(
types.AlternativeVideo._parse(client, altdoc, altdoc_video_attribute, altdoc_file_name)
)
elif raw.types.DocumentAttributeAudio in attributes: elif raw.types.DocumentAttributeAudio in attributes:
audio_attributes = attributes[raw.types.DocumentAttributeAudio] audio_attributes = attributes[raw.types.DocumentAttributeAudio]
@ -1238,6 +1257,7 @@ class Message(Object, Update):
invoice=invoice, invoice=invoice,
story=story, story=story,
video=video, video=video,
alternative_videos=types.List(alternative_videos) if alternative_videos else None,
video_note=video_note, video_note=video_note,
web_page_preview=web_page_preview, web_page_preview=web_page_preview,
sticker=sticker, sticker=sticker,