mirror of
https://github.com/Mayuri-Chan/pyrofork.git
synced 2025-12-29 12:04:51 +00:00
Merge branch 'main' of https://github.com/Mayuri-Chan/Pyrofork
This commit is contained in:
commit
56e9173579
23 changed files with 748 additions and 103 deletions
|
|
@ -26,6 +26,14 @@ 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;
|
||||
|
||||
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;
|
||||
inputUserSelf#f7c1b13f = InputUser;
|
||||
inputUser#f21158c6 user_id:long access_hash:long = InputUser;
|
||||
|
|
@ -244,7 +252,7 @@ inputReportReasonFake#f5ddd6e7 = ReportReason;
|
|||
inputReportReasonIllegalDrugs#a8eb2be = 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;
|
||||
|
||||
|
|
@ -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;
|
||||
updateNewStoryReaction#1824e40b story_id:int peer:Peer reaction:Reaction = 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;
|
||||
updateStarsRevenueStatus#a584b019 peer:Peer status:StarsRevenueStatus = 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;
|
||||
|
||||
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;
|
||||
|
||||
|
|
@ -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;
|
||||
|
||||
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;
|
||||
|
||||
|
|
@ -1882,18 +1890,36 @@ starGift#49c577cd flags:# limited:flags.0?true sold_out:flags.1?true birthday:fl
|
|||
|
||||
payments.starGiftsNotModified#a388a368 = 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;
|
||||
|
||||
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;
|
||||
|
||||
reportResultChooseOption#f0e4e0b6 title:string options:Vector<MessageReportOption> = ReportResult;
|
||||
reportResultAddComment#6f09ac31 flags:# optional:flags.0?true option:bytes = ReportResult;
|
||||
reportResultReported#8db33c4b = ReportResult;
|
||||
|
||||
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;
|
||||
|
||||
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---
|
||||
|
||||
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.getBlocked#9a868f80 flags:# my_stories_from:flags.0?true offset:int limit:int = contacts.Blocked;
|
||||
contacts.search#11f812d8 q:string limit:int = contacts.Found;
|
||||
contacts.resolveUsername#f93ccba3 username:string = contacts.ResolvedPeer;
|
||||
contacts.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.resetTopPeerRating#1ae373ac category:TopPeerCategory peer:InputPeer = 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.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.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.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.toggleUserEmojiStatusPermission#6de6392 bot:InputUser enabled:Bool = 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.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.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.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.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;
|
||||
|
||||
// LAYER 194
|
||||
// LAYER 195
|
||||
|
|
@ -165,6 +165,7 @@ def pyrogram_api():
|
|||
messages="""
|
||||
Messages
|
||||
send_message
|
||||
forward_media_group
|
||||
forward_messages
|
||||
copy_message
|
||||
copy_media_group
|
||||
|
|
@ -397,6 +398,7 @@ def pyrogram_api():
|
|||
sell_gift
|
||||
send_gift
|
||||
toggle_gift_is_saved
|
||||
get_owned_bots
|
||||
""",
|
||||
business="""
|
||||
Telegram Business
|
||||
|
|
@ -511,6 +513,7 @@ def pyrogram_api():
|
|||
Audio
|
||||
AvailableEffect
|
||||
Document
|
||||
AlternativeVideo
|
||||
Animation
|
||||
Video
|
||||
Voice
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
# along with Pyrofork. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
__fork_name__ = "PyroFork"
|
||||
__version__ = "2.3.52"
|
||||
__version__ = "2.3.53"
|
||||
__license__ = "GNU Lesser General Public License v3.0 (LGPL-3.0)"
|
||||
__copyright__ = "Copyright (C) 2022-present Mayuri-Chan <https://github.com/Mayuri-Chan>"
|
||||
|
||||
|
|
|
|||
|
|
@ -23,9 +23,8 @@ import logging
|
|||
from collections import OrderedDict
|
||||
|
||||
import pyrogram
|
||||
from pyrogram import errors
|
||||
from pyrogram import utils
|
||||
from pyrogram import raw
|
||||
from pyrogram import errors, raw, types, utils
|
||||
from pyrogram.handlers.handler import Handler
|
||||
from pyrogram.handlers import (
|
||||
BotBusinessConnectHandler,
|
||||
BotBusinessMessageHandler,
|
||||
|
|
@ -33,6 +32,7 @@ from pyrogram.handlers import (
|
|||
MessageHandler,
|
||||
EditedMessageHandler,
|
||||
EditedBotBusinessMessageHandler,
|
||||
ErrorHandler,
|
||||
DeletedMessagesHandler,
|
||||
DeletedBotBusinessMessagesHandler,
|
||||
MessageReactionUpdatedHandler,
|
||||
|
|
@ -97,6 +97,7 @@ class Dispatcher:
|
|||
|
||||
self.handler_worker_tasks = []
|
||||
self.locks_list = []
|
||||
self.error_handlers = []
|
||||
|
||||
self.updates_queue = asyncio.Queue()
|
||||
self.groups = OrderedDict()
|
||||
|
|
@ -286,6 +287,7 @@ class Dispatcher:
|
|||
|
||||
self.handler_worker_tasks.clear()
|
||||
self.groups.clear()
|
||||
self.error_handlers.clear()
|
||||
|
||||
log.info("Stopped %s HandlerTasks", self.client.workers)
|
||||
|
||||
|
|
@ -295,11 +297,14 @@ class Dispatcher:
|
|||
await lock.acquire()
|
||||
|
||||
try:
|
||||
if group not in self.groups:
|
||||
self.groups[group] = []
|
||||
self.groups = OrderedDict(sorted(self.groups.items()))
|
||||
|
||||
self.groups[group].append(handler)
|
||||
if isinstance(handler, ErrorHandler):
|
||||
if handler not in self.error_handlers:
|
||||
self.error_handlers.append(handler)
|
||||
else:
|
||||
if group not in self.groups:
|
||||
self.groups[group] = []
|
||||
self.groups = OrderedDict(sorted(self.groups.items()))
|
||||
self.groups[group].append(handler)
|
||||
finally:
|
||||
for lock in self.locks_list:
|
||||
lock.release()
|
||||
|
|
@ -312,71 +317,94 @@ class Dispatcher:
|
|||
await lock.acquire()
|
||||
|
||||
try:
|
||||
if group not in self.groups:
|
||||
raise ValueError(f"Group {group} does not exist. Handler was not removed.")
|
||||
|
||||
self.groups[group].remove(handler)
|
||||
if isinstance(handler, ErrorHandler):
|
||||
if handler not in self.error_handlers:
|
||||
raise ValueError(
|
||||
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:
|
||||
for lock in self.locks_list:
|
||||
lock.release()
|
||||
|
||||
self.loop.create_task(fn())
|
||||
|
||||
async def handler_worker(self, lock):
|
||||
async def handler_worker(self, lock: asyncio.Lock):
|
||||
while True:
|
||||
packet = await self.updates_queue.get()
|
||||
|
||||
if packet is None:
|
||||
break
|
||||
await self._process_packet(packet, lock)
|
||||
|
||||
try:
|
||||
update, users, chats = packet
|
||||
parser = self.update_parsers.get(type(update), None)
|
||||
async def _process_packet(
|
||||
self,
|
||||
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 = (
|
||||
await parser(update, users, chats)
|
||||
if parser is not None
|
||||
else (None, type(None))
|
||||
)
|
||||
|
||||
async with lock:
|
||||
for group in self.groups.values():
|
||||
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
|
||||
if parser is not None:
|
||||
parsed_result = parser(update, users, chats)
|
||||
if inspect.isawaitable(parsed_result):
|
||||
parsed_update, handler_type = await parsed_result
|
||||
else:
|
||||
parsed_update, handler_type = parsed_result
|
||||
else:
|
||||
parsed_update, handler_type = (None, type(None))
|
||||
|
||||
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):
|
||||
args = (update, users, chats)
|
||||
|
||||
if args is None:
|
||||
continue
|
||||
|
||||
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:
|
||||
await self._execute_callback(handler, update, users, chats)
|
||||
break
|
||||
except (pyrogram.StopPropagation, pyrogram.ContinuePropagation) as e:
|
||||
if isinstance(e, pyrogram.StopPropagation):
|
||||
raise
|
||||
except pyrogram.ContinuePropagation:
|
||||
continue
|
||||
except Exception as e:
|
||||
log.exception(e)
|
||||
except Exception as exception:
|
||||
if parsed_update is not None:
|
||||
await self._handle_exception(parsed_update, exception)
|
||||
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:
|
||||
pass
|
||||
except Exception as e:
|
||||
log.exception(e)
|
||||
raise
|
||||
except pyrogram.ContinuePropagation:
|
||||
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
|
||||
)
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ from .deleted_bot_business_messages_handler import DeletedBotBusinessMessagesHan
|
|||
from .disconnect_handler import DisconnectHandler
|
||||
from .edited_message_handler import EditedMessageHandler
|
||||
from .edited_bot_business_message_handler import EditedBotBusinessMessageHandler
|
||||
from .error_handler import ErrorHandler
|
||||
from .inline_query_handler import InlineQueryHandler
|
||||
from .message_handler import MessageHandler
|
||||
from .poll_handler import PollHandler
|
||||
|
|
|
|||
79
pyrogram/handlers/error_handler.py
Normal file
79
pyrogram/handlers/error_handler.py
Normal 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)
|
||||
|
|
@ -35,6 +35,10 @@ class RawUpdateHandler(Handler):
|
|||
*(client, update, users, chats)* as positional arguments (look at the section below for
|
||||
a detailed description).
|
||||
|
||||
filters (:obj:`Filters`):
|
||||
Pass one or more filters to allow only a subset of callback queries to be passed
|
||||
in your callback function.
|
||||
|
||||
Other Parameters:
|
||||
client (:obj:`~pyrogram.Client`):
|
||||
The Client itself, useful when you want to call other API methods inside the update handler.
|
||||
|
|
@ -64,5 +68,5 @@ class RawUpdateHandler(Handler):
|
|||
- :obj:`~pyrogram.raw.types.ChannelForbidden`
|
||||
"""
|
||||
|
||||
def __init__(self, callback: Callable):
|
||||
super().__init__(callback)
|
||||
def __init__(self, callback: Callable, filters=None):
|
||||
super().__init__(callback, filters)
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ from .set_bot_default_privileges import SetBotDefaultPrivileges
|
|||
from .set_bot_info import SetBotInfo
|
||||
from .set_chat_menu_button import SetChatMenuButton
|
||||
from .set_game_score import SetGameScore
|
||||
from .get_owned_bots import GetOwnedBots
|
||||
|
||||
|
||||
class Bots(
|
||||
|
|
@ -57,6 +58,7 @@ class Bots(
|
|||
SetChatMenuButton,
|
||||
GetChatMenuButton,
|
||||
AnswerWebAppQuery,
|
||||
GetCollectibleItemInfo
|
||||
GetCollectibleItemInfo,
|
||||
GetOwnedBots,
|
||||
):
|
||||
pass
|
||||
|
|
|
|||
46
pyrogram/methods/bots/get_owned_bots.py
Normal file
46
pyrogram/methods/bots/get_owned_bots.py
Normal 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
|
||||
])
|
||||
|
|
@ -22,14 +22,18 @@ from typing import Union
|
|||
import pyrogram
|
||||
from pyrogram import raw, types
|
||||
|
||||
from ..messages.inline_session import get_session
|
||||
|
||||
|
||||
class PinChatMessage:
|
||||
|
||||
async def pin_chat_message(
|
||||
self: "pyrogram.Client",
|
||||
chat_id: Union[int, str],
|
||||
message_id: int,
|
||||
disable_notification: bool = False,
|
||||
both_sides: bool = False,
|
||||
business_connection_id: str = None,
|
||||
) -> "types.Message":
|
||||
"""Pin a message in a group, channel or your own chat.
|
||||
You must be an administrator in the chat for this to work and must have the "can_pin_messages" admin right in
|
||||
|
|
@ -52,6 +56,9 @@ class PinChatMessage:
|
|||
both_sides (``bool``, *optional*):
|
||||
Pass True to pin the message for both sides (you and recipient).
|
||||
Applicable to private chats only. Defaults to False.
|
||||
|
||||
business_connection_id (``str``, *optional*):
|
||||
Unique identifier of the business connection on behalf of which the message will be pinned.
|
||||
|
||||
Returns:
|
||||
:obj:`~pyrogram.types.Message`: On success, the service message is returned.
|
||||
|
|
@ -65,19 +72,39 @@ class PinChatMessage:
|
|||
# Pin without notification
|
||||
await app.pin_chat_message(chat_id, message_id, disable_notification=True)
|
||||
"""
|
||||
r = await self.invoke(
|
||||
raw.functions.messages.UpdatePinnedMessage(
|
||||
peer=await self.resolve_peer(chat_id),
|
||||
id=message_id,
|
||||
silent=disable_notification or None,
|
||||
pm_oneside=not both_sides or None
|
||||
)
|
||||
rpc = raw.functions.messages.UpdatePinnedMessage(
|
||||
peer=await self.resolve_peer(chat_id),
|
||||
id=message_id,
|
||||
silent=disable_notification or None,
|
||||
pm_oneside=not both_sides or None
|
||||
)
|
||||
|
||||
session = None
|
||||
business_connection = None
|
||||
if business_connection_id:
|
||||
business_connection = self.business_user_connection_cache[business_connection_id]
|
||||
if not business_connection:
|
||||
business_connection = await self.get_business_connection(business_connection_id)
|
||||
session = await get_session(
|
||||
self,
|
||||
business_connection._raw.connection.dc_id
|
||||
)
|
||||
|
||||
if business_connection_id:
|
||||
r = await session.invoke(
|
||||
raw.functions.InvokeWithBusinessConnection(
|
||||
query=rpc,
|
||||
connection_id=business_connection_id
|
||||
)
|
||||
)
|
||||
else:
|
||||
r = await self.invoke(rpc)
|
||||
|
||||
users = {u.id: u for u in r.users}
|
||||
chats = {c.id: c for c in r.chats}
|
||||
|
||||
for i in r.updates:
|
||||
if isinstance(i, (raw.types.UpdateNewMessage,
|
||||
raw.types.UpdateNewChannelMessage)):
|
||||
return await types.Message._parse(self, i.message, users, chats)
|
||||
raw.types.UpdateNewChannelMessage,
|
||||
raw.types.UpdateBotNewBusinessMessage)):
|
||||
return await types.Message._parse(self, i.message, users, chats, business_connection_id=business_connection_id)
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ class UnpinAllChatMessages:
|
|||
async def unpin_all_chat_messages(
|
||||
self: "pyrogram.Client",
|
||||
chat_id: Union[int, str],
|
||||
message_thread_id: int = None,
|
||||
) -> bool:
|
||||
"""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
|
||||
|
|
@ -39,6 +40,9 @@ class UnpinAllChatMessages:
|
|||
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_thread_id (``int``, *optional*):
|
||||
Unique identifier for the target message thread of the forum topic
|
||||
|
||||
Returns:
|
||||
``bool``: True on success.
|
||||
|
||||
|
|
@ -48,10 +52,10 @@ class UnpinAllChatMessages:
|
|||
# Unpin all chat messages
|
||||
await app.unpin_all_chat_messages(chat_id)
|
||||
"""
|
||||
await self.invoke(
|
||||
r = await self.invoke(
|
||||
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 True
|
||||
return r.pts_count
|
||||
|
|
|
|||
|
|
@ -22,12 +22,15 @@ from typing import Union
|
|||
import pyrogram
|
||||
from pyrogram import raw
|
||||
|
||||
from ..messages.inline_session import get_session
|
||||
|
||||
|
||||
class UnpinChatMessage:
|
||||
async def unpin_chat_message(
|
||||
self: "pyrogram.Client",
|
||||
chat_id: Union[int, str],
|
||||
message_id: int = 0
|
||||
message_id: int = 0,
|
||||
business_connection_id: str = None,
|
||||
) -> bool:
|
||||
"""Unpin a message in a group, channel or your own chat.
|
||||
You must be an administrator in the chat for this to work and must have the "can_pin_messages" admin
|
||||
|
|
@ -38,12 +41,15 @@ class UnpinChatMessage:
|
|||
Parameters:
|
||||
chat_id (``int`` | ``str``):
|
||||
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*):
|
||||
Identifier of a message to unpin.
|
||||
Required if ``business_connection_id`` is specified.
|
||||
If not specified, the most recent pinned message (by sending date) will be unpinned.
|
||||
|
||||
business_connection_id (``str``, *optional*):
|
||||
Unique identifier of the business connection on behalf of which the message will be unpinned.
|
||||
|
||||
Returns:
|
||||
``bool``: True on success.
|
||||
|
||||
|
|
@ -52,12 +58,32 @@ class UnpinChatMessage:
|
|||
|
||||
await app.unpin_chat_message(chat_id, message_id)
|
||||
"""
|
||||
await self.invoke(
|
||||
raw.functions.messages.UpdatePinnedMessage(
|
||||
peer=await self.resolve_peer(chat_id),
|
||||
id=message_id,
|
||||
unpin=True
|
||||
)
|
||||
rpc = raw.functions.messages.UpdatePinnedMessage(
|
||||
peer=await self.resolve_peer(chat_id),
|
||||
id=message_id,
|
||||
unpin=True
|
||||
)
|
||||
|
||||
session = None
|
||||
business_connection = None
|
||||
if business_connection_id:
|
||||
business_connection = self.business_user_connection_cache[business_connection_id]
|
||||
if not business_connection:
|
||||
business_connection = await self.get_business_connection(business_connection_id)
|
||||
session = await get_session(
|
||||
self,
|
||||
business_connection._raw.connection.dc_id
|
||||
)
|
||||
|
||||
if business_connection_id:
|
||||
await session.invoke(
|
||||
raw.functions.InvokeWithBusinessConnection(
|
||||
query=rpc,
|
||||
connection_id=business_connection_id
|
||||
)
|
||||
)
|
||||
else:
|
||||
await self.invoke(rpc)
|
||||
|
||||
return True
|
||||
|
||||
|
|
@ -28,6 +28,7 @@ from .on_deleted_bot_business_messages import OnDeletedBotBusinessMessages
|
|||
from .on_disconnect import OnDisconnect
|
||||
from .on_edited_message import OnEditedMessage
|
||||
from .on_edited_bot_business_message import OnEditedBotBusinessMessage
|
||||
from .on_error import OnError
|
||||
from .on_inline_query import OnInlineQuery
|
||||
from .on_message import OnMessage
|
||||
from .on_poll import OnPoll
|
||||
|
|
@ -47,6 +48,7 @@ class Decorators(
|
|||
OnBotBusinessMessage,
|
||||
OnEditedMessage,
|
||||
OnEditedBotBusinessMessage,
|
||||
OnError,
|
||||
OnDeletedMessages,
|
||||
OnDeletedBotBusinessMessages,
|
||||
OnCallbackQuery,
|
||||
|
|
|
|||
50
pyrogram/methods/decorators/on_error.py
Normal file
50
pyrogram/methods/decorators/on_error.py
Normal 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
|
||||
|
|
@ -17,15 +17,17 @@
|
|||
# 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
|
||||
from typing import Callable, Optional
|
||||
|
||||
import pyrogram
|
||||
from pyrogram.filters import Filter
|
||||
|
||||
|
||||
class OnRawUpdate:
|
||||
def on_raw_update(
|
||||
self=None,
|
||||
group: int = 0
|
||||
self: Optional["OnRawUpdate"] = None,
|
||||
filters=None,
|
||||
group: int = 0,
|
||||
) -> Callable:
|
||||
"""Decorator for handling raw updates.
|
||||
|
||||
|
|
@ -33,24 +35,28 @@ class OnRawUpdate:
|
|||
:obj:`~pyrogram.handlers.RawUpdateHandler`.
|
||||
|
||||
Parameters:
|
||||
filters (:obj:`~pyrogram.filters`, *optional*):
|
||||
Pass one or more filters to allow only a subset of callback queries to be passed
|
||||
in your function.
|
||||
|
||||
group (``int``, *optional*):
|
||||
The group identifier, defaults to 0.
|
||||
"""
|
||||
|
||||
def decorator(func: Callable) -> Callable:
|
||||
if isinstance(self, pyrogram.Client):
|
||||
self.add_handler(pyrogram.handlers.RawUpdateHandler(func), group)
|
||||
else:
|
||||
self.add_handler(pyrogram.handlers.RawUpdateHandler(func, filters), group)
|
||||
elif isinstance(self, Filter) or self is None:
|
||||
if not hasattr(func, "handlers"):
|
||||
func.handlers = []
|
||||
|
||||
func.handlers.append(
|
||||
(
|
||||
pyrogram.handlers.RawUpdateHandler(func),
|
||||
group
|
||||
pyrogram.handlers.RawUpdateHandler(func, self),
|
||||
group if filters is None else filters
|
||||
)
|
||||
)
|
||||
|
||||
return func
|
||||
|
||||
return decorator
|
||||
return decorator
|
||||
|
|
@ -30,6 +30,7 @@ from .edit_message_caption import EditMessageCaption
|
|||
from .edit_message_media import EditMessageMedia
|
||||
from .edit_message_reply_markup import EditMessageReplyMarkup
|
||||
from .edit_message_text import EditMessageText
|
||||
from .forward_media_group import ForwardMediaGroup
|
||||
from .forward_messages import ForwardMessages
|
||||
from .get_available_effects import GetAvailableEffects
|
||||
from .get_chat_history import GetChatHistory
|
||||
|
|
@ -81,6 +82,7 @@ class Messages(
|
|||
EditMessageReplyMarkup,
|
||||
EditMessageMedia,
|
||||
EditMessageText,
|
||||
ForwardMediaGroup,
|
||||
ForwardMessages,
|
||||
GetAvailableEffects,
|
||||
GetMediaGroup,
|
||||
|
|
|
|||
120
pyrogram/methods/messages/forward_media_group.py
Normal file
120
pyrogram/methods/messages/forward_media_group.py
Normal 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)
|
||||
|
|
@ -20,6 +20,7 @@
|
|||
from .add_handler import AddHandler
|
||||
from .export_session_string import ExportSessionString
|
||||
from .remove_handler import RemoveHandler
|
||||
from .remove_error_handler import RemoveErrorHandler
|
||||
from .restart import Restart
|
||||
from .run import Run
|
||||
from .run_sync import RunSync
|
||||
|
|
@ -32,6 +33,7 @@ class Utilities(
|
|||
AddHandler,
|
||||
ExportSessionString,
|
||||
RemoveHandler,
|
||||
RemoveErrorHandler,
|
||||
Restart,
|
||||
Run,
|
||||
RunSync,
|
||||
|
|
|
|||
42
pyrogram/methods/utilities/remove_error_handler.py
Normal file
42
pyrogram/methods/utilities/remove_error_handler.py
Normal 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)
|
||||
|
|
@ -37,7 +37,7 @@ PRE_DELIM = "```"
|
|||
BLOCKQUOTE_DELIM = ">"
|
||||
BLOCKQUOTE_EXPANDABLE_DELIM = "**>"
|
||||
|
||||
MARKDOWN_RE = re.compile(r"({d})|\[(.+?)\]\((.+?)\)".format(
|
||||
MARKDOWN_RE = re.compile(r"({d})|(!?)\[(.+?)\]\((.+?)\)".format(
|
||||
d="|".join(
|
||||
["".join(i) for i in [
|
||||
[rf"\{j}" for j in i]
|
||||
|
|
@ -56,6 +56,7 @@ MARKDOWN_RE = re.compile(r"({d})|\[(.+?)\]\((.+?)\)".format(
|
|||
OPENING_TAG = "<{}>"
|
||||
CLOSING_TAG = "</{}>"
|
||||
URL_MARKUP = '<a href="{}">{}</a>'
|
||||
EMOJI_MARKUP = '<emoji id={}>{}</emoji>'
|
||||
FIXED_WIDTH_DELIMS = [CODE_DELIM, PRE_DELIM]
|
||||
CODE_TAG_RE = re.compile(r"<code>.*?</code>")
|
||||
|
||||
|
|
@ -117,7 +118,7 @@ class Markdown:
|
|||
|
||||
for i, match in enumerate(re.finditer(MARKDOWN_RE, text)):
|
||||
start, _ = match.span()
|
||||
delim, text_url, url = match.groups()
|
||||
delim, is_emoji, text_url, url = match.groups()
|
||||
full = match.group(0)
|
||||
|
||||
if delim in FIXED_WIDTH_DELIMS:
|
||||
|
|
@ -126,10 +127,16 @@ class Markdown:
|
|||
if is_fixed_width and delim not in FIXED_WIDTH_DELIMS:
|
||||
continue
|
||||
|
||||
if text_url:
|
||||
if not is_emoji and text_url:
|
||||
text = utils.replace_once(text, full, URL_MARKUP.format(url, text_url), start)
|
||||
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:
|
||||
tag = "b"
|
||||
elif delim == ITALIC_DELIM:
|
||||
|
|
@ -221,6 +228,10 @@ class Markdown:
|
|||
user = entity.user
|
||||
start_tag = "["
|
||||
end_tag = f"](tg://user?id={user.id})"
|
||||
elif entity_type == MessageEntityType.CUSTOM_EMOJI:
|
||||
emoji_id = entity.custom_emoji_id
|
||||
start_tag = ""
|
||||
else:
|
||||
continue
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
# 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 .alternative_video import AlternativeVideo
|
||||
from .animation import Animation
|
||||
from .audio import Audio
|
||||
from .available_effect import AvailableEffect
|
||||
|
|
@ -71,6 +72,7 @@ from .wallpaper_settings import WallpaperSettings
|
|||
from .translated_text import TranslatedText
|
||||
|
||||
__all__ = [
|
||||
"AlternativeVideo",
|
||||
"Animation",
|
||||
"Audio",
|
||||
"AvailableEffect",
|
||||
|
|
|
|||
134
pyrogram/types/messages_and_media/alternative_video.py
Normal file
134
pyrogram/types/messages_and_media/alternative_video.py
Normal 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
|
||||
)
|
||||
|
|
@ -506,6 +506,7 @@ class Message(Object, Update):
|
|||
screenshot_taken: "types.ScreenshotTaken" = None,
|
||||
invoice: "types.Invoice" = None,
|
||||
story: Union["types.MessageStory", "types.Story"] = None,
|
||||
alternative_videos: List["types.AlternativeVideo"] = None,
|
||||
video: "types.Video" = None,
|
||||
voice: "types.Voice" = None,
|
||||
video_note: "types.VideoNote" = None,
|
||||
|
|
@ -628,6 +629,7 @@ class Message(Object, Update):
|
|||
self.invoice = invoice
|
||||
self.story = story
|
||||
self.video = video
|
||||
self.alternative_videos = alternative_videos
|
||||
self.voice = voice
|
||||
self.video_note = video_note
|
||||
self.web_page_preview = web_page_preview
|
||||
|
|
@ -1055,6 +1057,7 @@ class Message(Object, Update):
|
|||
voice = None
|
||||
animation = None
|
||||
video = None
|
||||
alternative_videos = []
|
||||
video_note = None
|
||||
web_page_preview = None
|
||||
sticker = None
|
||||
|
|
@ -1122,6 +1125,22 @@ class Message(Object, Update):
|
|||
video = types.Video._parse(client, doc, video_attributes, file_name, media.ttl_seconds)
|
||||
media_type = enums.MessageMediaType.VIDEO
|
||||
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:
|
||||
audio_attributes = attributes[raw.types.DocumentAttributeAudio]
|
||||
|
||||
|
|
@ -1238,6 +1257,7 @@ class Message(Object, Update):
|
|||
invoice=invoice,
|
||||
story=story,
|
||||
video=video,
|
||||
alternative_videos=types.List(alternative_videos) if alternative_videos else None,
|
||||
video_note=video_note,
|
||||
web_page_preview=web_page_preview,
|
||||
sticker=sticker,
|
||||
|
|
|
|||
Loading…
Reference in a new issue