py-tgcalls 2.2.0b1__py3-none-any.whl → 2.2.0rc2__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: py-tgcalls
3
- Version: 2.2.0b1
3
+ Version: 2.2.0rc2
4
4
  Summary: Async client API for the Telegram Calls.
5
5
  Author-email: Laky-64 <iraci.matteo@gmail.com>
6
6
  Project-URL: Homepage, https://pytgcalls.github.io/
@@ -20,7 +20,7 @@ Requires-Python: >=3.9
20
20
  Description-Content-Type: text/markdown
21
21
  License-File: LICENSE
22
22
  Requires-Dist: aiohttp>=3.9.3
23
- Requires-Dist: ntgcalls<1.5.0,>=1.4.0b3
23
+ Requires-Dist: ntgcalls<3.0.0,>=2.0.0rc4
24
24
  Requires-Dist: deprecation
25
25
  Provides-Extra: pyrogram
26
26
  Requires-Dist: pyrogram>=1.2.20; extra == "pyrogram"
@@ -1,6 +1,6 @@
1
- py_tgcalls-2.2.0b1.dist-info/licenses/LICENSE,sha256=46mU2C5kSwOnkqkw9XQAJlhBL2JAf1_uCD8lVcXyMRg,7652
1
+ py_tgcalls-2.2.0rc2.dist-info/licenses/LICENSE,sha256=46mU2C5kSwOnkqkw9XQAJlhBL2JAf1_uCD8lVcXyMRg,7652
2
2
  pytgcalls/__init__.py,sha256=qbfwN7rYwIdCegMOzdcbvwazeNjDzgmowgcqLFNqKIM,308
3
- pytgcalls/__version__.py,sha256=vxOCce5MaSbG_e6e0p9wyBs_FQRS8KN-rxYr6a-pfLU,24
3
+ pytgcalls/__version__.py,sha256=Cyc7uLJxAPLnuEEaO5JtEF4rXv_8vGUZ7RKqg1iyVH8,25
4
4
  pytgcalls/environment.py,sha256=ctCHACvG6l8SdpPewSBhOvc70kbwpv18maC0TwLvZ08,1924
5
5
  pytgcalls/exceptions.py,sha256=Rijc-8T93WEWJxNW9jncU8_M6mYZZZcs8F2bqitEIeI,3787
6
6
  pytgcalls/ffmpeg.py,sha256=CZvSyuztc-TGKbKI9_2G7CLITe1ITf315YPyprWu_Pg,8645
@@ -15,7 +15,7 @@ pytgcalls/statictypes.py,sha256=CdlqgQNhTZ_uTE8-B8m01fJ7TlD2B42EI2QBPxDdAtA,3842
15
15
  pytgcalls/sync.py,sha256=IsOH3TD7cxUg_-zdGt12HoS8sBlXvcGayPZAoxxKM48,3396
16
16
  pytgcalls/version_manager.py,sha256=egeGgvb66zWlLTMuw2U-b0x8MfnRzMm1xAEVN87HF5c,296
17
17
  pytgcalls/wait_counter_lock.py,sha256=J-KCWUBCt7ktKQKyIseNG0RfXDVRh-h0wKhaZlf4aFs,437
18
- pytgcalls/ytdlp.py,sha256=_erYVXBYiD6jIo8xrozjBiaxm2mHXHTnEkUIUrxbj3c,2475
18
+ pytgcalls/ytdlp.py,sha256=MEPkVlFdbexbgqVRMt7C1YadPIER2R-4oN03osl5vq8,2473
19
19
  pytgcalls/custom_api/__init__.py,sha256=ZT8d0lc2YrDuw_YSFAXXHHMewoXGFZ-ANOBIAr0vGFQ,60
20
20
  pytgcalls/custom_api/custom_api.py,sha256=Ko3aS6psrwPmOhRPxvG0fepXt4STrA0StvINSxz4Cj8,1890
21
21
  pytgcalls/handlers/__init__.py,sha256=pubbxI4pLqQpAKf8-toD6ija4cpSvbCJOQFjTiDjX1E,75
@@ -70,12 +70,12 @@ pytgcalls/methods/utilities/run.py,sha256=cnYQd2xB5Cr_WS0Q2cXJZPGiN6JOCULzj1r4xX
70
70
  pytgcalls/methods/utilities/start.py,sha256=mn0kQZhTUuc-9CCJDbFIVsEtJ8kfnfZOGbVC505qVRM,3232
71
71
  pytgcalls/methods/utilities/stream_params.py,sha256=fOSloo1A7WTxaZEtOiPXjdexMeoJjq8CZtfHuIFX7Ns,3325
72
72
  pytgcalls/mtproto/__init__.py,sha256=X4zvzFG7km7qHyE0fdvA550WcOVO_xl_p__gvIfDGmw,130
73
- pytgcalls/mtproto/bridged_client.py,sha256=svXufZkCj7WM9XOWkuk5oJVQxgL1Bg_8rz7w_ZKWlQg,6451
74
- pytgcalls/mtproto/client_cache.py,sha256=fpmZhRUGD3G2t2LjEK0z4OPLmWVhJQZUvDW6bgr6StI,5907
75
- pytgcalls/mtproto/hydrogram_client.py,sha256=pSEtHowL833-vD5ppm78eBe7_Ik0pumChbDTMLsyP5Y,25553
73
+ pytgcalls/mtproto/bridged_client.py,sha256=b8RmasIkXBTn15tAWZJ2ptPfxhIh0rGoNuAYRUlaUyQ,6776
74
+ pytgcalls/mtproto/client_cache.py,sha256=iVcBW9WE2w4s-yswOZpZ1eC2t0yR8TjppS86qOv6f2Q,6292
75
+ pytgcalls/mtproto/hydrogram_client.py,sha256=hhQ2fnPR74G39hoJ3oKRP3jeC7c-QGSuVpyCMp2mqr4,28312
76
76
  pytgcalls/mtproto/mtproto_client.py,sha256=95A13HsNJfpNslsY_gWiqYXj_Vvo-pl3rVrsJGxftgg,8517
77
- pytgcalls/mtproto/pyrogram_client.py,sha256=linvRHAxZCb4PenJEN9-OtjQm7Go9OONyDotZcMydNY,25710
78
- pytgcalls/mtproto/telethon_client.py,sha256=iDMAAKvvFVigOrLPusG9ytiswxM2UCp4KZZuwtT-vKo,24987
77
+ pytgcalls/mtproto/pyrogram_client.py,sha256=vqFLbDLJt5rrhgTqmzuiW2mAVGl9-c9LK5cXgup0QAU,28308
78
+ pytgcalls/mtproto/telethon_client.py,sha256=ZMyLEZvog5iojoRyjcuk6-WIUnaJrJGnO6nfaKSMwao,26411
79
79
  pytgcalls/types/__init__.py,sha256=GlgBBXAwbNopXSeTTmiXktrEJhhN_rMBtuAllTBbN3k,1189
80
80
  pytgcalls/types/browsers.py,sha256=47Kr5q96n4Q4WvVhA6IUlS2egEcA9GRLlDeFcQYyc9M,9545
81
81
  pytgcalls/types/cache.py,sha256=FfsOcmYnsBGPlJoTPIXXYcUSpGE3rhx6cjIH77hyUL0,1059
@@ -116,7 +116,7 @@ pytgcalls/types/stream/record_stream.py,sha256=f4VQ6MY8HtOxt7vz0hWBFmbbAIvTRHpAI
116
116
  pytgcalls/types/stream/stream_ended.py,sha256=xR_kZwFf03hA6rw_nvI7Be7GwoCKzQf_1MKaGpPDXqY,716
117
117
  pytgcalls/types/stream/stream_frames.py,sha256=028ZhNV-mN3BGqMlmxusAV1xDQpXRYCeM0WXBZhRUhA,446
118
118
  pytgcalls/types/stream/video_quality.py,sha256=HBfWq005kh-D19MaVE9VzVdnODzrXf4IJUimCfslfiU,231
119
- py_tgcalls-2.2.0b1.dist-info/METADATA,sha256=m0Pdh7RF6YtcR-AcyyklygP1IUFwHgAKMpVOg_cc9Ew,5284
120
- py_tgcalls-2.2.0b1.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
121
- py_tgcalls-2.2.0b1.dist-info/top_level.txt,sha256=IUDUwn0KkcbUYZbCe9R5AUb2Ob-lmllNUGQqyeXXd8A,10
122
- py_tgcalls-2.2.0b1.dist-info/RECORD,,
119
+ py_tgcalls-2.2.0rc2.dist-info/METADATA,sha256=LkSNsGghHhrmEQAXwxTGygFaHVKhLD_yzsRIN7eqj2M,5286
120
+ py_tgcalls-2.2.0rc2.dist-info/WHEEL,sha256=DnLRTWE75wApRYVsjgc6wsVswC54sMSJhAEd4xhDpBk,91
121
+ py_tgcalls-2.2.0rc2.dist-info/top_level.txt,sha256=IUDUwn0KkcbUYZbCe9R5AUb2Ob-lmllNUGQqyeXXd8A,10
122
+ py_tgcalls-2.2.0rc2.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.3.1)
2
+ Generator: setuptools (80.4.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
pytgcalls/__version__.py CHANGED
@@ -1 +1 @@
1
- __version__ = '2.2.0b1'
1
+ __version__ = '2.2.0rc2'
@@ -1,4 +1,5 @@
1
1
  import random
2
+ import re
2
3
  from typing import Any
3
4
  from typing import List
4
5
  from typing import Optional
@@ -260,6 +261,17 @@ class BridgedClient(HandlersHolder):
260
261
  else:
261
262
  return None
262
263
 
264
+ @staticmethod
265
+ def extract_dc(error: str) -> Optional[int]:
266
+ dc_id = re.findall(
267
+ r'('
268
+ r'CALL_MIGRATE_|'
269
+ r'The file to be accessed is currently stored in DC *'
270
+ r')([0-9])',
271
+ error,
272
+ )
273
+ return int(dc_id[0][1]) if dc_id else None
274
+
263
275
  @staticmethod
264
276
  def rnd_id() -> int:
265
277
  return random.randint(0, 0x7FFFFFFF - 1)
@@ -22,6 +22,7 @@ class ClientCache:
22
22
  self._cache_duration = 1 if app.no_updates() else cache_duration
23
23
  self._full_chat_cache = Cache()
24
24
  self._call_participants_cache = Cache()
25
+ self._dc_call_cache = Cache()
25
26
  self._phone_calls = Cache()
26
27
 
27
28
  async def get_full_chat(
@@ -164,6 +165,23 @@ class ClientCache:
164
165
  ) -> None:
165
166
  self._full_chat_cache.pop(chat_id)
166
167
  self._call_participants_cache.pop(chat_id)
168
+ self._dc_call_cache.pop(chat_id)
169
+
170
+ def set_dc_call(
171
+ self,
172
+ chat_id: int,
173
+ dc_id: int,
174
+ ) -> None:
175
+ self._dc_call_cache.put(
176
+ chat_id,
177
+ dc_id,
178
+ )
179
+
180
+ def get_dc_call(
181
+ self,
182
+ chat_id: int,
183
+ ) -> Optional[int]:
184
+ return self._dc_call_cache.get(chat_id)
167
185
 
168
186
  def set_phone_call(
169
187
  self,
@@ -6,9 +6,14 @@ from typing import Union
6
6
 
7
7
  from hydrogram import Client
8
8
  from hydrogram import ContinuePropagation
9
+ from hydrogram.errors import AuthBytesInvalid
10
+ from hydrogram.errors import BadRequest
11
+ from hydrogram.errors import FileMigrate
9
12
  from hydrogram.errors import FloodWait
10
13
  from hydrogram.raw.base import InputPeer
11
14
  from hydrogram.raw.base import InputUser
15
+ from hydrogram.raw.functions.auth import ExportAuthorization
16
+ from hydrogram.raw.functions.auth import ImportAuthorization
12
17
  from hydrogram.raw.functions.channels import GetFullChannel
13
18
  from hydrogram.raw.functions.messages import GetDhConfig
14
19
  from hydrogram.raw.functions.messages import GetFullChat
@@ -62,6 +67,8 @@ from hydrogram.raw.types import UpdatePhoneCall
62
67
  from hydrogram.raw.types import UpdatePhoneCallSignalingData
63
68
  from hydrogram.raw.types import Updates
64
69
  from hydrogram.raw.types.messages import DhConfig
70
+ from hydrogram.session import Auth
71
+ from hydrogram.session import Session
65
72
  from ntgcalls import MediaSegmentQuality
66
73
  from ntgcalls import Protocol
67
74
 
@@ -317,7 +324,7 @@ class HydrogramClient(BridgedClient):
317
324
  chat = await self._app.resolve_peer(chat_id)
318
325
  if isinstance(chat, InputPeerChannel):
319
326
  input_call = (
320
- await self._app.invoke(
327
+ await self._invoke(
321
328
  GetFullChannel(
322
329
  channel=InputChannel(
323
330
  channel_id=chat.channel_id,
@@ -328,14 +335,14 @@ class HydrogramClient(BridgedClient):
328
335
  ).full_chat.call
329
336
  else:
330
337
  input_call = (
331
- await self._app.invoke(
338
+ await self._invoke(
332
339
  GetFullChat(chat_id=chat.chat_id),
333
340
  )
334
341
  ).full_chat.call
335
342
 
336
343
  if input_call is not None:
337
344
  raw_call = (
338
- await self._app.invoke(
345
+ await self._invoke(
339
346
  GetGroupCall(
340
347
  call=input_call,
341
348
  limit=-1,
@@ -356,7 +363,7 @@ class HydrogramClient(BridgedClient):
356
363
  return input_call
357
364
 
358
365
  async def get_dhc(self) -> DhConfig:
359
- return await self._app.invoke(
366
+ return await self._invoke(
360
367
  GetDhConfig(
361
368
  version=0,
362
369
  random_length=256,
@@ -378,7 +385,7 @@ class HydrogramClient(BridgedClient):
378
385
  participants = []
379
386
  next_offset = ''
380
387
  while True:
381
- result = await self._app.invoke(
388
+ result = await self._invoke(
382
389
  GetGroupParticipants(
383
390
  call=input_call,
384
391
  ids=[],
@@ -405,7 +412,7 @@ class HydrogramClient(BridgedClient):
405
412
  ) -> str:
406
413
  chat_call = await self._cache.get_full_chat(chat_id)
407
414
  if chat_call is not None:
408
- result: Updates = await self._app.invoke(
415
+ result: Updates = await self._invoke(
409
416
  JoinGroupCall(
410
417
  call=chat_call,
411
418
  params=DataJSON(data=json_join),
@@ -438,7 +445,7 @@ class HydrogramClient(BridgedClient):
438
445
  ):
439
446
  chat_call = await self._cache.get_full_chat(chat_id)
440
447
  if chat_call is not None:
441
- result: Updates = await self._app.invoke(
448
+ result: Updates = await self._invoke(
442
449
  JoinGroupCallPresentation(
443
450
  call=chat_call,
444
451
  params=DataJSON(data=json_join),
@@ -456,7 +463,7 @@ class HydrogramClient(BridgedClient):
456
463
  ):
457
464
  chat_call = await self._cache.get_full_chat(chat_id)
458
465
  if chat_call is not None:
459
- await self._app.invoke(
466
+ await self._invoke(
460
467
  LeaveGroupCallPresentation(
461
468
  call=chat_call,
462
469
  ),
@@ -469,7 +476,7 @@ class HydrogramClient(BridgedClient):
469
476
  protocol: Protocol,
470
477
  has_video: bool,
471
478
  ):
472
- update = await self._app.invoke(
479
+ update = await self._invoke(
473
480
  RequestCall(
474
481
  user_id=await self.resolve_peer(user_id),
475
482
  random_id=self.rnd_id(),
@@ -492,7 +499,7 @@ class HydrogramClient(BridgedClient):
492
499
  g_b: bytes,
493
500
  protocol: Protocol,
494
501
  ):
495
- await self._app.invoke(
502
+ await self._invoke(
496
503
  AcceptCall(
497
504
  peer=self._cache.get_phone_call(user_id),
498
505
  g_b=g_b,
@@ -508,7 +515,7 @@ class HydrogramClient(BridgedClient):
508
515
  protocol: Protocol,
509
516
  ) -> CallProtocol:
510
517
  res = (
511
- await self._app.invoke(
518
+ await self._invoke(
512
519
  ConfirmCall(
513
520
  peer=self._cache.get_phone_call(user_id),
514
521
  g_a=g_a,
@@ -528,7 +535,7 @@ class HydrogramClient(BridgedClient):
528
535
  user_id: int,
529
536
  data: bytes,
530
537
  ):
531
- await self._app.invoke(
538
+ await self._invoke(
532
539
  SendSignalingData(
533
540
  peer=self._cache.get_phone_call(user_id),
534
541
  data=data,
@@ -539,7 +546,7 @@ class HydrogramClient(BridgedClient):
539
546
  self,
540
547
  chat_id: int,
541
548
  ):
542
- result: Updates = await self._app.invoke(
549
+ result: Updates = await self._invoke(
543
550
  CreateGroupCall(
544
551
  peer=await self.resolve_peer(chat_id),
545
552
  random_id=self.rnd_id(),
@@ -569,7 +576,7 @@ class HydrogramClient(BridgedClient):
569
576
  ):
570
577
  chat_call = await self._cache.get_full_chat(chat_id)
571
578
  if chat_call is not None:
572
- await self._app.invoke(
579
+ await self._invoke(
573
580
  LeaveGroupCall(
574
581
  call=chat_call,
575
582
  source=0,
@@ -589,7 +596,7 @@ class HydrogramClient(BridgedClient):
589
596
  if is_missed
590
597
  else PhoneCallDiscardReasonHangup()
591
598
  )
592
- await self._app.invoke(
599
+ await self._invoke(
593
600
  DiscardCall(
594
601
  peer=peer,
595
602
  duration=0,
@@ -608,7 +615,7 @@ class HydrogramClient(BridgedClient):
608
615
  ):
609
616
  chat_call = await self._cache.get_full_chat(chat_id)
610
617
  if chat_call is not None:
611
- await self._app.invoke(
618
+ await self._invoke(
612
619
  EditGroupCallParticipant(
613
620
  call=chat_call,
614
621
  participant=participant,
@@ -629,7 +636,7 @@ class HydrogramClient(BridgedClient):
629
636
  if chat_call is not None:
630
637
  try:
631
638
  return (
632
- await self._app.invoke(
639
+ await self._invoke(
633
640
  GetFile(
634
641
  location=InputGroupCallStream(
635
642
  call=chat_call,
@@ -656,9 +663,8 @@ class HydrogramClient(BridgedClient):
656
663
  ):
657
664
  chat_call = await self._cache.get_full_chat(chat_id)
658
665
  if chat_call is not None:
659
- # noinspection PyBroadException
660
666
  channels = (
661
- await self._app.invoke(
667
+ await self._invoke(
662
668
  GetGroupCallStreamChannels(
663
669
  call=chat_call,
664
670
  ),
@@ -680,7 +686,7 @@ class HydrogramClient(BridgedClient):
680
686
  ):
681
687
  chat_call = await self._cache.get_full_chat(chat_id)
682
688
  if chat_call is not None:
683
- await self._app.invoke(
689
+ await self._invoke(
684
690
  EditGroupCallParticipant(
685
691
  call=chat_call,
686
692
  participant=participant,
@@ -719,5 +725,78 @@ class HydrogramClient(BridgedClient):
719
725
  def no_updates(self):
720
726
  return self._app.no_updates
721
727
 
728
+ async def _invoke(
729
+ self,
730
+ request,
731
+ dc_id: Optional[int] = None,
732
+ chat_id: Optional[int] = None,
733
+ sleep_threshold: Optional[int] = None,
734
+ ):
735
+ if chat_id is not None:
736
+ dc_id = self._cache.get_dc_call(chat_id)
737
+
738
+ if dc_id is None:
739
+ session = self._app
740
+ else:
741
+ session = self._app.media_sessions.get(dc_id)
742
+ if not session:
743
+ session = self._app.media_sessions[dc_id] = Session(
744
+ self._app,
745
+ dc_id,
746
+ await Auth(
747
+ self._app,
748
+ dc_id,
749
+ await self._app.storage.test_mode(),
750
+ ).create()
751
+ if dc_id != await self._app.storage.dc_id()
752
+ else await self._app.storage.auth_key(),
753
+ await self._app.storage.test_mode(),
754
+ is_media=True,
755
+ )
756
+ await session.start()
757
+ if dc_id != await self._app.storage.dc_id():
758
+ for _ in range(3):
759
+ exported_auth = await self._invoke(
760
+ ExportAuthorization(
761
+ dc_id=dc_id,
762
+ ),
763
+ )
764
+
765
+ try:
766
+ await session.invoke(
767
+ ImportAuthorization(
768
+ id=exported_auth.id,
769
+ bytes=exported_auth.bytes,
770
+ ),
771
+ )
772
+ except AuthBytesInvalid:
773
+ continue
774
+ else:
775
+ break
776
+ else:
777
+ raise AuthBytesInvalid
778
+ try:
779
+ return await session.invoke(
780
+ request,
781
+ sleep_threshold=sleep_threshold,
782
+ )
783
+ except (BadRequest, FileMigrate) as e:
784
+ dc_new = BridgedClient.extract_dc(
785
+ str(e),
786
+ )
787
+ if chat_id is not None and dc_new is not None:
788
+ self._cache.set_dc_call(
789
+ chat_id,
790
+ dc_new,
791
+ )
792
+ if dc_new is not None:
793
+ return await self._invoke(
794
+ request,
795
+ dc_new,
796
+ chat_id,
797
+ sleep_threshold,
798
+ )
799
+ raise
800
+
722
801
  async def start(self):
723
802
  await self._app.start()
@@ -4,14 +4,18 @@ from typing import List
4
4
  from typing import Optional
5
5
  from typing import Union
6
6
 
7
- import pyrogram
8
7
  from ntgcalls import MediaSegmentQuality
9
8
  from ntgcalls import Protocol
10
9
  from pyrogram import Client
11
10
  from pyrogram import ContinuePropagation
11
+ from pyrogram.errors import AuthBytesInvalid
12
+ from pyrogram.errors import BadRequest
13
+ from pyrogram.errors import FileMigrate
12
14
  from pyrogram.errors import FloodWait
13
15
  from pyrogram.raw.base import InputPeer
14
16
  from pyrogram.raw.base import InputUser
17
+ from pyrogram.raw.functions.auth import ExportAuthorization
18
+ from pyrogram.raw.functions.auth import ImportAuthorization
15
19
  from pyrogram.raw.functions.channels import GetFullChannel
16
20
  from pyrogram.raw.functions.messages import GetDhConfig
17
21
  from pyrogram.raw.functions.messages import GetFullChat
@@ -65,13 +69,14 @@ from pyrogram.raw.types import UpdatePhoneCall
65
69
  from pyrogram.raw.types import UpdatePhoneCallSignalingData
66
70
  from pyrogram.raw.types import Updates
67
71
  from pyrogram.raw.types.messages import DhConfig
72
+ from pyrogram.session import Auth
73
+ from pyrogram.session import Session
68
74
 
69
75
  from ..types import CallProtocol
70
76
  from ..types import ChatUpdate
71
77
  from ..types import GroupCallParticipant
72
78
  from ..types import RawCallUpdate
73
79
  from ..types import UpdatedGroupCallParticipant
74
- from ..version_manager import VersionManager
75
80
  from .bridged_client import BridgedClient
76
81
  from .client_cache import ClientCache
77
82
 
@@ -84,12 +89,6 @@ class PyrogramClient(BridgedClient):
84
89
  ):
85
90
  super().__init__()
86
91
  self._app: Client = client
87
- if VersionManager.version_tuple(
88
- pyrogram.__version__,
89
- ) > VersionManager.version_tuple(
90
- '2.0.0',
91
- ):
92
- self._app.send = self._app.invoke
93
92
  self._cache: ClientCache = ClientCache(
94
93
  cache_duration,
95
94
  self,
@@ -325,7 +324,7 @@ class PyrogramClient(BridgedClient):
325
324
  chat = await self._app.resolve_peer(chat_id)
326
325
  if isinstance(chat, InputPeerChannel):
327
326
  input_call = (
328
- await self._app.send(
327
+ await self._invoke(
329
328
  GetFullChannel(
330
329
  channel=InputChannel(
331
330
  channel_id=chat.channel_id,
@@ -336,14 +335,14 @@ class PyrogramClient(BridgedClient):
336
335
  ).full_chat.call
337
336
  else:
338
337
  input_call = (
339
- await self._app.send(
338
+ await self._invoke(
340
339
  GetFullChat(chat_id=chat.chat_id),
341
340
  )
342
341
  ).full_chat.call
343
342
 
344
343
  if input_call is not None:
345
344
  raw_call = (
346
- await self._app.send(
345
+ await self._invoke(
347
346
  GetGroupCall(
348
347
  call=input_call,
349
348
  limit=-1,
@@ -364,7 +363,7 @@ class PyrogramClient(BridgedClient):
364
363
  return input_call
365
364
 
366
365
  async def get_dhc(self) -> DhConfig:
367
- return await self._app.send(
366
+ return await self._invoke(
368
367
  GetDhConfig(
369
368
  version=0,
370
369
  random_length=256,
@@ -386,7 +385,7 @@ class PyrogramClient(BridgedClient):
386
385
  participants = []
387
386
  next_offset = ''
388
387
  while True:
389
- result = await self._app.send(
388
+ result = await self._invoke(
390
389
  GetGroupParticipants(
391
390
  call=input_call,
392
391
  ids=[],
@@ -413,7 +412,7 @@ class PyrogramClient(BridgedClient):
413
412
  ) -> str:
414
413
  chat_call = await self._cache.get_full_chat(chat_id)
415
414
  if chat_call is not None:
416
- result: Updates = await self._app.send(
415
+ result: Updates = await self._invoke(
417
416
  JoinGroupCall(
418
417
  call=chat_call,
419
418
  params=DataJSON(data=json_join),
@@ -446,7 +445,7 @@ class PyrogramClient(BridgedClient):
446
445
  ):
447
446
  chat_call = await self._cache.get_full_chat(chat_id)
448
447
  if chat_call is not None:
449
- result: Updates = await self._app.send(
448
+ result: Updates = await self._invoke(
450
449
  JoinGroupCallPresentation(
451
450
  call=chat_call,
452
451
  params=DataJSON(data=json_join),
@@ -464,7 +463,7 @@ class PyrogramClient(BridgedClient):
464
463
  ):
465
464
  chat_call = await self._cache.get_full_chat(chat_id)
466
465
  if chat_call is not None:
467
- await self._app.send(
466
+ await self._invoke(
468
467
  LeaveGroupCallPresentation(
469
468
  call=chat_call,
470
469
  ),
@@ -477,7 +476,7 @@ class PyrogramClient(BridgedClient):
477
476
  protocol: Protocol,
478
477
  has_video: bool,
479
478
  ):
480
- update = await self._app.invoke(
479
+ update = await self._invoke(
481
480
  RequestCall(
482
481
  user_id=await self.resolve_peer(user_id),
483
482
  random_id=self.rnd_id(),
@@ -500,7 +499,7 @@ class PyrogramClient(BridgedClient):
500
499
  g_b: bytes,
501
500
  protocol: Protocol,
502
501
  ):
503
- await self._app.invoke(
502
+ await self._invoke(
504
503
  AcceptCall(
505
504
  peer=self._cache.get_phone_call(user_id),
506
505
  g_b=g_b,
@@ -516,7 +515,7 @@ class PyrogramClient(BridgedClient):
516
515
  protocol: Protocol,
517
516
  ) -> CallProtocol:
518
517
  res = (
519
- await self._app.invoke(
518
+ await self._invoke(
520
519
  ConfirmCall(
521
520
  peer=self._cache.get_phone_call(user_id),
522
521
  g_a=g_a,
@@ -536,7 +535,7 @@ class PyrogramClient(BridgedClient):
536
535
  user_id: int,
537
536
  data: bytes,
538
537
  ):
539
- await self._app.invoke(
538
+ await self._invoke(
540
539
  SendSignalingData(
541
540
  peer=self._cache.get_phone_call(user_id),
542
541
  data=data,
@@ -547,7 +546,7 @@ class PyrogramClient(BridgedClient):
547
546
  self,
548
547
  chat_id: int,
549
548
  ):
550
- result: Updates = await self._app.send(
549
+ result: Updates = await self._invoke(
551
550
  CreateGroupCall(
552
551
  peer=await self.resolve_peer(chat_id),
553
552
  random_id=self.rnd_id(),
@@ -577,7 +576,7 @@ class PyrogramClient(BridgedClient):
577
576
  ):
578
577
  chat_call = await self._cache.get_full_chat(chat_id)
579
578
  if chat_call is not None:
580
- await self._app.send(
579
+ await self._invoke(
581
580
  LeaveGroupCall(
582
581
  call=chat_call,
583
582
  source=0,
@@ -597,7 +596,7 @@ class PyrogramClient(BridgedClient):
597
596
  if is_missed
598
597
  else PhoneCallDiscardReasonHangup()
599
598
  )
600
- await self._app.invoke(
599
+ await self._invoke(
601
600
  DiscardCall(
602
601
  peer=peer,
603
602
  duration=0,
@@ -616,7 +615,7 @@ class PyrogramClient(BridgedClient):
616
615
  ):
617
616
  chat_call = await self._cache.get_full_chat(chat_id)
618
617
  if chat_call is not None:
619
- await self._app.send(
618
+ await self._invoke(
620
619
  EditGroupCallParticipant(
621
620
  call=chat_call,
622
621
  participant=participant,
@@ -637,7 +636,7 @@ class PyrogramClient(BridgedClient):
637
636
  if chat_call is not None:
638
637
  try:
639
638
  return (
640
- await self._app.send(
639
+ await self._invoke(
641
640
  GetFile(
642
641
  location=InputGroupCallStream(
643
642
  call=chat_call,
@@ -651,6 +650,7 @@ class PyrogramClient(BridgedClient):
651
650
  offset=0,
652
651
  limit=limit,
653
652
  ),
653
+ chat_id=chat_id,
654
654
  sleep_threshold=0,
655
655
  )
656
656
  ).bytes
@@ -664,12 +664,12 @@ class PyrogramClient(BridgedClient):
664
664
  ):
665
665
  chat_call = await self._cache.get_full_chat(chat_id)
666
666
  if chat_call is not None:
667
- # noinspection PyBroadException
668
667
  channels = (
669
- await self._app.send(
668
+ await self._invoke(
670
669
  GetGroupCallStreamChannels(
671
670
  call=chat_call,
672
671
  ),
672
+ chat_id=chat_id,
673
673
  )
674
674
  ).channels
675
675
  if len(channels) > 0:
@@ -688,7 +688,7 @@ class PyrogramClient(BridgedClient):
688
688
  ):
689
689
  chat_call = await self._cache.get_full_chat(chat_id)
690
690
  if chat_call is not None:
691
- await self._app.send(
691
+ await self._invoke(
692
692
  EditGroupCallParticipant(
693
693
  call=chat_call,
694
694
  participant=participant,
@@ -727,5 +727,78 @@ class PyrogramClient(BridgedClient):
727
727
  def no_updates(self):
728
728
  return self._app.no_updates
729
729
 
730
+ async def _invoke(
731
+ self,
732
+ request,
733
+ dc_id: Optional[int] = None,
734
+ chat_id: Optional[int] = None,
735
+ sleep_threshold: Optional[int] = None,
736
+ ):
737
+ if chat_id is not None:
738
+ dc_id = self._cache.get_dc_call(chat_id)
739
+
740
+ if dc_id is None:
741
+ session = self._app
742
+ else:
743
+ session = self._app.media_sessions.get(dc_id)
744
+ if not session:
745
+ session = self._app.media_sessions[dc_id] = Session(
746
+ self._app,
747
+ dc_id,
748
+ await Auth(
749
+ self._app,
750
+ dc_id,
751
+ await self._app.storage.test_mode(),
752
+ ).create()
753
+ if dc_id != await self._app.storage.dc_id()
754
+ else await self._app.storage.auth_key(),
755
+ await self._app.storage.test_mode(),
756
+ is_media=True,
757
+ )
758
+ await session.start()
759
+ if dc_id != await self._app.storage.dc_id():
760
+ for _ in range(3):
761
+ exported_auth = await self._invoke(
762
+ ExportAuthorization(
763
+ dc_id=dc_id,
764
+ ),
765
+ )
766
+
767
+ try:
768
+ await session.invoke(
769
+ ImportAuthorization(
770
+ id=exported_auth.id,
771
+ bytes=exported_auth.bytes,
772
+ ),
773
+ )
774
+ except AuthBytesInvalid:
775
+ continue
776
+ else:
777
+ break
778
+ else:
779
+ raise AuthBytesInvalid
780
+ try:
781
+ return await session.invoke(
782
+ request,
783
+ sleep_threshold=sleep_threshold,
784
+ )
785
+ except (BadRequest, FileMigrate) as e:
786
+ dc_new = BridgedClient.extract_dc(
787
+ str(e),
788
+ )
789
+ if chat_id is not None and dc_new is not None:
790
+ self._cache.set_dc_call(
791
+ chat_id,
792
+ dc_new,
793
+ )
794
+ if dc_new is not None:
795
+ return await self._invoke(
796
+ request,
797
+ dc_new,
798
+ chat_id,
799
+ sleep_threshold,
800
+ )
801
+ raise
802
+
730
803
  async def start(self):
731
804
  await self._app.start()
@@ -6,7 +6,9 @@ from typing import Union
6
6
  from ntgcalls import MediaSegmentQuality
7
7
  from ntgcalls import Protocol
8
8
  from telethon import TelegramClient
9
+ from telethon.errors import BadRequestError
9
10
  from telethon.errors import ChannelPrivateError
11
+ from telethon.errors import FileMigrateError
10
12
  from telethon.errors import FloodWaitError
11
13
  from telethon.events import Raw
12
14
  from telethon.tl.functions.channels import GetFullChannelRequest
@@ -309,7 +311,7 @@ class TelethonClient(BridgedClient):
309
311
  chat = await self._app.get_input_entity(chat_id)
310
312
  if isinstance(chat, InputPeerChannel):
311
313
  input_call = (
312
- await self._app(
314
+ await self._invoke(
313
315
  GetFullChannelRequest(
314
316
  InputChannel(
315
317
  chat.channel_id,
@@ -320,14 +322,14 @@ class TelethonClient(BridgedClient):
320
322
  ).full_chat.call
321
323
  else:
322
324
  input_call = (
323
- await self._app(
325
+ await self._invoke(
324
326
  GetFullChatRequest(chat_id),
325
327
  )
326
328
  ).full_chat.call
327
329
 
328
330
  if input_call is not None:
329
331
  raw_call = (
330
- await self._app(
332
+ await self._invoke(
331
333
  GetGroupCallRequest(
332
334
  call=input_call,
333
335
  limit=-1,
@@ -348,7 +350,7 @@ class TelethonClient(BridgedClient):
348
350
  return input_call
349
351
 
350
352
  async def get_dhc(self) -> DhConfig:
351
- return await self._app(
353
+ return await self._invoke(
352
354
  GetDhConfigRequest(
353
355
  version=0,
354
356
  random_length=256,
@@ -370,7 +372,7 @@ class TelethonClient(BridgedClient):
370
372
  participants = []
371
373
  next_offset = ''
372
374
  while True:
373
- result = await self._app(
375
+ result = await self._invoke(
374
376
  GetGroupParticipantsRequest(
375
377
  call=input_call,
376
378
  ids=[],
@@ -397,7 +399,7 @@ class TelethonClient(BridgedClient):
397
399
  ) -> str:
398
400
  chat_call = await self._cache.get_full_chat(chat_id)
399
401
  if chat_call is not None:
400
- result: Updates = await self._app(
402
+ result: Updates = await self._invoke(
401
403
  JoinGroupCallRequest(
402
404
  call=chat_call,
403
405
  params=DataJSON(data=json_join),
@@ -430,7 +432,7 @@ class TelethonClient(BridgedClient):
430
432
  ):
431
433
  chat_call = await self._cache.get_full_chat(chat_id)
432
434
  if chat_call is not None:
433
- result: Updates = await self._app(
435
+ result: Updates = await self._invoke(
434
436
  JoinGroupCallPresentationRequest(
435
437
  call=chat_call,
436
438
  params=DataJSON(data=json_join),
@@ -448,7 +450,7 @@ class TelethonClient(BridgedClient):
448
450
  ):
449
451
  chat_call = await self._cache.get_full_chat(chat_id)
450
452
  if chat_call is not None:
451
- await self._app(
453
+ await self._invoke(
452
454
  LeaveGroupCallPresentationRequest(
453
455
  call=chat_call,
454
456
  ),
@@ -461,7 +463,7 @@ class TelethonClient(BridgedClient):
461
463
  protocol: Protocol,
462
464
  has_video: bool,
463
465
  ):
464
- update = await self._app(
466
+ update = await self._invoke(
465
467
  RequestCallRequest(
466
468
  user_id=await self.resolve_peer(user_id),
467
469
  random_id=self.rnd_id(),
@@ -484,7 +486,7 @@ class TelethonClient(BridgedClient):
484
486
  g_b: bytes,
485
487
  protocol: Protocol,
486
488
  ):
487
- return await self._app(
489
+ return await self._invoke(
488
490
  AcceptCallRequest(
489
491
  peer=self._cache.get_phone_call(user_id),
490
492
  g_b=g_b,
@@ -500,7 +502,7 @@ class TelethonClient(BridgedClient):
500
502
  protocol: Protocol,
501
503
  ) -> CallProtocol:
502
504
  res = (
503
- await self._app(
505
+ await self._invoke(
504
506
  ConfirmCallRequest(
505
507
  peer=self._cache.get_phone_call(user_id),
506
508
  g_a=g_a,
@@ -520,7 +522,7 @@ class TelethonClient(BridgedClient):
520
522
  user_id: int,
521
523
  data: bytes,
522
524
  ):
523
- await self._app(
525
+ await self._invoke(
524
526
  SendSignalingDataRequest(
525
527
  peer=self._cache.get_phone_call(user_id),
526
528
  data=data,
@@ -531,7 +533,7 @@ class TelethonClient(BridgedClient):
531
533
  self,
532
534
  chat_id: int,
533
535
  ):
534
- result: Updates = await self._app(
536
+ result: Updates = await self._invoke(
535
537
  CreateGroupCallRequest(
536
538
  peer=await self.resolve_peer(chat_id),
537
539
  random_id=self.rnd_id(),
@@ -561,7 +563,7 @@ class TelethonClient(BridgedClient):
561
563
  ):
562
564
  chat_call = await self._cache.get_full_chat(chat_id)
563
565
  if chat_call is not None:
564
- await self._app(
566
+ await self._invoke(
565
567
  LeaveGroupCallRequest(
566
568
  call=chat_call,
567
569
  source=0,
@@ -581,7 +583,7 @@ class TelethonClient(BridgedClient):
581
583
  if is_missed
582
584
  else PhoneCallDiscardReasonHangup()
583
585
  )
584
- await self._app(
586
+ await self._invoke(
585
587
  DiscardCallRequest(
586
588
  peer=peer,
587
589
  duration=0,
@@ -600,7 +602,7 @@ class TelethonClient(BridgedClient):
600
602
  ):
601
603
  chat_call = await self._cache.get_full_chat(chat_id)
602
604
  if chat_call is not None:
603
- await self._app(
605
+ await self._invoke(
604
606
  EditGroupCallParticipantRequest(
605
607
  call=chat_call,
606
608
  participant=participant,
@@ -621,7 +623,7 @@ class TelethonClient(BridgedClient):
621
623
  if chat_call is not None:
622
624
  try:
623
625
  return (
624
- await self._app(
626
+ await self._invoke(
625
627
  GetFileRequest(
626
628
  location=InputGroupCallStream(
627
629
  call=chat_call,
@@ -635,7 +637,8 @@ class TelethonClient(BridgedClient):
635
637
  offset=0,
636
638
  limit=limit,
637
639
  ),
638
- flood_sleep_threshold=0,
640
+ chat_id=chat_id,
641
+ sleep_threshold=0,
639
642
  )
640
643
  ).bytes
641
644
  except FloodWaitError:
@@ -648,12 +651,12 @@ class TelethonClient(BridgedClient):
648
651
  ):
649
652
  chat_call = await self._cache.get_full_chat(chat_id)
650
653
  if chat_call is not None:
651
- # noinspection PyBroadException
652
654
  channels = (
653
- await self._app(
655
+ await self._invoke(
654
656
  GetGroupCallStreamChannelsRequest(
655
657
  call=chat_call,
656
658
  ),
659
+ chat_id=chat_id,
657
660
  )
658
661
  ).channels
659
662
  if len(channels) > 0:
@@ -672,7 +675,7 @@ class TelethonClient(BridgedClient):
672
675
  ):
673
676
  chat_call = await self._cache.get_full_chat(chat_id)
674
677
  if chat_call is not None:
675
- await self._app(
678
+ await self._invoke(
676
679
  EditGroupCallParticipantRequest(
677
680
  call=chat_call,
678
681
  participant=participant,
@@ -711,6 +714,44 @@ class TelethonClient(BridgedClient):
711
714
  def no_updates(self):
712
715
  return False
713
716
 
717
+ # noinspection PyProtectedMember
718
+ async def _invoke(
719
+ self,
720
+ request,
721
+ dc_id: Optional[int] = None,
722
+ chat_id: Optional[int] = None,
723
+ sleep_threshold: Optional[int] = None,
724
+ ):
725
+ try:
726
+ if chat_id is not None:
727
+ dc_id = self._cache.get_dc_call(chat_id)
728
+ if dc_id is None or self._app.session.dc_id == dc_id:
729
+ sender_dc = self._app._sender
730
+ else:
731
+ sender_dc = await self._app._borrow_exported_sender(dc_id)
732
+ return await self._app._call(
733
+ sender_dc,
734
+ request,
735
+ flood_sleep_threshold=sleep_threshold,
736
+ )
737
+ except (BadRequestError, FileMigrateError) as e:
738
+ dc_new = BridgedClient.extract_dc(
739
+ str(e),
740
+ )
741
+ if chat_id is not None and dc_new is not None:
742
+ self._cache.set_dc_call(
743
+ chat_id,
744
+ dc_new,
745
+ )
746
+ if dc_new is not None:
747
+ return await self._invoke(
748
+ request,
749
+ dc_new,
750
+ chat_id,
751
+ sleep_threshold,
752
+ )
753
+ raise
754
+
714
755
  # noinspection PyUnresolvedReferences
715
756
  async def start(self):
716
757
  await self._app.start()
pytgcalls/ytdlp.py CHANGED
@@ -38,7 +38,7 @@ class YtDlp:
38
38
  'yt-dlp',
39
39
  '-g',
40
40
  '-f',
41
- 'bestvideo[vcodec~=\'(vp09|avc1)\']+m4a/best',
41
+ 'bestvideo[vcodec~="(vp09|avc1)"]+m4a/best',
42
42
  '-S',
43
43
  'res:'
44
44
  f'{min(video_parameters.width, video_parameters.height)}',