WPyrogram 2.0.149__py3-none-any.whl → 2.0.150__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.
- {WPyrogram-2.0.149.dist-info → WPyrogram-2.0.150.dist-info}/METADATA +1 -1
- {WPyrogram-2.0.149.dist-info → WPyrogram-2.0.150.dist-info}/RECORD +12 -12
- pyrogram/__init__.py +1 -1
- pyrogram/client.py +114 -17
- pyrogram/dispatcher.py +62 -99
- pyrogram/filters.py +31 -10
- pyrogram/utils.py +177 -109
- {WPyrogram-2.0.149.dist-info → WPyrogram-2.0.150.dist-info}/COPYING +0 -0
- {WPyrogram-2.0.149.dist-info → WPyrogram-2.0.150.dist-info}/COPYING.lesser +0 -0
- {WPyrogram-2.0.149.dist-info → WPyrogram-2.0.150.dist-info}/NOTICE +0 -0
- {WPyrogram-2.0.149.dist-info → WPyrogram-2.0.150.dist-info}/WHEEL +0 -0
- {WPyrogram-2.0.149.dist-info → WPyrogram-2.0.150.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: WPyrogram
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.150
|
|
4
4
|
Summary: Elegant, modern and asynchronous Telegram MTProto API framework in Python for users and bots - Woto's experimental fork
|
|
5
5
|
Home-page: https://github.com/ALiwoto/WPyrogram
|
|
6
6
|
Download-URL: https://github.com/ALiwoto/WPyrogram
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
pyrogram/__init__.py,sha256=
|
|
2
|
-
pyrogram/client.py,sha256=
|
|
3
|
-
pyrogram/dispatcher.py,sha256=
|
|
1
|
+
pyrogram/__init__.py,sha256=u9UQDjfd1cxA6-EUvI2Db2oi4v9yNdlVJUxGJuFcWG4,1401
|
|
2
|
+
pyrogram/client.py,sha256=Tgtlw85sYEjgzI8cVPL7FwhiCvUNow7FsQcbn5Gx4nY,50088
|
|
3
|
+
pyrogram/dispatcher.py,sha256=cJV2tUDAfgNAh0aSjRHqiWNUBQ7ipGzBNTyJHunxLxc,13947
|
|
4
4
|
pyrogram/emoji.py,sha256=ixb3OXR1XpLf3BQpw8kWIpmbQRowRIaFedjdnuzljac,208021
|
|
5
5
|
pyrogram/file_id.py,sha256=jmMBAt2O_XhLE_AXKffZ4F5gccXJB8GHV7MlFCpiklA,15154
|
|
6
|
-
pyrogram/filters.py,sha256=
|
|
6
|
+
pyrogram/filters.py,sha256=5d7GL7W2OLwg9E8RUHLudQnbhdK7OrtFs2mSFTmoR1s,28802
|
|
7
7
|
pyrogram/mime_types.py,sha256=JbYF6B-JrxZQNga8dPz2C8Dwp8-2pOrE1uv3KIBbjA0,61915
|
|
8
8
|
pyrogram/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
9
|
pyrogram/sync.py,sha256=cxKmLz9NAJPX2N2n2CogTLXyNo-dWgf6etBRXnqq1XA,3739
|
|
10
|
-
pyrogram/utils.py,sha256=
|
|
10
|
+
pyrogram/utils.py,sha256=HHlPe3CxShGPUidDdvCSXVgye5OeqTvmj-QUu5zyNh0,20125
|
|
11
11
|
pyrogram/connection/__init__.py,sha256=q7KiWEV6VNDQGoj5s13GPyhRRlafXVPvX1h1VpAsVf8,854
|
|
12
12
|
pyrogram/connection/connection.py,sha256=X5T7jd_lAXsH86f9gaNd4GDg4XIVnll7wrlVvSfgBJ0,2765
|
|
13
13
|
pyrogram/connection/transport/__init__.py,sha256=nRpFdumPinBtuLOZkR8hH_vXlN7ChzW1oyJ6na4a8qw,838
|
|
@@ -3316,10 +3316,10 @@ pyrogram/types/user_and_chats/video_chat_ended.py,sha256=J5xTIAMSGDz8_z-EGrKwkoJ
|
|
|
3316
3316
|
pyrogram/types/user_and_chats/video_chat_members_invited.py,sha256=9QhonApJd1XjOeJu9fqx2_u6nIOTZhfXZw_tX9XXxd8,1610
|
|
3317
3317
|
pyrogram/types/user_and_chats/video_chat_scheduled.py,sha256=rUvDXF8r-2MfsAZKAcaWb_UZt5VoHebPjeJXWRDHGSo,1531
|
|
3318
3318
|
pyrogram/types/user_and_chats/video_chat_started.py,sha256=WOAuM_CnkRA-W_Iedx0fIHB9RJ-AsvJIiSF05HzLRdA,1043
|
|
3319
|
-
WPyrogram-2.0.
|
|
3320
|
-
WPyrogram-2.0.
|
|
3321
|
-
WPyrogram-2.0.
|
|
3322
|
-
WPyrogram-2.0.
|
|
3323
|
-
WPyrogram-2.0.
|
|
3324
|
-
WPyrogram-2.0.
|
|
3325
|
-
WPyrogram-2.0.
|
|
3319
|
+
WPyrogram-2.0.150.dist-info/COPYING,sha256=ixuiBLtpoK3iv89l7ylKkg9rs2GzF9ukPH7ynZYzK5s,35148
|
|
3320
|
+
WPyrogram-2.0.150.dist-info/COPYING.lesser,sha256=pWgb-bBdsU2Gd2kwAXxketnm5W_2u8_fIeWEgojfrxs,7651
|
|
3321
|
+
WPyrogram-2.0.150.dist-info/METADATA,sha256=AnGagkc8BFEJyL0oohTeuZo7eCyZeofShG0E-m1cEHE,4922
|
|
3322
|
+
WPyrogram-2.0.150.dist-info/NOTICE,sha256=QL3iJdeVh_ZqRZZchyruQic5bYUWdx3yl0OkSUC2PQc,774
|
|
3323
|
+
WPyrogram-2.0.150.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
|
3324
|
+
WPyrogram-2.0.150.dist-info/top_level.txt,sha256=vS2-tKX7dJ1uKjistE5CoOVbv4yqglA95SK0KFEDwl0,9
|
|
3325
|
+
WPyrogram-2.0.150.dist-info/RECORD,,
|
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.150"
|
|
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
|
|
35
|
+
from typing import Union, List, Optional, Callable, AsyncGenerator, Type, Tuple
|
|
36
36
|
|
|
37
37
|
import pyrogram
|
|
38
38
|
from pyrogram import __version__, __license__
|
|
@@ -45,7 +45,8 @@ from pyrogram.errors import (
|
|
|
45
45
|
SessionPasswordNeeded,
|
|
46
46
|
VolumeLocNotFound, ChannelPrivate,
|
|
47
47
|
BadRequest, AuthBytesInvalid,
|
|
48
|
-
FloodWait, FloodPremiumWait
|
|
48
|
+
FloodWait, FloodPremiumWait,
|
|
49
|
+
ChannelInvalid, PersistentTimestampInvalid, PersistentTimestampOutdated
|
|
49
50
|
)
|
|
50
51
|
from pyrogram.handlers.handler import Handler
|
|
51
52
|
from pyrogram.methods import Methods
|
|
@@ -53,6 +54,8 @@ from pyrogram.session import Auth, Session
|
|
|
53
54
|
from pyrogram.storage import Storage, FileStorage, MemoryStorage
|
|
54
55
|
from pyrogram.types import User, TermsOfService
|
|
55
56
|
from pyrogram.utils import ainput
|
|
57
|
+
from .connection import Connection
|
|
58
|
+
from .connection.transport import TCP, TCPAbridged
|
|
56
59
|
from .dispatcher import Dispatcher
|
|
57
60
|
from .file_id import FileId, FileType, ThumbnailSource
|
|
58
61
|
from .mime_types import mime_types
|
|
@@ -264,7 +267,9 @@ class Client(Methods):
|
|
|
264
267
|
max_message_cache_size: int = MAX_MESSAGE_CACHE_SIZE,
|
|
265
268
|
storage_engine: Optional[Storage] = None,
|
|
266
269
|
client_platform: "enums.ClientPlatform" = enums.ClientPlatform.OTHER,
|
|
267
|
-
init_connection_params: Optional["raw.base.JSONValue"] = None
|
|
270
|
+
init_connection_params: Optional["raw.base.JSONValue"] = None,
|
|
271
|
+
connection_factory: Type[Connection] = Connection,
|
|
272
|
+
protocol_factory: Type[TCP] = TCPAbridged
|
|
268
273
|
):
|
|
269
274
|
super().__init__()
|
|
270
275
|
|
|
@@ -299,6 +304,8 @@ class Client(Methods):
|
|
|
299
304
|
self.max_message_cache_size = max_message_cache_size
|
|
300
305
|
self.client_platform = client_platform
|
|
301
306
|
self.init_connection_params = init_connection_params
|
|
307
|
+
self.connection_factory = connection_factory
|
|
308
|
+
self.protocol_factory = protocol_factory
|
|
302
309
|
|
|
303
310
|
self.executor = ThreadPoolExecutor(self.workers, thread_name_prefix="Handler")
|
|
304
311
|
|
|
@@ -541,25 +548,26 @@ class Client(Methods):
|
|
|
541
548
|
async def fetch_peers(self, peers: List[Union[raw.types.User, raw.types.Chat, raw.types.Channel]]) -> bool:
|
|
542
549
|
is_min = False
|
|
543
550
|
parsed_peers = []
|
|
551
|
+
parsed_usernames = []
|
|
544
552
|
|
|
545
553
|
for peer in peers:
|
|
546
554
|
if getattr(peer, "min", False):
|
|
547
555
|
is_min = True
|
|
548
556
|
continue
|
|
549
557
|
|
|
550
|
-
usernames =
|
|
558
|
+
usernames = []
|
|
551
559
|
phone_number = None
|
|
552
560
|
|
|
553
561
|
if isinstance(peer, raw.types.User):
|
|
554
562
|
peer_id = peer.id
|
|
555
563
|
access_hash = peer.access_hash
|
|
556
|
-
usernames = (
|
|
557
|
-
[peer.username.lower()] if peer.username
|
|
558
|
-
else [username.username.lower() for username in peer.usernames] if peer.usernames
|
|
559
|
-
else None
|
|
560
|
-
)
|
|
561
564
|
phone_number = peer.phone
|
|
562
565
|
peer_type = "bot" if peer.bot else "user"
|
|
566
|
+
|
|
567
|
+
if peer.username:
|
|
568
|
+
usernames.append(peer.username.lower())
|
|
569
|
+
elif peer.usernames:
|
|
570
|
+
usernames.extend(username.username.lower() for username in peer.usernames)
|
|
563
571
|
elif isinstance(peer, (raw.types.Chat, raw.types.ChatForbidden)):
|
|
564
572
|
peer_id = -peer.id
|
|
565
573
|
access_hash = 0
|
|
@@ -567,12 +575,12 @@ class Client(Methods):
|
|
|
567
575
|
elif isinstance(peer, raw.types.Channel):
|
|
568
576
|
peer_id = utils.get_channel_id(peer.id)
|
|
569
577
|
access_hash = peer.access_hash
|
|
570
|
-
usernames = (
|
|
571
|
-
[peer.username.lower()] if peer.username
|
|
572
|
-
else [username.username.lower() for username in peer.usernames] if peer.usernames
|
|
573
|
-
else None
|
|
574
|
-
)
|
|
575
578
|
peer_type = "channel" if peer.broadcast else "supergroup"
|
|
579
|
+
|
|
580
|
+
if peer.username:
|
|
581
|
+
usernames.append(peer.username.lower())
|
|
582
|
+
elif peer.usernames:
|
|
583
|
+
usernames.extend(username.username.lower() for username in peer.usernames)
|
|
576
584
|
elif isinstance(peer, raw.types.ChannelForbidden):
|
|
577
585
|
peer_id = utils.get_channel_id(peer.id)
|
|
578
586
|
access_hash = peer.access_hash
|
|
@@ -580,9 +588,11 @@ class Client(Methods):
|
|
|
580
588
|
else:
|
|
581
589
|
continue
|
|
582
590
|
|
|
583
|
-
parsed_peers.append((peer_id, access_hash, peer_type,
|
|
591
|
+
parsed_peers.append((peer_id, access_hash, peer_type, phone_number))
|
|
592
|
+
parsed_usernames.append((peer_id, usernames))
|
|
584
593
|
|
|
585
594
|
await self.storage.update_peers(parsed_peers)
|
|
595
|
+
await self.storage.update_usernames(parsed_usernames)
|
|
586
596
|
|
|
587
597
|
return is_min
|
|
588
598
|
|
|
@@ -639,10 +649,11 @@ class Client(Methods):
|
|
|
639
649
|
)]
|
|
640
650
|
),
|
|
641
651
|
pts=pts - pts_count,
|
|
642
|
-
limit=pts
|
|
652
|
+
limit=pts,
|
|
653
|
+
force=False
|
|
643
654
|
)
|
|
644
655
|
)
|
|
645
|
-
except ChannelPrivate:
|
|
656
|
+
except (ChannelPrivate, PersistentTimestampOutdated, PersistentTimestampInvalid):
|
|
646
657
|
pass
|
|
647
658
|
else:
|
|
648
659
|
if not isinstance(diff, raw.types.updates.ChannelDifferenceEmpty):
|
|
@@ -688,6 +699,92 @@ class Client(Methods):
|
|
|
688
699
|
elif isinstance(updates, raw.types.UpdatesTooLong):
|
|
689
700
|
log.info(updates)
|
|
690
701
|
|
|
702
|
+
async def recover_gaps(self) -> Tuple[int, int]:
|
|
703
|
+
states = await self.storage.update_state()
|
|
704
|
+
|
|
705
|
+
message_updates_counter = 0
|
|
706
|
+
other_updates_counter = 0
|
|
707
|
+
|
|
708
|
+
if not states:
|
|
709
|
+
log.info("No states found, skipping recovery.")
|
|
710
|
+
return (message_updates_counter, other_updates_counter)
|
|
711
|
+
|
|
712
|
+
for state in states:
|
|
713
|
+
id, local_pts, _, local_date, _ = state
|
|
714
|
+
|
|
715
|
+
prev_pts = 0
|
|
716
|
+
|
|
717
|
+
while True:
|
|
718
|
+
try:
|
|
719
|
+
diff = await self.invoke(
|
|
720
|
+
raw.functions.updates.GetChannelDifference(
|
|
721
|
+
channel=await self.resolve_peer(id),
|
|
722
|
+
filter=raw.types.ChannelMessagesFilterEmpty(),
|
|
723
|
+
pts=local_pts,
|
|
724
|
+
limit=10000,
|
|
725
|
+
force=False
|
|
726
|
+
) if id < 0 else
|
|
727
|
+
raw.functions.updates.GetDifference(
|
|
728
|
+
pts=local_pts,
|
|
729
|
+
date=local_date,
|
|
730
|
+
qts=0
|
|
731
|
+
)
|
|
732
|
+
)
|
|
733
|
+
except (ChannelPrivate, ChannelInvalid, PersistentTimestampOutdated, PersistentTimestampInvalid):
|
|
734
|
+
break
|
|
735
|
+
|
|
736
|
+
if isinstance(diff, raw.types.updates.DifferenceEmpty):
|
|
737
|
+
break
|
|
738
|
+
elif isinstance(diff, raw.types.updates.DifferenceTooLong):
|
|
739
|
+
break
|
|
740
|
+
elif isinstance(diff, raw.types.updates.Difference):
|
|
741
|
+
local_pts = diff.state.pts
|
|
742
|
+
elif isinstance(diff, raw.types.updates.DifferenceSlice):
|
|
743
|
+
local_pts = diff.intermediate_state.pts
|
|
744
|
+
local_date = diff.intermediate_state.date
|
|
745
|
+
|
|
746
|
+
if prev_pts == local_pts:
|
|
747
|
+
break
|
|
748
|
+
|
|
749
|
+
prev_pts = local_pts
|
|
750
|
+
elif isinstance(diff, raw.types.updates.ChannelDifferenceEmpty):
|
|
751
|
+
break
|
|
752
|
+
elif isinstance(diff, raw.types.updates.ChannelDifferenceTooLong):
|
|
753
|
+
break
|
|
754
|
+
elif isinstance(diff, raw.types.updates.ChannelDifference):
|
|
755
|
+
local_pts = diff.pts
|
|
756
|
+
|
|
757
|
+
users = {i.id: i for i in diff.users}
|
|
758
|
+
chats = {i.id: i for i in diff.chats}
|
|
759
|
+
|
|
760
|
+
for message in diff.new_messages:
|
|
761
|
+
message_updates_counter += 1
|
|
762
|
+
self.dispatcher.updates_queue.put_nowait(
|
|
763
|
+
(
|
|
764
|
+
raw.types.UpdateNewMessage(
|
|
765
|
+
message=message,
|
|
766
|
+
pts=local_pts,
|
|
767
|
+
pts_count=-1
|
|
768
|
+
),
|
|
769
|
+
users,
|
|
770
|
+
chats
|
|
771
|
+
)
|
|
772
|
+
)
|
|
773
|
+
|
|
774
|
+
for update in diff.other_updates:
|
|
775
|
+
other_updates_counter += 1
|
|
776
|
+
self.dispatcher.updates_queue.put_nowait(
|
|
777
|
+
(update, users, chats)
|
|
778
|
+
)
|
|
779
|
+
|
|
780
|
+
if isinstance(diff, (raw.types.updates.Difference, raw.types.updates.ChannelDifference)):
|
|
781
|
+
break
|
|
782
|
+
|
|
783
|
+
await self.storage.update_state(id)
|
|
784
|
+
|
|
785
|
+
log.info("Recovered %s messages and %s updates.", message_updates_counter, other_updates_counter)
|
|
786
|
+
return (message_updates_counter, other_updates_counter)
|
|
787
|
+
|
|
691
788
|
async def load_session(self):
|
|
692
789
|
await self.storage.open()
|
|
693
790
|
|
pyrogram/dispatcher.py
CHANGED
|
@@ -22,13 +22,12 @@ import logging
|
|
|
22
22
|
from collections import OrderedDict
|
|
23
23
|
|
|
24
24
|
import pyrogram
|
|
25
|
-
from pyrogram import errors
|
|
26
25
|
from pyrogram import utils
|
|
27
|
-
from pyrogram import raw
|
|
28
26
|
from pyrogram.handlers import (
|
|
29
27
|
CallbackQueryHandler, MessageHandler, EditedMessageHandler, DeletedMessagesHandler,
|
|
30
28
|
UserStatusHandler, RawUpdateHandler, InlineQueryHandler, PollHandler, PreCheckoutQueryHandler,
|
|
31
|
-
ChosenInlineResultHandler, ChatMemberUpdatedHandler, ChatJoinRequestHandler, StoryHandler
|
|
29
|
+
ChosenInlineResultHandler, ChatMemberUpdatedHandler, ChatJoinRequestHandler, StoryHandler,
|
|
30
|
+
ShippingQueryHandler, MessageReactionHandler, MessageReactionCountHandler, ChatBoostHandler
|
|
32
31
|
)
|
|
33
32
|
from pyrogram.raw.types import (
|
|
34
33
|
UpdateNewMessage, UpdateNewChannelMessage, UpdateNewScheduledMessage,
|
|
@@ -38,7 +37,9 @@ from pyrogram.raw.types import (
|
|
|
38
37
|
UpdateBotCallbackQuery, UpdateInlineBotCallbackQuery, UpdateBotPrecheckoutQuery,
|
|
39
38
|
UpdateUserStatus, UpdateBotInlineQuery, UpdateMessagePoll,
|
|
40
39
|
UpdateBotInlineSend, UpdateChatParticipant, UpdateChannelParticipant,
|
|
41
|
-
UpdateBotChatInviteRequester, UpdateStory
|
|
40
|
+
UpdateBotChatInviteRequester, UpdateStory, UpdateBotShippingQuery, UpdateBotMessageReaction,
|
|
41
|
+
UpdateBotMessageReactions, UpdateBotChatBoost, UpdateBusinessBotCallbackQuery,
|
|
42
|
+
UpdateBotPurchasedPaidMedia, UpdateMessagePollVote
|
|
42
43
|
)
|
|
43
44
|
|
|
44
45
|
log = logging.getLogger(__name__)
|
|
@@ -48,15 +49,20 @@ class Dispatcher:
|
|
|
48
49
|
NEW_MESSAGE_UPDATES = (UpdateNewMessage, UpdateNewChannelMessage, UpdateNewScheduledMessage, UpdateBotNewBusinessMessage)
|
|
49
50
|
EDIT_MESSAGE_UPDATES = (UpdateEditMessage, UpdateEditChannelMessage, UpdateBotEditBusinessMessage)
|
|
50
51
|
DELETE_MESSAGES_UPDATES = (UpdateDeleteMessages, UpdateDeleteChannelMessages, UpdateBotDeleteBusinessMessage)
|
|
51
|
-
CALLBACK_QUERY_UPDATES = (UpdateBotCallbackQuery, UpdateInlineBotCallbackQuery)
|
|
52
|
+
CALLBACK_QUERY_UPDATES = (UpdateBotCallbackQuery, UpdateInlineBotCallbackQuery, UpdateBusinessBotCallbackQuery)
|
|
52
53
|
CHAT_MEMBER_UPDATES = (UpdateChatParticipant, UpdateChannelParticipant)
|
|
53
54
|
USER_STATUS_UPDATES = (UpdateUserStatus,)
|
|
54
55
|
BOT_INLINE_QUERY_UPDATES = (UpdateBotInlineQuery,)
|
|
55
|
-
POLL_UPDATES = (UpdateMessagePoll,)
|
|
56
|
+
POLL_UPDATES = (UpdateMessagePoll, UpdateMessagePollVote)
|
|
56
57
|
CHOSEN_INLINE_RESULT_UPDATES = (UpdateBotInlineSend,)
|
|
57
58
|
CHAT_JOIN_REQUEST_UPDATES = (UpdateBotChatInviteRequester,)
|
|
58
59
|
NEW_STORY_UPDATES = (UpdateStory,)
|
|
59
60
|
PRE_CHECKOUT_QUERY_UPDATES = (UpdateBotPrecheckoutQuery,)
|
|
61
|
+
SHIPPING_QUERY_UPDATES = (UpdateBotShippingQuery,)
|
|
62
|
+
MESSAGE_REACTION_UPDATES = (UpdateBotMessageReaction,)
|
|
63
|
+
MESSAGE_REACTION_COUNT_UPDATES = (UpdateBotMessageReactions,)
|
|
64
|
+
CHAT_BOOST_UPDATES = (UpdateBotChatBoost,)
|
|
65
|
+
PURCHASED_PAID_MEDIA_UPDATES = (UpdateBotPurchasedPaidMedia,)
|
|
60
66
|
|
|
61
67
|
def __init__(self, client: "pyrogram.Client"):
|
|
62
68
|
self.client = client
|
|
@@ -69,6 +75,8 @@ class Dispatcher:
|
|
|
69
75
|
self.groups = OrderedDict()
|
|
70
76
|
|
|
71
77
|
async def message_parser(update, users, chats):
|
|
78
|
+
connection_id = getattr(update, "connection_id", None)
|
|
79
|
+
|
|
72
80
|
return (
|
|
73
81
|
await pyrogram.types.Message._parse(
|
|
74
82
|
self.client,
|
|
@@ -76,8 +84,9 @@ class Dispatcher:
|
|
|
76
84
|
users,
|
|
77
85
|
chats,
|
|
78
86
|
is_scheduled=isinstance(update, UpdateNewScheduledMessage),
|
|
79
|
-
|
|
80
|
-
|
|
87
|
+
replies=0 if getattr(update, "connection_id", None) else 1,
|
|
88
|
+
business_connection_id=connection_id,
|
|
89
|
+
raw_reply_to_message=getattr(update, "reply_to_message", None)
|
|
81
90
|
),
|
|
82
91
|
MessageHandler
|
|
83
92
|
)
|
|
@@ -99,7 +108,7 @@ class Dispatcher:
|
|
|
99
108
|
|
|
100
109
|
async def callback_query_parser(update, users, chats):
|
|
101
110
|
return (
|
|
102
|
-
await pyrogram.types.CallbackQuery._parse(self.client, update, users),
|
|
111
|
+
await pyrogram.types.CallbackQuery._parse(self.client, update, users, chats),
|
|
103
112
|
CallbackQueryHandler
|
|
104
113
|
)
|
|
105
114
|
|
|
@@ -117,7 +126,7 @@ class Dispatcher:
|
|
|
117
126
|
|
|
118
127
|
async def poll_parser(update, users, chats):
|
|
119
128
|
return (
|
|
120
|
-
pyrogram.types.Poll._parse_update(self.client, update),
|
|
129
|
+
pyrogram.types.Poll._parse_update(self.client, update, users),
|
|
121
130
|
PollHandler
|
|
122
131
|
)
|
|
123
132
|
|
|
@@ -151,6 +160,36 @@ class Dispatcher:
|
|
|
151
160
|
PreCheckoutQueryHandler
|
|
152
161
|
)
|
|
153
162
|
|
|
163
|
+
async def shipping_query_parser(update, users, chats):
|
|
164
|
+
return (
|
|
165
|
+
await pyrogram.types.ShippingQuery._parse(self.client, update, users),
|
|
166
|
+
ShippingQueryHandler
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
async def message_reaction_parser(update, users, chats):
|
|
170
|
+
return (
|
|
171
|
+
pyrogram.types.MessageReactionUpdated._parse(self.client, update, users, chats),
|
|
172
|
+
MessageReactionHandler
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
async def message_reaction_count_parser(update, users, chats):
|
|
176
|
+
return (
|
|
177
|
+
pyrogram.types.MessageReactionCountUpdated._parse(self.client, update, users, chats),
|
|
178
|
+
MessageReactionCountHandler
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
async def chat_boost_parser(update, users, chats):
|
|
182
|
+
return (
|
|
183
|
+
pyrogram.types.ChatBoostUpdated._parse(self.client, update, users, chats),
|
|
184
|
+
ChatBoostHandler
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
async def purchased_paid_media_parser(update, users, chats):
|
|
188
|
+
return (
|
|
189
|
+
pyrogram.types.PurchasedPaidMedia._parse(self.client, update, users),
|
|
190
|
+
ChatBoostHandler
|
|
191
|
+
)
|
|
192
|
+
|
|
154
193
|
self.update_parsers = {
|
|
155
194
|
Dispatcher.NEW_MESSAGE_UPDATES: message_parser,
|
|
156
195
|
Dispatcher.EDIT_MESSAGE_UPDATES: edited_message_parser,
|
|
@@ -163,7 +202,12 @@ class Dispatcher:
|
|
|
163
202
|
Dispatcher.CHAT_MEMBER_UPDATES: chat_member_updated_parser,
|
|
164
203
|
Dispatcher.CHAT_JOIN_REQUEST_UPDATES: chat_join_request_parser,
|
|
165
204
|
Dispatcher.NEW_STORY_UPDATES: story_parser,
|
|
166
|
-
Dispatcher.PRE_CHECKOUT_QUERY_UPDATES: pre_checkout_query_parser
|
|
205
|
+
Dispatcher.PRE_CHECKOUT_QUERY_UPDATES: pre_checkout_query_parser,
|
|
206
|
+
Dispatcher.SHIPPING_QUERY_UPDATES: shipping_query_parser,
|
|
207
|
+
Dispatcher.MESSAGE_REACTION_UPDATES: message_reaction_parser,
|
|
208
|
+
Dispatcher.MESSAGE_REACTION_COUNT_UPDATES: message_reaction_count_parser,
|
|
209
|
+
Dispatcher.CHAT_BOOST_UPDATES: chat_boost_parser,
|
|
210
|
+
Dispatcher.PURCHASED_PAID_MEDIA_UPDATES: purchased_paid_media_parser,
|
|
167
211
|
}
|
|
168
212
|
|
|
169
213
|
self.update_parsers = {key: value for key_tuple, value in self.update_parsers.items() for key in key_tuple}
|
|
@@ -180,93 +224,7 @@ class Dispatcher:
|
|
|
180
224
|
log.info("Started %s HandlerTasks", self.client.workers)
|
|
181
225
|
|
|
182
226
|
if not self.client.skip_updates:
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
if not states:
|
|
186
|
-
log.info("No states found, skipping recovery.")
|
|
187
|
-
return
|
|
188
|
-
|
|
189
|
-
message_updates_counter = 0
|
|
190
|
-
other_updates_counter = 0
|
|
191
|
-
|
|
192
|
-
for state in states:
|
|
193
|
-
id, local_pts, _, local_date, _ = state
|
|
194
|
-
|
|
195
|
-
prev_pts = 0
|
|
196
|
-
|
|
197
|
-
while True:
|
|
198
|
-
try:
|
|
199
|
-
diff = await self.client.invoke(
|
|
200
|
-
raw.functions.updates.GetChannelDifference(
|
|
201
|
-
channel=await self.client.resolve_peer(id),
|
|
202
|
-
filter=raw.types.ChannelMessagesFilterEmpty(),
|
|
203
|
-
pts=local_pts,
|
|
204
|
-
limit=10000
|
|
205
|
-
) if id < 0 else
|
|
206
|
-
raw.functions.updates.GetDifference(
|
|
207
|
-
pts=local_pts,
|
|
208
|
-
date=local_date,
|
|
209
|
-
qts=0
|
|
210
|
-
)
|
|
211
|
-
)
|
|
212
|
-
except (errors.ChannelPrivate, errors.ChannelInvalid):
|
|
213
|
-
break
|
|
214
|
-
|
|
215
|
-
if isinstance(diff, raw.types.updates.DifferenceEmpty):
|
|
216
|
-
break
|
|
217
|
-
elif isinstance(diff, raw.types.updates.DifferenceTooLong):
|
|
218
|
-
break
|
|
219
|
-
elif isinstance(diff, raw.types.updates.Difference):
|
|
220
|
-
local_pts = diff.state.pts
|
|
221
|
-
elif isinstance(diff, raw.types.updates.DifferenceSlice):
|
|
222
|
-
local_pts = diff.intermediate_state.pts
|
|
223
|
-
local_date = diff.intermediate_state.date
|
|
224
|
-
|
|
225
|
-
if prev_pts == local_pts:
|
|
226
|
-
break
|
|
227
|
-
|
|
228
|
-
prev_pts = local_pts
|
|
229
|
-
elif isinstance(diff, raw.types.updates.ChannelDifferenceEmpty):
|
|
230
|
-
break
|
|
231
|
-
elif isinstance(diff, raw.types.updates.ChannelDifferenceTooLong):
|
|
232
|
-
break
|
|
233
|
-
elif isinstance(diff, raw.types.updates.ChannelDifference):
|
|
234
|
-
local_pts = diff.pts
|
|
235
|
-
|
|
236
|
-
users = {i.id: i for i in getattr(diff, "users", [])}
|
|
237
|
-
chats = {i.id: i for i in getattr(diff, "chats", [])}
|
|
238
|
-
|
|
239
|
-
for message in diff.new_messages:
|
|
240
|
-
message_updates_counter += 1
|
|
241
|
-
self.updates_queue.put_nowait(
|
|
242
|
-
(
|
|
243
|
-
raw.types.UpdateNewMessage(
|
|
244
|
-
message=message,
|
|
245
|
-
pts=local_pts,
|
|
246
|
-
pts_count=-1
|
|
247
|
-
) if id == self.client.me.id else
|
|
248
|
-
raw.types.UpdateNewChannelMessage(
|
|
249
|
-
message=message,
|
|
250
|
-
pts=local_pts,
|
|
251
|
-
pts_count=-1
|
|
252
|
-
),
|
|
253
|
-
users,
|
|
254
|
-
chats
|
|
255
|
-
)
|
|
256
|
-
)
|
|
257
|
-
|
|
258
|
-
for update in diff.other_updates:
|
|
259
|
-
other_updates_counter += 1
|
|
260
|
-
self.updates_queue.put_nowait(
|
|
261
|
-
(update, users, chats)
|
|
262
|
-
)
|
|
263
|
-
|
|
264
|
-
if isinstance(diff, (raw.types.updates.Difference, raw.types.updates.ChannelDifference)):
|
|
265
|
-
break
|
|
266
|
-
|
|
267
|
-
await self.client.storage.update_state(id)
|
|
268
|
-
|
|
269
|
-
log.info("Recovered %s messages and %s updates.", message_updates_counter, other_updates_counter)
|
|
227
|
+
await self.client.recover_gaps()
|
|
270
228
|
|
|
271
229
|
async def stop(self):
|
|
272
230
|
if not self.client.no_updates:
|
|
@@ -345,7 +303,12 @@ class Dispatcher:
|
|
|
345
303
|
continue
|
|
346
304
|
|
|
347
305
|
elif isinstance(handler, RawUpdateHandler):
|
|
348
|
-
|
|
306
|
+
try:
|
|
307
|
+
if await handler.check(self.client, update):
|
|
308
|
+
args = (update, users, chats)
|
|
309
|
+
except Exception as e:
|
|
310
|
+
log.exception(e)
|
|
311
|
+
continue
|
|
349
312
|
|
|
350
313
|
if args is None:
|
|
351
314
|
continue
|
pyrogram/filters.py
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
import inspect
|
|
20
20
|
import re
|
|
21
|
-
from typing import Callable, Union, List, Pattern
|
|
21
|
+
from typing import Callable, Union, List, Pattern, Optional
|
|
22
22
|
|
|
23
23
|
import pyrogram
|
|
24
24
|
from pyrogram import enums
|
|
@@ -261,6 +261,16 @@ caption = create(caption_filter)
|
|
|
261
261
|
|
|
262
262
|
# endregion
|
|
263
263
|
|
|
264
|
+
# region self_destruction_filter
|
|
265
|
+
async def self_destruction_filter(_, __, m: Message):
|
|
266
|
+
return bool(m.media and getattr(getattr(m, m.media.value, None), "ttl_seconds", None))
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
self_destruction = create(self_destruction_filter)
|
|
270
|
+
"""Filter self-destruction media messages."""
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
# endregion
|
|
264
274
|
|
|
265
275
|
# region audio_filter
|
|
266
276
|
async def audio_filter(_, __, m: Message):
|
|
@@ -339,13 +349,13 @@ giveaway = create(giveaway_filter)
|
|
|
339
349
|
|
|
340
350
|
# endregion
|
|
341
351
|
|
|
342
|
-
# region
|
|
343
|
-
async def
|
|
344
|
-
return bool(m.
|
|
352
|
+
# region giveaway_winners_filter
|
|
353
|
+
async def giveaway_winners_filter(_, __, m: Message):
|
|
354
|
+
return bool(m.giveaway_winners)
|
|
345
355
|
|
|
346
356
|
|
|
347
|
-
|
|
348
|
-
"""Filter messages that contain :obj:`~pyrogram.types.
|
|
357
|
+
giveaway_winners = create(giveaway_winners_filter)
|
|
358
|
+
"""Filter messages that contain :obj:`~pyrogram.types.GiveawayWinners` objects."""
|
|
349
359
|
|
|
350
360
|
|
|
351
361
|
# endregion
|
|
@@ -359,6 +369,17 @@ gift_code = create(gift_code_filter)
|
|
|
359
369
|
"""Filter messages that contain :obj:`~pyrogram.types.GiftCode` objects."""
|
|
360
370
|
|
|
361
371
|
|
|
372
|
+
# endregion
|
|
373
|
+
|
|
374
|
+
# region star_gift_filter
|
|
375
|
+
async def star_gift_filter(_, __, m: Message):
|
|
376
|
+
return bool(m.star_gift)
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
star_gift = create(star_gift_filter)
|
|
380
|
+
"""Filter messages that contain :obj:`~pyrogram.types.StarGift` objects."""
|
|
381
|
+
|
|
382
|
+
|
|
362
383
|
# endregion
|
|
363
384
|
|
|
364
385
|
# region requested_chats_filter
|
|
@@ -971,7 +992,7 @@ def regex(pattern: Union[str, Pattern], flags: int = 0):
|
|
|
971
992
|
elif isinstance(update, InlineQuery):
|
|
972
993
|
value = update.query
|
|
973
994
|
elif isinstance(update, PreCheckoutQuery):
|
|
974
|
-
value = update.
|
|
995
|
+
value = update.invoice_payload
|
|
975
996
|
else:
|
|
976
997
|
raise ValueError(f"Regex filter doesn't work with {type(update)}")
|
|
977
998
|
|
|
@@ -1001,7 +1022,7 @@ class user(Filter, set):
|
|
|
1001
1022
|
Defaults to None (no users).
|
|
1002
1023
|
"""
|
|
1003
1024
|
|
|
1004
|
-
def __init__(self, users: Union[int, str, List[Union[int, str]]] = None):
|
|
1025
|
+
def __init__(self, users: Optional[Union[int, str, List[Union[int, str]]]] = None):
|
|
1005
1026
|
users = [] if users is None else users if isinstance(users, list) else [users]
|
|
1006
1027
|
|
|
1007
1028
|
super().__init__(
|
|
@@ -1033,7 +1054,7 @@ class chat(Filter, set):
|
|
|
1033
1054
|
Defaults to None (no chats).
|
|
1034
1055
|
"""
|
|
1035
1056
|
|
|
1036
|
-
def __init__(self, chats: Union[int, str, List[Union[int, str]]] = None):
|
|
1057
|
+
def __init__(self, chats: Optional[Union[int, str, List[Union[int, str]]]] = None):
|
|
1037
1058
|
chats = [] if chats is None else chats if isinstance(chats, list) else [chats]
|
|
1038
1059
|
|
|
1039
1060
|
super().__init__(
|
|
@@ -1066,7 +1087,7 @@ class topic(Filter, set):
|
|
|
1066
1087
|
Defaults to None (no topics).
|
|
1067
1088
|
"""
|
|
1068
1089
|
|
|
1069
|
-
def __init__(self, topics: Union[int, List[int]] = None):
|
|
1090
|
+
def __init__(self, topics: Optional[Union[int, List[int]]] = None):
|
|
1070
1091
|
topics = [] if topics is None else topics if isinstance(topics, list) else [topics]
|
|
1071
1092
|
|
|
1072
1093
|
super().__init__(
|
pyrogram/utils.py
CHANGED
|
@@ -16,21 +16,23 @@
|
|
|
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 concurrent.futures.thread import ThreadPoolExecutor
|
|
20
|
+
from datetime import datetime, timezone
|
|
21
|
+
from getpass import getpass
|
|
22
|
+
from io import BytesIO
|
|
23
|
+
from typing import Union, List, Dict, Optional
|
|
19
24
|
import asyncio
|
|
20
25
|
import base64
|
|
21
26
|
import functools
|
|
22
27
|
import hashlib
|
|
23
28
|
import os
|
|
24
|
-
import struct
|
|
25
|
-
from concurrent.futures.thread import ThreadPoolExecutor
|
|
26
|
-
from datetime import datetime, timezone
|
|
27
|
-
from getpass import getpass
|
|
28
29
|
import re
|
|
29
|
-
|
|
30
|
+
import struct
|
|
30
31
|
|
|
31
32
|
import pyrogram
|
|
32
33
|
from pyrogram import raw, enums
|
|
33
34
|
from pyrogram import types
|
|
35
|
+
from pyrogram.types.messages_and_media.message import Str
|
|
34
36
|
from pyrogram.file_id import FileId, FileType, PHOTO_TYPES, DOCUMENT_TYPES
|
|
35
37
|
|
|
36
38
|
|
|
@@ -89,122 +91,112 @@ def get_input_media_from_file_id(
|
|
|
89
91
|
|
|
90
92
|
|
|
91
93
|
async def parse_messages(
|
|
92
|
-
client,
|
|
93
|
-
messages: "raw.
|
|
94
|
+
client: "pyrogram.Client",
|
|
95
|
+
messages: Union["raw.base.messages.Messages", "raw.base.Updates"],
|
|
94
96
|
replies: int = 1,
|
|
95
97
|
business_connection_id: str = None
|
|
96
98
|
) -> List["types.Message"]:
|
|
97
|
-
users = {i.id: i for i in messages
|
|
98
|
-
chats = {i.id: i for i in messages
|
|
99
|
-
topics = {i.id: i for i in messages
|
|
100
|
-
if hasattr(messages, "topics") else None
|
|
101
|
-
|
|
102
|
-
if not messages.messages:
|
|
103
|
-
return types.List()
|
|
99
|
+
users = {i.id: i for i in getattr(messages, "users", [])}
|
|
100
|
+
chats = {i.id: i for i in getattr(messages, "chats", [])}
|
|
101
|
+
topics = {i.id: i for i in getattr(messages, "topics", [])}
|
|
104
102
|
|
|
105
103
|
parsed_messages = []
|
|
106
104
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
topics,
|
|
115
|
-
replies=0,
|
|
116
|
-
business_connection_id=business_connection_id
|
|
117
|
-
)
|
|
105
|
+
if isinstance(
|
|
106
|
+
messages,
|
|
107
|
+
(
|
|
108
|
+
raw.types.messages.ChannelMessages,
|
|
109
|
+
raw.types.messages.Messages,
|
|
110
|
+
raw.types.messages.MessagesNotModified,
|
|
111
|
+
raw.types.messages.MessagesSlice
|
|
118
112
|
)
|
|
113
|
+
):
|
|
114
|
+
if not messages.messages:
|
|
115
|
+
return types.List()
|
|
116
|
+
|
|
117
|
+
for message in messages.messages:
|
|
118
|
+
parsed_messages.append(
|
|
119
|
+
await types.Message._parse(
|
|
120
|
+
client=client,
|
|
121
|
+
message=message,
|
|
122
|
+
users=users,
|
|
123
|
+
chats=chats,
|
|
124
|
+
topics=topics,
|
|
125
|
+
replies=0,
|
|
126
|
+
business_connection_id=business_connection_id
|
|
127
|
+
)
|
|
128
|
+
)
|
|
119
129
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
for i in messages.messages
|
|
124
|
-
if not isinstance(i, raw.types.MessageEmpty) and i.reply_to and isinstance(i.reply_to, raw.types.MessageReplyHeader)
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
message_reply_to_story = {
|
|
128
|
-
i.id: {'user_id': i.reply_to.user_id, 'story_id': i.reply_to.story_id}
|
|
129
|
-
for i in messages.messages
|
|
130
|
-
if not isinstance(i, raw.types.MessageEmpty) and i.reply_to and isinstance(i.reply_to, raw.types.MessageReplyStoryHeader)
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
if messages_with_replies:
|
|
134
|
-
# We need a chat id, but some messages might be empty (no chat attribute available)
|
|
135
|
-
# Scan until we find a message with a chat available (there must be one, because we are fetching replies)
|
|
136
|
-
for m in parsed_messages:
|
|
137
|
-
if not isinstance(m, types.Message):
|
|
138
|
-
continue
|
|
139
|
-
|
|
140
|
-
if m.chat:
|
|
141
|
-
chat_id = m.chat.id
|
|
142
|
-
break
|
|
143
|
-
else:
|
|
144
|
-
chat_id = 0
|
|
130
|
+
if replies:
|
|
131
|
+
messages_with_replies = {}
|
|
132
|
+
messages_with_story_replies = {}
|
|
145
133
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
for value in messages_with_replies.values()
|
|
149
|
-
)
|
|
150
|
-
reply_messages: List[pyrogram.types.Message] = []
|
|
151
|
-
if is_all_within_chat:
|
|
152
|
-
# fast path: fetch all messages within the same chat
|
|
153
|
-
reply_messages = await client.get_messages(
|
|
154
|
-
chat_id,
|
|
155
|
-
reply_to_message_ids=messages_with_replies.keys(),
|
|
156
|
-
replies=replies - 1
|
|
157
|
-
)
|
|
158
|
-
else:
|
|
159
|
-
# slow path: fetch all messages individually
|
|
160
|
-
for target_reply_to in messages_with_replies.values():
|
|
161
|
-
to_be_added_msg = None
|
|
162
|
-
the_chat_id = chat_id
|
|
163
|
-
if target_reply_to.reply_to_peer_id:
|
|
164
|
-
the_chat_id = get_channel_id(target_reply_to.reply_to_peer_id.channel_id)
|
|
165
|
-
to_be_added_msg = await client.get_messages(
|
|
166
|
-
chat_id=the_chat_id,
|
|
167
|
-
message_ids=target_reply_to.reply_to_msg_id,
|
|
168
|
-
replies=replies - 1
|
|
169
|
-
)
|
|
170
|
-
if isinstance(to_be_added_msg, list):
|
|
171
|
-
for current_to_be_added in to_be_added_msg:
|
|
172
|
-
reply_messages.append(current_to_be_added)
|
|
173
|
-
elif to_be_added_msg:
|
|
174
|
-
reply_messages.append(to_be_added_msg)
|
|
175
|
-
|
|
176
|
-
for message in parsed_messages:
|
|
177
|
-
reply_to = messages_with_replies.get(message.id, None)
|
|
178
|
-
if not reply_to:
|
|
134
|
+
for m in messages.messages:
|
|
135
|
+
if isinstance(m, raw.types.MessageEmpty):
|
|
179
136
|
continue
|
|
180
137
|
|
|
181
|
-
|
|
138
|
+
if m.reply_to and isinstance(m.reply_to, raw.types.MessageReplyHeader):
|
|
139
|
+
messages_with_replies[m.id] = m.reply_to
|
|
182
140
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
message.reply_to_message = reply
|
|
141
|
+
if m.reply_to and isinstance(m.reply_to, raw.types.MessageReplyStoryHeader):
|
|
142
|
+
messages_with_story_replies[m.id] = m.reply_to
|
|
186
143
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
144
|
+
if messages_with_replies:
|
|
145
|
+
# We need a chat id, but some messages might be empty (no chat attribute available)
|
|
146
|
+
# Scan until we find a message with a chat available (there must be one, because we are fetching replies)
|
|
147
|
+
chat_id = next((m.chat.id for m in parsed_messages if m.chat), 0)
|
|
191
148
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
break
|
|
195
|
-
else:
|
|
196
|
-
chat_id = 0
|
|
149
|
+
is_all_replies_in_same_chat = not any(m.reply_to_peer_id for m in messages_with_replies.values())
|
|
150
|
+
reply_messages: List["types.Message"] = []
|
|
197
151
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
152
|
+
if is_all_replies_in_same_chat:
|
|
153
|
+
reply_messages = await client.get_messages(
|
|
154
|
+
chat_id,
|
|
155
|
+
reply_to_message_ids=list(messages_with_replies.keys()),
|
|
156
|
+
replies=replies - 1
|
|
157
|
+
)
|
|
158
|
+
else:
|
|
159
|
+
for reply_header in messages_with_replies.values():
|
|
160
|
+
reply_messages.append(
|
|
161
|
+
await client.get_messages(
|
|
162
|
+
chat_id=get_peer_id(reply_header.reply_to_peer_id) if getattr(reply_header, "reply_to_peer_id", None) else chat_id,
|
|
163
|
+
message_ids=reply_header.reply_to_msg_id,
|
|
164
|
+
replies=replies - 1
|
|
165
|
+
)
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
for message in parsed_messages:
|
|
169
|
+
reply_to = messages_with_replies.get(message.id, None)
|
|
170
|
+
|
|
171
|
+
if not reply_to:
|
|
172
|
+
continue
|
|
173
|
+
|
|
174
|
+
for reply in reply_messages:
|
|
175
|
+
if reply.id == reply_to.reply_to_msg_id:
|
|
176
|
+
message.reply_to_message = reply
|
|
177
|
+
else:
|
|
178
|
+
for u in getattr(messages, "updates", []):
|
|
179
|
+
if isinstance(
|
|
180
|
+
u,
|
|
181
|
+
(
|
|
182
|
+
raw.types.UpdateNewMessage,
|
|
183
|
+
raw.types.UpdateNewChannelMessage,
|
|
184
|
+
raw.types.UpdateNewScheduledMessage,
|
|
185
|
+
raw.types.UpdateBotNewBusinessMessage,
|
|
186
|
+
)
|
|
187
|
+
):
|
|
188
|
+
parsed_messages.append(
|
|
189
|
+
await types.Message._parse(
|
|
190
|
+
client,
|
|
191
|
+
u.message,
|
|
192
|
+
users,
|
|
193
|
+
chats,
|
|
194
|
+
is_scheduled=isinstance(u, raw.types.UpdateNewScheduledMessage),
|
|
195
|
+
business_connection_id=getattr(u, "connection_id", business_connection_id),
|
|
196
|
+
raw_reply_to_message=getattr(u, "reply_to_message", None),
|
|
197
|
+
replies=0
|
|
198
|
+
)
|
|
203
199
|
)
|
|
204
|
-
|
|
205
|
-
for message in parsed_messages:
|
|
206
|
-
if message.id in reply_messages:
|
|
207
|
-
message.reply_to_story = reply_messages[message.id]
|
|
208
200
|
|
|
209
201
|
return types.List(parsed_messages)
|
|
210
202
|
|
|
@@ -212,7 +204,6 @@ async def parse_messages(
|
|
|
212
204
|
def parse_deleted_messages(client, update, users, chats) -> List["types.Message"]:
|
|
213
205
|
messages = update.messages
|
|
214
206
|
channel_id = getattr(update, "channel_id", None)
|
|
215
|
-
business_connection_id = getattr(update, "connection_id", None)
|
|
216
207
|
peer = getattr(update, "peer", None)
|
|
217
208
|
|
|
218
209
|
chat = None
|
|
@@ -244,7 +235,7 @@ def parse_deleted_messages(client, update, users, chats) -> List["types.Message"
|
|
|
244
235
|
types.Message(
|
|
245
236
|
id=message,
|
|
246
237
|
chat=chat,
|
|
247
|
-
business_connection_id=
|
|
238
|
+
business_connection_id=getattr(update, "connection_id", None),
|
|
248
239
|
client=client
|
|
249
240
|
)
|
|
250
241
|
)
|
|
@@ -465,8 +456,8 @@ def compute_password_check(
|
|
|
465
456
|
async def parse_text_entities(
|
|
466
457
|
client: "pyrogram.Client",
|
|
467
458
|
text: str,
|
|
468
|
-
parse_mode: enums.ParseMode,
|
|
469
|
-
entities: List["types.MessageEntity"]
|
|
459
|
+
parse_mode: Optional[enums.ParseMode],
|
|
460
|
+
entities: Optional[List["types.MessageEntity"]]
|
|
470
461
|
) -> Dict[str, Union[str, List[raw.base.MessageEntity]]]:
|
|
471
462
|
if entities:
|
|
472
463
|
# Inject the client instance because parsing user mentions requires it
|
|
@@ -506,3 +497,80 @@ def get_first_url(text):
|
|
|
506
497
|
matches = re.findall(r"(https?):\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])", text)
|
|
507
498
|
|
|
508
499
|
return f"{matches[0][0]}://{matches[0][1]}{matches[0][2]}" if matches else None
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
def parse_text_with_entities(client, message: "raw.types.TextWithEntities", users):
|
|
503
|
+
entities = types.List(
|
|
504
|
+
filter(
|
|
505
|
+
lambda x: x is not None,
|
|
506
|
+
[
|
|
507
|
+
types.MessageEntity._parse(client, entity, users)
|
|
508
|
+
for entity in getattr(message, "entities", [])
|
|
509
|
+
]
|
|
510
|
+
)
|
|
511
|
+
)
|
|
512
|
+
|
|
513
|
+
return {
|
|
514
|
+
"text": Str(getattr(message, "text", "")).init(entities) or None,
|
|
515
|
+
"entities": entities or None
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
|
|
519
|
+
def expand_inline_bytes(bytes_data: bytes):
|
|
520
|
+
if len(bytes_data) < 3 or bytes_data[0] != 0x01:
|
|
521
|
+
return bytearray()
|
|
522
|
+
|
|
523
|
+
header = bytearray(
|
|
524
|
+
b"\xff\xd8\xff\xe0\x00\x10\x4a\x46\x49"
|
|
525
|
+
b"\x46\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xff\xdb\x00\x43\x00\x28\x1c"
|
|
526
|
+
b"\x1e\x23\x1e\x19\x28\x23\x21\x23\x2d\x2b\x28\x30\x3c\x64\x41\x3c\x37\x37"
|
|
527
|
+
b"\x3c\x7b\x58\x5d\x49\x64\x91\x80\x99\x96\x8f\x80\x8c\x8a\xa0\xb4\xe6\xc3"
|
|
528
|
+
b"\xa0\xaa\xda\xad\x8a\x8c\xc8\xff\xcb\xda\xee\xf5\xff\xff\xff\x9b\xc1\xff"
|
|
529
|
+
b"\xff\xff\xfa\xff\xe6\xfd\xff\xf8\xff\xdb\x00\x43\x01\x2b\x2d\x2d\x3c\x35"
|
|
530
|
+
b"\x3c\x76\x41\x41\x76\xf8\xa5\x8c\xa5\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8"
|
|
531
|
+
b"\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8"
|
|
532
|
+
b"\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8"
|
|
533
|
+
b"\xf8\xf8\xf8\xf8\xf8\xff\xc0\x00\x11\x08\x00\x00\x00\x00\x03\x01\x22\x00"
|
|
534
|
+
b"\x02\x11\x01\x03\x11\x01\xff\xc4\x00\x1f\x00\x00\x01\x05\x01\x01\x01\x01"
|
|
535
|
+
b"\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08"
|
|
536
|
+
b"\x09\x0a\x0b\xff\xc4\x00\xb5\x10\x00\x02\x01\x03\x03\x02\x04\x03\x05\x05"
|
|
537
|
+
b"\x04\x04\x00\x00\x01\x7d\x01\x02\x03\x00\x04\x11\x05\x12\x21\x31\x41\x06"
|
|
538
|
+
b"\x13\x51\x61\x07\x22\x71\x14\x32\x81\x91\xa1\x08\x23\x42\xb1\xc1\x15\x52"
|
|
539
|
+
b"\xd1\xf0\x24\x33\x62\x72\x82\x09\x0a\x16\x17\x18\x19\x1a\x25\x26\x27\x28"
|
|
540
|
+
b"\x29\x2a\x34\x35\x36\x37\x38\x39\x3a\x43\x44\x45\x46\x47\x48\x49\x4a\x53"
|
|
541
|
+
b"\x54\x55\x56\x57\x58\x59\x5a\x63\x64\x65\x66\x67\x68\x69\x6a\x73\x74\x75"
|
|
542
|
+
b"\x76\x77\x78\x79\x7a\x83\x84\x85\x86\x87\x88\x89\x8a\x92\x93\x94\x95\x96"
|
|
543
|
+
b"\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4\xb5\xb6"
|
|
544
|
+
b"\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xd2\xd3\xd4\xd5\xd6"
|
|
545
|
+
b"\xd7\xd8\xd9\xda\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf1\xf2\xf3\xf4"
|
|
546
|
+
b"\xf5\xf6\xf7\xf8\xf9\xfa\xff\xc4\x00\x1f\x01\x00\x03\x01\x01\x01\x01\x01"
|
|
547
|
+
b"\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08"
|
|
548
|
+
b"\x09\x0a\x0b\xff\xc4\x00\xb5\x11\x00\x02\x01\x02\x04\x04\x03\x04\x07\x05"
|
|
549
|
+
b"\x04\x04\x00\x01\x02\x77\x00\x01\x02\x03\x11\x04\x05\x21\x31\x06\x12\x41"
|
|
550
|
+
b"\x51\x07\x61\x71\x13\x22\x32\x81\x08\x14\x42\x91\xa1\xb1\xc1\x09\x23\x33"
|
|
551
|
+
b"\x52\xf0\x15\x62\x72\xd1\x0a\x16\x24\x34\xe1\x25\xf1\x17\x18\x19\x1a\x26"
|
|
552
|
+
b"\x27\x28\x29\x2a\x35\x36\x37\x38\x39\x3a\x43\x44\x45\x46\x47\x48\x49\x4a"
|
|
553
|
+
b"\x53\x54\x55\x56\x57\x58\x59\x5a\x63\x64\x65\x66\x67\x68\x69\x6a\x73\x74"
|
|
554
|
+
b"\x75\x76\x77\x78\x79\x7a\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x92\x93\x94"
|
|
555
|
+
b"\x95\x96\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4"
|
|
556
|
+
b"\xb5\xb6\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xd2\xd3\xd4"
|
|
557
|
+
b"\xd5\xd6\xd7\xd8\xd9\xda\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf2\xf3\xf4"
|
|
558
|
+
b"\xf5\xf6\xf7\xf8\xf9\xfa\xff\xda\x00\x0c\x03\x01\x00\x02\x11\x03\x11\x00"
|
|
559
|
+
b"\x3f\x00"
|
|
560
|
+
)
|
|
561
|
+
|
|
562
|
+
footer = bytearray(b"\xff\xd9")
|
|
563
|
+
|
|
564
|
+
header[164] = bytes_data[1]
|
|
565
|
+
header[166] = bytes_data[2]
|
|
566
|
+
|
|
567
|
+
return header + bytes_data[3:] + footer
|
|
568
|
+
|
|
569
|
+
|
|
570
|
+
def from_inline_bytes(data: bytes, file_name: str = None) -> BytesIO:
|
|
571
|
+
b = BytesIO()
|
|
572
|
+
|
|
573
|
+
b.write(data)
|
|
574
|
+
b.name = file_name or f"photo_{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}.jpg"
|
|
575
|
+
|
|
576
|
+
return b
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|