WPyrogram 2.0.153__py3-none-any.whl → 2.0.154__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyrogram/__init__.py +1 -1
- pyrogram/client.py +13 -2
- pyrogram/errors/exceptions/__init__.py +3 -3
- pyrogram/errors/exceptions/all.py +67 -67
- pyrogram/helper_bot.py +325 -0
- pyrogram/methods/advanced/invoke.py +14 -3
- pyrogram/methods/bots/send_game.py +4 -7
- pyrogram/methods/bots/send_invoice.py +3 -7
- pyrogram/methods/messages/edit_inline_media.py +4 -2
- pyrogram/methods/messages/edit_inline_reply_markup.py +4 -2
- pyrogram/methods/messages/edit_inline_text.py +3 -2
- pyrogram/methods/messages/edit_message_media.py +4 -2
- pyrogram/methods/messages/edit_message_reply_markup.py +4 -2
- pyrogram/methods/messages/edit_message_text.py +3 -2
- pyrogram/methods/messages/send_animation.py +41 -30
- pyrogram/methods/messages/send_audio.py +32 -20
- pyrogram/methods/messages/send_cached_media.py +3 -7
- pyrogram/methods/messages/send_contact.py +3 -7
- pyrogram/methods/messages/send_dice.py +3 -7
- pyrogram/methods/messages/send_document.py +32 -20
- pyrogram/methods/messages/send_location.py +3 -7
- pyrogram/methods/messages/send_message.py +7 -8
- pyrogram/methods/messages/send_photo.py +32 -20
- pyrogram/methods/messages/send_poll.py +3 -9
- pyrogram/methods/messages/send_sticker.py +32 -20
- pyrogram/methods/messages/send_venue.py +3 -7
- pyrogram/methods/messages/send_video.py +32 -20
- pyrogram/methods/messages/send_video_note.py +32 -20
- pyrogram/methods/messages/send_voice.py +32 -20
- pyrogram/methods/messages/send_web_page.py +3 -7
- pyrogram/methods/messages/stop_poll.py +5 -2
- pyrogram/reply_markup.py +70 -0
- pyrogram/types/bots_and_keyboards/inline_keyboard_markup.py +11 -1
- pyrogram/types/messages_and_media/message.py +8 -8
- pyrogram/types/user_and_chats/emoji_status.py +2 -13
- pyrogram/utils.py +1 -0
- {wpyrogram-2.0.153.dist-info → wpyrogram-2.0.154.dist-info}/METADATA +1 -1
- {wpyrogram-2.0.153.dist-info → wpyrogram-2.0.154.dist-info}/RECORD +43 -41
- {wpyrogram-2.0.153.dist-info → wpyrogram-2.0.154.dist-info}/WHEEL +1 -1
- {wpyrogram-2.0.153.dist-info → wpyrogram-2.0.154.dist-info}/licenses/COPYING +0 -0
- {wpyrogram-2.0.153.dist-info → wpyrogram-2.0.154.dist-info}/licenses/COPYING.lesser +0 -0
- {wpyrogram-2.0.153.dist-info → wpyrogram-2.0.154.dist-info}/licenses/NOTICE +0 -0
- {wpyrogram-2.0.153.dist-info → wpyrogram-2.0.154.dist-info}/top_level.txt +0 -0
pyrogram/__init__.py
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
# You should have received a copy of the GNU Lesser General Public License
|
|
17
17
|
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
|
|
18
18
|
|
|
19
|
-
__version__ = "2.0.
|
|
19
|
+
__version__ = "2.0.154"
|
|
20
20
|
__license__ = "GNU Lesser General Public License v3.0 (LGPL-3.0)"
|
|
21
21
|
__copyright__ = "Copyright (C) 2017-present Dan <https://github.com/delivrance>"
|
|
22
22
|
|
pyrogram/client.py
CHANGED
|
@@ -32,7 +32,7 @@ from importlib import import_module
|
|
|
32
32
|
from io import StringIO, BytesIO
|
|
33
33
|
from mimetypes import MimeTypes
|
|
34
34
|
from pathlib import Path
|
|
35
|
-
from typing import Union, List, Optional, Callable, AsyncGenerator, Type, Tuple
|
|
35
|
+
from typing import Union, List, Optional, Callable, AsyncGenerator, Type, Tuple, Dict
|
|
36
36
|
|
|
37
37
|
import pyrogram
|
|
38
38
|
from pyrogram import __version__, __license__
|
|
@@ -58,6 +58,7 @@ from .connection import Connection
|
|
|
58
58
|
from .connection.transport import TCP, TCPAbridged
|
|
59
59
|
from .dispatcher import Dispatcher
|
|
60
60
|
from .file_id import FileId, FileType, ThumbnailSource
|
|
61
|
+
from .helper_bot import HelperBotQuery, answer_helper_bot_query
|
|
61
62
|
from .mime_types import mime_types
|
|
62
63
|
from .parser import Parser
|
|
63
64
|
from .session.internals import MsgId
|
|
@@ -269,7 +270,9 @@ class Client(Methods):
|
|
|
269
270
|
client_platform: "enums.ClientPlatform" = enums.ClientPlatform.OTHER,
|
|
270
271
|
init_connection_params: Optional["raw.base.JSONValue"] = None,
|
|
271
272
|
connection_factory: Type[Connection] = Connection,
|
|
272
|
-
protocol_factory: Type[TCP] = TCPAbridged
|
|
273
|
+
protocol_factory: Type[TCP] = TCPAbridged,
|
|
274
|
+
helper_bot: Optional["Client"] = None,
|
|
275
|
+
helper_bot_chat_id: Optional[Union[int, str]] = None
|
|
273
276
|
):
|
|
274
277
|
super().__init__()
|
|
275
278
|
|
|
@@ -306,6 +309,9 @@ class Client(Methods):
|
|
|
306
309
|
self.init_connection_params = init_connection_params
|
|
307
310
|
self.connection_factory = connection_factory
|
|
308
311
|
self.protocol_factory = protocol_factory
|
|
312
|
+
self.helper_bot = helper_bot
|
|
313
|
+
self.helper_bot_chat_id = helper_bot_chat_id
|
|
314
|
+
self._helper_bot_queries: Dict[str, HelperBotQuery] = {}
|
|
309
315
|
|
|
310
316
|
self.executor = ThreadPoolExecutor(self.workers, thread_name_prefix="Handler")
|
|
311
317
|
|
|
@@ -660,6 +666,9 @@ class Client(Methods):
|
|
|
660
666
|
users.update({u.id: u for u in diff.users})
|
|
661
667
|
chats.update({c.id: c for c in diff.chats})
|
|
662
668
|
|
|
669
|
+
# Internal inline replies must not wait for a busy application handler.
|
|
670
|
+
if await answer_helper_bot_query(self, update):
|
|
671
|
+
continue
|
|
663
672
|
self.dispatcher.updates_queue.put_nowait((update, users, chats))
|
|
664
673
|
elif isinstance(updates, (raw.types.UpdateShortMessage, raw.types.UpdateShortChatMessage)):
|
|
665
674
|
if not self.skip_updates:
|
|
@@ -695,6 +704,8 @@ class Client(Methods):
|
|
|
695
704
|
if diff.other_updates: # The other_updates list can be empty
|
|
696
705
|
self.dispatcher.updates_queue.put_nowait((diff.other_updates[0], {}, {}))
|
|
697
706
|
elif isinstance(updates, raw.types.UpdateShort):
|
|
707
|
+
if await answer_helper_bot_query(self, updates.update):
|
|
708
|
+
return
|
|
698
709
|
self.dispatcher.updates_queue.put_nowait((updates.update, {}, {}))
|
|
699
710
|
elif isinstance(updates, raw.types.UpdatesTooLong):
|
|
700
711
|
log.info(updates)
|
|
@@ -16,11 +16,11 @@
|
|
|
16
16
|
# You should have received a copy of the GNU Lesser General Public License
|
|
17
17
|
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
|
|
18
18
|
|
|
19
|
-
from .bad_request_400 import *
|
|
20
|
-
from .unauthorized_401 import *
|
|
21
19
|
from .forbidden_403 import *
|
|
22
|
-
from .see_other_303 import *
|
|
23
20
|
from .service_unavailable_503 import *
|
|
21
|
+
from .bad_request_400 import *
|
|
22
|
+
from .unauthorized_401 import *
|
|
24
23
|
from .not_acceptable_406 import *
|
|
25
24
|
from .internal_server_error_500 import *
|
|
25
|
+
from .see_other_303 import *
|
|
26
26
|
from .flood_420 import *
|
|
@@ -19,6 +19,65 @@
|
|
|
19
19
|
count = 736
|
|
20
20
|
|
|
21
21
|
exceptions = {
|
|
22
|
+
403: {
|
|
23
|
+
"_": "Forbidden",
|
|
24
|
+
"ANONYMOUS_REACTIONS_DISABLED": "AnonymousReactionsDisabled",
|
|
25
|
+
"BROADCAST_FORBIDDEN": "BroadcastForbidden",
|
|
26
|
+
"CHANNEL_PUBLIC_GROUP_NA": "ChannelPublicGroupNa",
|
|
27
|
+
"CHAT_ACTION_FORBIDDEN": "ChatActionForbidden",
|
|
28
|
+
"CHAT_ADMIN_INVITE_REQUIRED": "ChatAdminInviteRequired",
|
|
29
|
+
"CHAT_ADMIN_REQUIRED": "ChatAdminRequired",
|
|
30
|
+
"CHAT_FORBIDDEN": "ChatForbidden",
|
|
31
|
+
"CHAT_GUEST_SEND_FORBIDDEN": "ChatGuestSendForbidden",
|
|
32
|
+
"CHAT_SEND_AUDIOS_FORBIDDEN": "ChatSendAudiosForbidden",
|
|
33
|
+
"CHAT_SEND_DOCS_FORBIDDEN": "ChatSendDocsForbidden",
|
|
34
|
+
"CHAT_SEND_GAME_FORBIDDEN": "ChatSendGameForbidden",
|
|
35
|
+
"CHAT_SEND_GIFS_FORBIDDEN": "ChatSendGifsForbidden",
|
|
36
|
+
"CHAT_SEND_INLINE_FORBIDDEN": "ChatSendInlineForbidden",
|
|
37
|
+
"CHAT_SEND_MEDIA_FORBIDDEN": "ChatSendMediaForbidden",
|
|
38
|
+
"CHAT_SEND_PHOTOS_FORBIDDEN": "ChatSendPhotosForbidden",
|
|
39
|
+
"CHAT_SEND_PLAIN_FORBIDDEN": "ChatSendPlainForbidden",
|
|
40
|
+
"CHAT_SEND_POLL_FORBIDDEN": "ChatSendPollForbidden",
|
|
41
|
+
"CHAT_SEND_ROUNDVIDEOS_FORBIDDEN": "ChatSendRoundvideosForbidden",
|
|
42
|
+
"CHAT_SEND_STICKERS_FORBIDDEN": "ChatSendStickersForbidden",
|
|
43
|
+
"CHAT_SEND_VIDEOS_FORBIDDEN": "ChatSendVideosForbidden",
|
|
44
|
+
"CHAT_SEND_VOICES_FORBIDDEN": "ChatSendVoicesForbidden",
|
|
45
|
+
"CHAT_WRITE_FORBIDDEN": "ChatWriteForbidden",
|
|
46
|
+
"EDIT_BOT_INVITE_FORBIDDEN": "EditBotInviteForbidden",
|
|
47
|
+
"GROUPCALL_ALREADY_STARTED": "GroupcallAlreadyStarted",
|
|
48
|
+
"GROUPCALL_FORBIDDEN": "GroupcallForbidden",
|
|
49
|
+
"INLINE_BOT_REQUIRED": "InlineBotRequired",
|
|
50
|
+
"LIVE_DISABLED": "LiveDisabled",
|
|
51
|
+
"MESSAGE_AUTHOR_REQUIRED": "MessageAuthorRequired",
|
|
52
|
+
"MESSAGE_DELETE_FORBIDDEN": "MessageDeleteForbidden",
|
|
53
|
+
"NOT_ALLOWED": "NotAllowed",
|
|
54
|
+
"NOT_ELIGIBLE": "NotEligible",
|
|
55
|
+
"PARTICIPANT_JOIN_MISSING": "ParticipantJoinMissing",
|
|
56
|
+
"POLL_VOTE_REQUIRED": "PollVoteRequired",
|
|
57
|
+
"PREMIUM_ACCOUNT_REQUIRED": "PremiumAccountRequired",
|
|
58
|
+
"PRIVACY_PREMIUM_REQUIRED": "PrivacyPremiumRequired",
|
|
59
|
+
"PUBLIC_CHANNEL_MISSING": "PublicChannelMissing",
|
|
60
|
+
"RIGHT_FORBIDDEN": "RightForbidden",
|
|
61
|
+
"SENSITIVE_CHANGE_FORBIDDEN": "SensitiveChangeForbidden",
|
|
62
|
+
"TAKEOUT_REQUIRED": "TakeoutRequired",
|
|
63
|
+
"USER_BOT_INVALID": "UserBotInvalid",
|
|
64
|
+
"USER_CHANNELS_TOO_MUCH": "UserChannelsTooMuch",
|
|
65
|
+
"USER_DELETED": "UserDeleted",
|
|
66
|
+
"USER_INVALID": "UserInvalid",
|
|
67
|
+
"USER_IS_BLOCKED": "UserIsBlocked",
|
|
68
|
+
"USER_NOT_MUTUAL_CONTACT": "UserNotMutualContact",
|
|
69
|
+
"USER_NOT_PARTICIPANT": "UserNotParticipant",
|
|
70
|
+
"USER_PRIVACY_RESTRICTED": "UserPrivacyRestricted",
|
|
71
|
+
"USER_RESTRICTED": "UserRestricted",
|
|
72
|
+
"VOICE_MESSAGES_FORBIDDEN": "VoiceMessagesForbidden",
|
|
73
|
+
"YOUR_PRIVACY_RESTRICTED": "YourPrivacyRestricted",
|
|
74
|
+
},
|
|
75
|
+
503: {
|
|
76
|
+
"_": "ServiceUnavailable",
|
|
77
|
+
"ApiCallError": "ApiCallError",
|
|
78
|
+
"Timedout": "Timedout",
|
|
79
|
+
"Timeout": "Timeout",
|
|
80
|
+
},
|
|
22
81
|
400: {
|
|
23
82
|
"_": "BadRequest",
|
|
24
83
|
"ABOUT_TOO_LONG": "AboutTooLong",
|
|
@@ -612,73 +671,6 @@ exceptions = {
|
|
|
612
671
|
"USER_DEACTIVATED": "UserDeactivated",
|
|
613
672
|
"USER_DEACTIVATED_BAN": "UserDeactivatedBan",
|
|
614
673
|
},
|
|
615
|
-
403: {
|
|
616
|
-
"_": "Forbidden",
|
|
617
|
-
"ANONYMOUS_REACTIONS_DISABLED": "AnonymousReactionsDisabled",
|
|
618
|
-
"BROADCAST_FORBIDDEN": "BroadcastForbidden",
|
|
619
|
-
"CHANNEL_PUBLIC_GROUP_NA": "ChannelPublicGroupNa",
|
|
620
|
-
"CHAT_ACTION_FORBIDDEN": "ChatActionForbidden",
|
|
621
|
-
"CHAT_ADMIN_INVITE_REQUIRED": "ChatAdminInviteRequired",
|
|
622
|
-
"CHAT_ADMIN_REQUIRED": "ChatAdminRequired",
|
|
623
|
-
"CHAT_FORBIDDEN": "ChatForbidden",
|
|
624
|
-
"CHAT_GUEST_SEND_FORBIDDEN": "ChatGuestSendForbidden",
|
|
625
|
-
"CHAT_SEND_AUDIOS_FORBIDDEN": "ChatSendAudiosForbidden",
|
|
626
|
-
"CHAT_SEND_DOCS_FORBIDDEN": "ChatSendDocsForbidden",
|
|
627
|
-
"CHAT_SEND_GAME_FORBIDDEN": "ChatSendGameForbidden",
|
|
628
|
-
"CHAT_SEND_GIFS_FORBIDDEN": "ChatSendGifsForbidden",
|
|
629
|
-
"CHAT_SEND_INLINE_FORBIDDEN": "ChatSendInlineForbidden",
|
|
630
|
-
"CHAT_SEND_MEDIA_FORBIDDEN": "ChatSendMediaForbidden",
|
|
631
|
-
"CHAT_SEND_PHOTOS_FORBIDDEN": "ChatSendPhotosForbidden",
|
|
632
|
-
"CHAT_SEND_PLAIN_FORBIDDEN": "ChatSendPlainForbidden",
|
|
633
|
-
"CHAT_SEND_POLL_FORBIDDEN": "ChatSendPollForbidden",
|
|
634
|
-
"CHAT_SEND_ROUNDVIDEOS_FORBIDDEN": "ChatSendRoundvideosForbidden",
|
|
635
|
-
"CHAT_SEND_STICKERS_FORBIDDEN": "ChatSendStickersForbidden",
|
|
636
|
-
"CHAT_SEND_VIDEOS_FORBIDDEN": "ChatSendVideosForbidden",
|
|
637
|
-
"CHAT_SEND_VOICES_FORBIDDEN": "ChatSendVoicesForbidden",
|
|
638
|
-
"CHAT_WRITE_FORBIDDEN": "ChatWriteForbidden",
|
|
639
|
-
"EDIT_BOT_INVITE_FORBIDDEN": "EditBotInviteForbidden",
|
|
640
|
-
"GROUPCALL_ALREADY_STARTED": "GroupcallAlreadyStarted",
|
|
641
|
-
"GROUPCALL_FORBIDDEN": "GroupcallForbidden",
|
|
642
|
-
"INLINE_BOT_REQUIRED": "InlineBotRequired",
|
|
643
|
-
"LIVE_DISABLED": "LiveDisabled",
|
|
644
|
-
"MESSAGE_AUTHOR_REQUIRED": "MessageAuthorRequired",
|
|
645
|
-
"MESSAGE_DELETE_FORBIDDEN": "MessageDeleteForbidden",
|
|
646
|
-
"NOT_ALLOWED": "NotAllowed",
|
|
647
|
-
"NOT_ELIGIBLE": "NotEligible",
|
|
648
|
-
"PARTICIPANT_JOIN_MISSING": "ParticipantJoinMissing",
|
|
649
|
-
"POLL_VOTE_REQUIRED": "PollVoteRequired",
|
|
650
|
-
"PREMIUM_ACCOUNT_REQUIRED": "PremiumAccountRequired",
|
|
651
|
-
"PRIVACY_PREMIUM_REQUIRED": "PrivacyPremiumRequired",
|
|
652
|
-
"PUBLIC_CHANNEL_MISSING": "PublicChannelMissing",
|
|
653
|
-
"RIGHT_FORBIDDEN": "RightForbidden",
|
|
654
|
-
"SENSITIVE_CHANGE_FORBIDDEN": "SensitiveChangeForbidden",
|
|
655
|
-
"TAKEOUT_REQUIRED": "TakeoutRequired",
|
|
656
|
-
"USER_BOT_INVALID": "UserBotInvalid",
|
|
657
|
-
"USER_CHANNELS_TOO_MUCH": "UserChannelsTooMuch",
|
|
658
|
-
"USER_DELETED": "UserDeleted",
|
|
659
|
-
"USER_INVALID": "UserInvalid",
|
|
660
|
-
"USER_IS_BLOCKED": "UserIsBlocked",
|
|
661
|
-
"USER_NOT_MUTUAL_CONTACT": "UserNotMutualContact",
|
|
662
|
-
"USER_NOT_PARTICIPANT": "UserNotParticipant",
|
|
663
|
-
"USER_PRIVACY_RESTRICTED": "UserPrivacyRestricted",
|
|
664
|
-
"USER_RESTRICTED": "UserRestricted",
|
|
665
|
-
"VOICE_MESSAGES_FORBIDDEN": "VoiceMessagesForbidden",
|
|
666
|
-
"YOUR_PRIVACY_RESTRICTED": "YourPrivacyRestricted",
|
|
667
|
-
},
|
|
668
|
-
303: {
|
|
669
|
-
"_": "SeeOther",
|
|
670
|
-
"FILE_MIGRATE_X": "FileMigrate",
|
|
671
|
-
"NETWORK_MIGRATE_X": "NetworkMigrate",
|
|
672
|
-
"PHONE_MIGRATE_X": "PhoneMigrate",
|
|
673
|
-
"STATS_MIGRATE_X": "StatsMigrate",
|
|
674
|
-
"USER_MIGRATE_X": "UserMigrate",
|
|
675
|
-
},
|
|
676
|
-
503: {
|
|
677
|
-
"_": "ServiceUnavailable",
|
|
678
|
-
"ApiCallError": "ApiCallError",
|
|
679
|
-
"Timedout": "Timedout",
|
|
680
|
-
"Timeout": "Timeout",
|
|
681
|
-
},
|
|
682
674
|
406: {
|
|
683
675
|
"_": "NotAcceptable",
|
|
684
676
|
"AUTH_KEY_DUPLICATED": "AuthKeyDuplicated",
|
|
@@ -767,6 +759,14 @@ exceptions = {
|
|
|
767
759
|
"WORKER_BUSY_TOO_LONG_RETRY": "WorkerBusyTooLongRetry",
|
|
768
760
|
"WP_ID_GENERATE_FAILED": "WpIdGenerateFailed",
|
|
769
761
|
},
|
|
762
|
+
303: {
|
|
763
|
+
"_": "SeeOther",
|
|
764
|
+
"FILE_MIGRATE_X": "FileMigrate",
|
|
765
|
+
"NETWORK_MIGRATE_X": "NetworkMigrate",
|
|
766
|
+
"PHONE_MIGRATE_X": "PhoneMigrate",
|
|
767
|
+
"STATS_MIGRATE_X": "StatsMigrate",
|
|
768
|
+
"USER_MIGRATE_X": "UserMigrate",
|
|
769
|
+
},
|
|
770
770
|
420: {
|
|
771
771
|
"_": "Flood",
|
|
772
772
|
"2FA_CONFIRM_WAIT_X": "TwoFaConfirmWait",
|
pyrogram/helper_bot.py
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
# pyright: strict
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from copy import copy
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from typing import TYPE_CHECKING, List, Optional, TypedDict, Union, cast
|
|
9
|
+
from uuid import uuid4
|
|
10
|
+
|
|
11
|
+
from pyrogram import enums, raw, types, utils
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from pyrogram import Client
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
log = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
SendRequest = Union[raw.functions.messages.SendMessage, raw.functions.messages.SendMedia]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class InvokeOptions(TypedDict):
|
|
24
|
+
retries: int
|
|
25
|
+
timeout: float
|
|
26
|
+
sleep_threshold: Optional[float]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class HelperBotQuery:
|
|
31
|
+
user_id: int
|
|
32
|
+
result: raw.base.InputBotInlineResult
|
|
33
|
+
error: Optional[Exception] = None
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
async def answer_helper_bot_query(client: Client, update: raw.base.Update) -> bool:
|
|
37
|
+
if not isinstance(update, raw.types.UpdateBotInlineQuery):
|
|
38
|
+
return False
|
|
39
|
+
|
|
40
|
+
pending = client._helper_bot_queries.get(update.query) # pyright: ignore[reportPrivateUsage]
|
|
41
|
+
if pending is None or pending.user_id != update.user_id:
|
|
42
|
+
return False
|
|
43
|
+
|
|
44
|
+
try:
|
|
45
|
+
await client.invoke(
|
|
46
|
+
raw.functions.messages.SetInlineBotResults(
|
|
47
|
+
query_id=update.query_id, results=[pending.result], cache_time=0, private=True
|
|
48
|
+
)
|
|
49
|
+
)
|
|
50
|
+
except Exception as error:
|
|
51
|
+
pending.error = error
|
|
52
|
+
log.exception("Failed to answer a helper bot query")
|
|
53
|
+
|
|
54
|
+
return True
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
async def send_with_helper_bot(
|
|
58
|
+
client: Client,
|
|
59
|
+
query: SendRequest,
|
|
60
|
+
retries: int,
|
|
61
|
+
timeout: float,
|
|
62
|
+
sleep_threshold: Optional[float],
|
|
63
|
+
) -> raw.base.Updates:
|
|
64
|
+
helper = client.helper_bot
|
|
65
|
+
if helper is None or not helper.is_connected or helper.me is None or not helper.me.is_bot:
|
|
66
|
+
raise ValueError("helper_bot must be a started bot Client")
|
|
67
|
+
if helper.no_updates or not helper.me.username:
|
|
68
|
+
raise ValueError("helper_bot needs a username and incoming updates enabled")
|
|
69
|
+
|
|
70
|
+
# Fail explicitly when the inline API cannot preserve a send option.
|
|
71
|
+
content_fields = {"message", "entities", "reply_markup", "no_webpage", "invert_media", "media"}
|
|
72
|
+
send_fields = {
|
|
73
|
+
"peer",
|
|
74
|
+
"random_id",
|
|
75
|
+
"silent",
|
|
76
|
+
"background",
|
|
77
|
+
"clear_draft",
|
|
78
|
+
"reply_to",
|
|
79
|
+
"schedule_date",
|
|
80
|
+
"send_as",
|
|
81
|
+
"quick_reply_shortcut",
|
|
82
|
+
"allow_paid_stars",
|
|
83
|
+
}
|
|
84
|
+
for name in query.__slots__:
|
|
85
|
+
if name not in send_fields and name not in content_fields:
|
|
86
|
+
if getattr(query, name) not in (None, False):
|
|
87
|
+
raise ValueError(f"The helper bot inline API does not support {name}")
|
|
88
|
+
|
|
89
|
+
invoke_options: InvokeOptions = {
|
|
90
|
+
"retries": retries,
|
|
91
|
+
"timeout": timeout,
|
|
92
|
+
"sleep_threshold": sleep_threshold,
|
|
93
|
+
}
|
|
94
|
+
result = await build_inline_result(client, helper, query, invoke_options)
|
|
95
|
+
if result is None:
|
|
96
|
+
# The staging send was not confirmed. Do not send or retry the destination message.
|
|
97
|
+
return raw.types.Updates(updates=[], users=[], chats=[], date=0, seq=0)
|
|
98
|
+
|
|
99
|
+
token = "wpy_helper_" + uuid4().hex
|
|
100
|
+
pending = HelperBotQuery(cast(types.User, client.me).id, result)
|
|
101
|
+
helper._helper_bot_queries[token] = pending # pyright: ignore[reportPrivateUsage]
|
|
102
|
+
try:
|
|
103
|
+
results = cast(
|
|
104
|
+
raw.types.messages.BotResults,
|
|
105
|
+
await client.invoke(
|
|
106
|
+
raw.functions.messages.GetInlineBotResults(
|
|
107
|
+
bot=cast(raw.base.InputUser, await client.resolve_peer(helper.me.username)),
|
|
108
|
+
peer=query.peer,
|
|
109
|
+
query=token,
|
|
110
|
+
offset="",
|
|
111
|
+
),
|
|
112
|
+
**invoke_options,
|
|
113
|
+
),
|
|
114
|
+
)
|
|
115
|
+
except Exception:
|
|
116
|
+
if pending.error is not None:
|
|
117
|
+
raise pending.error
|
|
118
|
+
raise
|
|
119
|
+
finally:
|
|
120
|
+
helper._helper_bot_queries.pop(token, None) # pyright: ignore[reportPrivateUsage]
|
|
121
|
+
|
|
122
|
+
if not any(item.id == result.id for item in results.results):
|
|
123
|
+
raise ValueError("The helper bot returned no matching inline result")
|
|
124
|
+
|
|
125
|
+
# Reuse the caller's random_id, including across transport and flood-wait retries.
|
|
126
|
+
return cast(
|
|
127
|
+
raw.base.Updates,
|
|
128
|
+
await client.invoke(
|
|
129
|
+
raw.functions.messages.SendInlineBotResult(
|
|
130
|
+
peer=query.peer,
|
|
131
|
+
random_id=query.random_id,
|
|
132
|
+
query_id=results.query_id,
|
|
133
|
+
id=result.id,
|
|
134
|
+
silent=query.silent,
|
|
135
|
+
background=query.background,
|
|
136
|
+
clear_draft=query.clear_draft,
|
|
137
|
+
reply_to=query.reply_to,
|
|
138
|
+
schedule_date=query.schedule_date,
|
|
139
|
+
send_as=query.send_as,
|
|
140
|
+
quick_reply_shortcut=query.quick_reply_shortcut,
|
|
141
|
+
allow_paid_stars=query.allow_paid_stars,
|
|
142
|
+
),
|
|
143
|
+
**invoke_options,
|
|
144
|
+
),
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
async def build_inline_result(
|
|
149
|
+
client: Client, helper: Client, query: SendRequest, invoke_options: InvokeOptions
|
|
150
|
+
) -> Optional[raw.base.InputBotInlineResult]:
|
|
151
|
+
entities: List[raw.base.MessageEntity] = []
|
|
152
|
+
for entity in query.entities or []:
|
|
153
|
+
if isinstance(entity, raw.types.InputMessageEntityMentionName):
|
|
154
|
+
entity = copy(entity)
|
|
155
|
+
if isinstance(entity.user_id, raw.types.InputUserEmpty):
|
|
156
|
+
raise ValueError("A helper bot mention requires a user ID")
|
|
157
|
+
user_id = (
|
|
158
|
+
cast(types.User, client.me).id
|
|
159
|
+
if isinstance(entity.user_id, raw.types.InputUserSelf)
|
|
160
|
+
else entity.user_id.user_id
|
|
161
|
+
)
|
|
162
|
+
entity.user_id = cast(raw.base.InputUser, await helper.resolve_peer(user_id))
|
|
163
|
+
entities.append(entity)
|
|
164
|
+
|
|
165
|
+
if isinstance(query, raw.functions.messages.SendMessage):
|
|
166
|
+
return raw.types.InputBotInlineResult(
|
|
167
|
+
id="message",
|
|
168
|
+
type="article",
|
|
169
|
+
title="Message",
|
|
170
|
+
send_message=raw.types.InputBotInlineMessageText(
|
|
171
|
+
no_webpage=query.no_webpage,
|
|
172
|
+
message=query.message,
|
|
173
|
+
entities=entities or None,
|
|
174
|
+
reply_markup=query.reply_markup,
|
|
175
|
+
invert_media=query.invert_media,
|
|
176
|
+
),
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
media = query.media
|
|
180
|
+
inline_message: Optional[raw.base.InputBotInlineMessage] = None
|
|
181
|
+
result_type = "article"
|
|
182
|
+
if isinstance(media, (raw.types.InputMediaGeoPoint, raw.types.InputMediaGeoLive)):
|
|
183
|
+
if isinstance(media, raw.types.InputMediaGeoLive) and media.stopped:
|
|
184
|
+
raise ValueError("The helper bot cannot send a stopped live location")
|
|
185
|
+
result_type = "geo"
|
|
186
|
+
inline_message = raw.types.InputBotInlineMessageMediaGeo(
|
|
187
|
+
geo_point=media.geo_point,
|
|
188
|
+
heading=media.heading if isinstance(media, raw.types.InputMediaGeoLive) else None,
|
|
189
|
+
period=media.period if isinstance(media, raw.types.InputMediaGeoLive) else None,
|
|
190
|
+
proximity_notification_radius=media.proximity_notification_radius
|
|
191
|
+
if isinstance(media, raw.types.InputMediaGeoLive)
|
|
192
|
+
else None,
|
|
193
|
+
reply_markup=query.reply_markup,
|
|
194
|
+
)
|
|
195
|
+
elif isinstance(media, raw.types.InputMediaVenue):
|
|
196
|
+
result_type = "venue"
|
|
197
|
+
inline_message = raw.types.InputBotInlineMessageMediaVenue(
|
|
198
|
+
geo_point=media.geo_point,
|
|
199
|
+
title=media.title,
|
|
200
|
+
address=media.address,
|
|
201
|
+
provider=media.provider,
|
|
202
|
+
venue_id=media.venue_id,
|
|
203
|
+
venue_type=media.venue_type,
|
|
204
|
+
reply_markup=query.reply_markup,
|
|
205
|
+
)
|
|
206
|
+
elif isinstance(media, raw.types.InputMediaContact):
|
|
207
|
+
result_type = "contact"
|
|
208
|
+
inline_message = raw.types.InputBotInlineMessageMediaContact(
|
|
209
|
+
phone_number=media.phone_number,
|
|
210
|
+
first_name=media.first_name,
|
|
211
|
+
last_name=media.last_name,
|
|
212
|
+
vcard=media.vcard,
|
|
213
|
+
reply_markup=query.reply_markup,
|
|
214
|
+
)
|
|
215
|
+
elif isinstance(media, raw.types.InputMediaWebPage):
|
|
216
|
+
inline_message = raw.types.InputBotInlineMessageMediaWebPage(
|
|
217
|
+
url=media.url,
|
|
218
|
+
message=query.message,
|
|
219
|
+
entities=entities or None,
|
|
220
|
+
force_large_media=media.force_large_media,
|
|
221
|
+
force_small_media=media.force_small_media,
|
|
222
|
+
optional=media.optional,
|
|
223
|
+
invert_media=query.invert_media,
|
|
224
|
+
reply_markup=query.reply_markup,
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
if inline_message is not None:
|
|
228
|
+
return raw.types.InputBotInlineResult(
|
|
229
|
+
id="message", type=result_type, title="Message", send_message=inline_message
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
if not isinstance(
|
|
233
|
+
media,
|
|
234
|
+
(
|
|
235
|
+
raw.types.InputMediaPhoto,
|
|
236
|
+
raw.types.InputMediaUploadedPhoto,
|
|
237
|
+
raw.types.InputMediaPhotoExternal,
|
|
238
|
+
raw.types.InputMediaDocument,
|
|
239
|
+
raw.types.InputMediaUploadedDocument,
|
|
240
|
+
raw.types.InputMediaDocumentExternal,
|
|
241
|
+
),
|
|
242
|
+
):
|
|
243
|
+
raise ValueError(f"The helper bot inline API does not support {type(media).__name__}")
|
|
244
|
+
|
|
245
|
+
for name in ("ttl_seconds", "spoiler", "video_cover", "video_timestamp"):
|
|
246
|
+
if getattr(media, name, None) not in (None, False):
|
|
247
|
+
raise ValueError(f"The helper bot inline API does not support media option {name}")
|
|
248
|
+
if isinstance(media, raw.types.InputMediaUploadedDocument):
|
|
249
|
+
for attribute in media.attributes:
|
|
250
|
+
if isinstance(attribute, raw.types.DocumentAttributeVideo) and attribute.round_message:
|
|
251
|
+
raise ValueError("The helper bot inline API does not support video notes")
|
|
252
|
+
|
|
253
|
+
if not client.helper_bot_chat_id:
|
|
254
|
+
raise ValueError("Sending media through helper_bot requires helper_bot_chat_id")
|
|
255
|
+
peer = await client.resolve_peer(client.helper_bot_chat_id)
|
|
256
|
+
if not isinstance(peer, raw.types.InputPeerChannel):
|
|
257
|
+
raise ValueError(
|
|
258
|
+
"helper_bot_chat_id must be a channel or supergroup accessible to both clients"
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
# The bot must see the uploaded media to obtain a file reference for its own account.
|
|
262
|
+
staged = cast(
|
|
263
|
+
Union[raw.types.Updates, raw.types.UpdatesCombined],
|
|
264
|
+
await client.invoke(
|
|
265
|
+
raw.functions.messages.SendMedia(
|
|
266
|
+
peer=peer, media=media, message="", random_id=client.rnd_id(), silent=True
|
|
267
|
+
),
|
|
268
|
+
**invoke_options,
|
|
269
|
+
),
|
|
270
|
+
)
|
|
271
|
+
message_id: Optional[int] = None
|
|
272
|
+
for update in staged.updates:
|
|
273
|
+
if isinstance(update, raw.types.UpdateNewChannelMessage):
|
|
274
|
+
message_id = update.message.id
|
|
275
|
+
break
|
|
276
|
+
|
|
277
|
+
if message_id is None:
|
|
278
|
+
log.warning("Helper bot media upload returned no message update; destination send skipped")
|
|
279
|
+
return None
|
|
280
|
+
|
|
281
|
+
message = cast(types.Message, await helper.get_messages(client.helper_bot_chat_id, message_id))
|
|
282
|
+
if message.empty or message.media is None:
|
|
283
|
+
log.warning("Helper bot staging message is unavailable; destination send skipped")
|
|
284
|
+
return None
|
|
285
|
+
|
|
286
|
+
media_types = {
|
|
287
|
+
enums.MessageMediaType.ANIMATION: "gif",
|
|
288
|
+
enums.MessageMediaType.AUDIO: "audio",
|
|
289
|
+
enums.MessageMediaType.DOCUMENT: "file",
|
|
290
|
+
enums.MessageMediaType.VIDEO: "video",
|
|
291
|
+
enums.MessageMediaType.VOICE: "voice",
|
|
292
|
+
enums.MessageMediaType.STICKER: "sticker",
|
|
293
|
+
}
|
|
294
|
+
send_message = raw.types.InputBotInlineMessageMediaAuto(
|
|
295
|
+
message=query.message,
|
|
296
|
+
entities=entities or None,
|
|
297
|
+
reply_markup=query.reply_markup,
|
|
298
|
+
invert_media=query.invert_media,
|
|
299
|
+
)
|
|
300
|
+
if message.photo is not None:
|
|
301
|
+
photo = cast(
|
|
302
|
+
raw.types.InputMediaPhoto, utils.get_input_media_from_file_id(message.photo.file_id)
|
|
303
|
+
)
|
|
304
|
+
return raw.types.InputBotInlineResultPhoto(
|
|
305
|
+
id="message", type="photo", photo=photo.id, send_message=send_message
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
file = (
|
|
309
|
+
message.animation
|
|
310
|
+
or message.audio
|
|
311
|
+
or message.document
|
|
312
|
+
or message.video
|
|
313
|
+
or message.voice
|
|
314
|
+
or message.sticker
|
|
315
|
+
)
|
|
316
|
+
if file is None or message.media not in media_types:
|
|
317
|
+
raise ValueError(f"The helper bot inline API does not support {message.media}")
|
|
318
|
+
document = cast(raw.types.InputMediaDocument, utils.get_input_media_from_file_id(file.file_id))
|
|
319
|
+
return raw.types.InputBotInlineResultDocument(
|
|
320
|
+
id="message",
|
|
321
|
+
type=media_types[message.media],
|
|
322
|
+
title="Media",
|
|
323
|
+
document=document.id,
|
|
324
|
+
send_message=send_message,
|
|
325
|
+
)
|
|
@@ -17,12 +17,14 @@
|
|
|
17
17
|
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
|
|
18
18
|
|
|
19
19
|
import logging
|
|
20
|
+
from typing import Any, Optional
|
|
20
21
|
|
|
21
22
|
import pyrogram
|
|
22
23
|
from pyrogram import raw
|
|
23
24
|
from pyrogram.raw.core import TLObject
|
|
24
25
|
from pyrogram.session import Session
|
|
25
26
|
from pyrogram.methods.messages.business_session import get_session
|
|
27
|
+
from pyrogram.helper_bot import send_with_helper_bot
|
|
26
28
|
|
|
27
29
|
log = logging.getLogger(__name__)
|
|
28
30
|
|
|
@@ -33,9 +35,9 @@ class Invoke:
|
|
|
33
35
|
query: TLObject,
|
|
34
36
|
retries: int = Session.MAX_RETRIES,
|
|
35
37
|
timeout: float = Session.WAIT_TIMEOUT,
|
|
36
|
-
sleep_threshold: float = None,
|
|
37
|
-
business_connection_id: str = None
|
|
38
|
-
):
|
|
38
|
+
sleep_threshold: Optional[float] = None,
|
|
39
|
+
business_connection_id: Optional[str] = None
|
|
40
|
+
) -> Any:
|
|
39
41
|
"""Invoke raw Telegram functions.
|
|
40
42
|
|
|
41
43
|
This method makes it possible to manually call every single Telegram API method in a low-level manner.
|
|
@@ -75,6 +77,15 @@ class Invoke:
|
|
|
75
77
|
if not self.is_connected:
|
|
76
78
|
raise ConnectionError("Client has not been started yet")
|
|
77
79
|
|
|
80
|
+
if (self.helper_bot is not None and self.me is not None and not self.me.is_bot
|
|
81
|
+
and isinstance(query, (raw.functions.messages.SendMessage, raw.functions.messages.SendMedia))
|
|
82
|
+
and isinstance(query.reply_markup, raw.types.ReplyInlineMarkup)):
|
|
83
|
+
if business_connection_id:
|
|
84
|
+
raise ValueError("The helper bot inline API does not support business connections")
|
|
85
|
+
return await send_with_helper_bot(
|
|
86
|
+
self, query, retries=retries, timeout=timeout, sleep_threshold=sleep_threshold
|
|
87
|
+
)
|
|
88
|
+
|
|
78
89
|
session = self.session
|
|
79
90
|
|
|
80
91
|
if business_connection_id:
|
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
from typing import Union
|
|
20
20
|
|
|
21
21
|
import pyrogram
|
|
22
|
+
from typing import Optional
|
|
23
|
+
from pyrogram.reply_markup import ReplyMarkup
|
|
22
24
|
from pyrogram import raw
|
|
23
25
|
from pyrogram import types
|
|
24
26
|
from pyrogram import utils
|
|
@@ -36,12 +38,7 @@ class SendGame:
|
|
|
36
38
|
reply_to_chat_id: Union[int, str] = None,
|
|
37
39
|
protect_content: bool = None,
|
|
38
40
|
allow_paid_broadcast: bool = None,
|
|
39
|
-
reply_markup:
|
|
40
|
-
"types.InlineKeyboardMarkup",
|
|
41
|
-
"types.ReplyKeyboardMarkup",
|
|
42
|
-
"types.ReplyKeyboardRemove",
|
|
43
|
-
"types.ForceReply"
|
|
44
|
-
] = None
|
|
41
|
+
reply_markup: "Optional[ReplyMarkup]" = None
|
|
45
42
|
) -> "types.Message":
|
|
46
43
|
"""Send a game.
|
|
47
44
|
|
|
@@ -114,7 +111,7 @@ class SendGame:
|
|
|
114
111
|
random_id=self.rnd_id(),
|
|
115
112
|
noforwards=protect_content,
|
|
116
113
|
allow_paid_floodskip=allow_paid_broadcast,
|
|
117
|
-
reply_markup=await
|
|
114
|
+
reply_markup=await utils.write_reply_markup(self, reply_markup, for_send=True),
|
|
118
115
|
effect=effect_id
|
|
119
116
|
)
|
|
120
117
|
)
|
|
@@ -20,6 +20,7 @@ import logging
|
|
|
20
20
|
from typing import List, Optional, Union
|
|
21
21
|
|
|
22
22
|
import pyrogram
|
|
23
|
+
from pyrogram.reply_markup import ReplyMarkup
|
|
23
24
|
from pyrogram import enums, raw, utils, types
|
|
24
25
|
|
|
25
26
|
log = logging.getLogger(__name__)
|
|
@@ -56,12 +57,7 @@ class SendInvoice:
|
|
|
56
57
|
message_effect_id: Optional[int] = None,
|
|
57
58
|
reply_to_message_id: Optional[int] = None,
|
|
58
59
|
allow_paid_broadcast: bool = None,
|
|
59
|
-
reply_markup: Optional[
|
|
60
|
-
"types.InlineKeyboardMarkup",
|
|
61
|
-
"types.ReplyKeyboardMarkup",
|
|
62
|
-
"types.ReplyKeyboardRemove",
|
|
63
|
-
"types.ForceReply"
|
|
64
|
-
]] = None,
|
|
60
|
+
reply_markup: "Optional[ReplyMarkup]" = None,
|
|
65
61
|
caption: str = "",
|
|
66
62
|
parse_mode: Optional["enums.ParseMode"] = None,
|
|
67
63
|
caption_entities: Optional[List["types.MessageEntity"]] = None
|
|
@@ -224,7 +220,7 @@ class SendInvoice:
|
|
|
224
220
|
random_id=self.rnd_id(),
|
|
225
221
|
noforwards=protect_content,
|
|
226
222
|
allow_paid_floodskip=allow_paid_broadcast,
|
|
227
|
-
reply_markup=await
|
|
223
|
+
reply_markup=await utils.write_reply_markup(self, reply_markup, for_send=True),
|
|
228
224
|
effect=message_effect_id,
|
|
229
225
|
**await utils.parse_text_entities(self, caption, parse_mode, caption_entities)
|
|
230
226
|
)
|