py-tgcalls 2.1.0rc7__py3-none-any.whl → 2.1.2b1__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.
- py_tgcalls-2.1.2b1.dist-info/METADATA +126 -0
- {py_tgcalls-2.1.0rc7.dist-info → py_tgcalls-2.1.2b1.dist-info}/RECORD +44 -29
- {py_tgcalls-2.1.0rc7.dist-info → py_tgcalls-2.1.2b1.dist-info}/WHEEL +1 -1
- pytgcalls/__version__.py +1 -1
- pytgcalls/exceptions.py +7 -31
- pytgcalls/methods/__init__.py +2 -0
- pytgcalls/methods/calls/leave_call.py +1 -1
- pytgcalls/methods/internal/__init__.py +35 -0
- pytgcalls/methods/internal/clear_cache.py +8 -0
- pytgcalls/methods/internal/clear_call.py +20 -0
- pytgcalls/methods/internal/connect_call.py +133 -0
- pytgcalls/methods/internal/emit_sig_data.py +9 -0
- pytgcalls/methods/internal/handle_connection_changed.py +27 -0
- pytgcalls/methods/internal/handle_mtproto_updates.py +175 -0
- pytgcalls/methods/internal/handle_stream_ended.py +23 -0
- pytgcalls/methods/internal/handle_stream_frame.py +41 -0
- pytgcalls/methods/internal/join_presentation.py +58 -0
- pytgcalls/methods/internal/request_broadcast_part.py +42 -0
- pytgcalls/methods/internal/request_broadcast_timestamp.py +25 -0
- pytgcalls/methods/internal/switch_connection.py +35 -0
- pytgcalls/methods/internal/update_status.py +22 -0
- pytgcalls/methods/stream/play.py +7 -106
- pytgcalls/methods/stream/record.py +0 -6
- pytgcalls/methods/utilities/__init__.py +0 -6
- pytgcalls/methods/utilities/start.py +23 -289
- pytgcalls/mtproto/bridged_client.py +30 -8
- pytgcalls/mtproto/hydrogram_client.py +77 -5
- pytgcalls/mtproto/mtproto_client.py +37 -4
- pytgcalls/mtproto/pyrogram_client.py +81 -9
- pytgcalls/mtproto/telethon_client.py +77 -5
- pytgcalls/mutex.py +13 -1
- pytgcalls/pytgcalls.py +3 -0
- pytgcalls/scaffold.py +79 -0
- pytgcalls/types/__init__.py +2 -0
- pytgcalls/types/calls/__init__.py +2 -0
- pytgcalls/types/calls/pending_connection.py +17 -0
- pytgcalls/types/chats/chat_update.py +8 -1
- pytgcalls/types/py_object.py +9 -10
- pytgcalls/types/stream/media_stream.py +1 -1
- pytgcalls/wait_counter_lock.py +20 -0
- py_tgcalls-2.1.0rc7.dist-info/METADATA +0 -292
- pytgcalls/methods/utilities/join_presentation.py +0 -50
- {py_tgcalls-2.1.0rc7.dist-info → py_tgcalls-2.1.2b1.dist-info/licenses}/LICENSE +0 -0
- {py_tgcalls-2.1.0rc7.dist-info → py_tgcalls-2.1.2b1.dist-info}/top_level.txt +0 -0
- /pytgcalls/methods/{utilities → internal}/log_retries.py +0 -0
- /pytgcalls/methods/{utilities → internal}/update_sources.py +0 -0
|
@@ -5,9 +5,11 @@ from typing import Optional
|
|
|
5
5
|
from typing import Union
|
|
6
6
|
|
|
7
7
|
import pyrogram
|
|
8
|
+
from ntgcalls import MediaSegmentQuality
|
|
8
9
|
from ntgcalls import Protocol
|
|
9
10
|
from pyrogram import Client
|
|
10
11
|
from pyrogram import ContinuePropagation
|
|
12
|
+
from pyrogram.errors import FloodWait
|
|
11
13
|
from pyrogram.raw.base import InputPeer
|
|
12
14
|
from pyrogram.raw.base import InputUser
|
|
13
15
|
from pyrogram.raw.functions.channels import GetFullChannel
|
|
@@ -19,6 +21,7 @@ from pyrogram.raw.functions.phone import CreateGroupCall
|
|
|
19
21
|
from pyrogram.raw.functions.phone import DiscardCall
|
|
20
22
|
from pyrogram.raw.functions.phone import EditGroupCallParticipant
|
|
21
23
|
from pyrogram.raw.functions.phone import GetGroupCall
|
|
24
|
+
from pyrogram.raw.functions.phone import GetGroupCallStreamChannels
|
|
22
25
|
from pyrogram.raw.functions.phone import GetGroupParticipants
|
|
23
26
|
from pyrogram.raw.functions.phone import JoinGroupCall
|
|
24
27
|
from pyrogram.raw.functions.phone import JoinGroupCallPresentation
|
|
@@ -26,6 +29,7 @@ from pyrogram.raw.functions.phone import LeaveGroupCall
|
|
|
26
29
|
from pyrogram.raw.functions.phone import LeaveGroupCallPresentation
|
|
27
30
|
from pyrogram.raw.functions.phone import RequestCall
|
|
28
31
|
from pyrogram.raw.functions.phone import SendSignalingData
|
|
32
|
+
from pyrogram.raw.functions.upload import GetFile
|
|
29
33
|
from pyrogram.raw.types import Channel
|
|
30
34
|
from pyrogram.raw.types import ChannelForbidden
|
|
31
35
|
from pyrogram.raw.types import Chat
|
|
@@ -35,6 +39,7 @@ from pyrogram.raw.types import GroupCall
|
|
|
35
39
|
from pyrogram.raw.types import GroupCallDiscarded
|
|
36
40
|
from pyrogram.raw.types import InputChannel
|
|
37
41
|
from pyrogram.raw.types import InputGroupCall
|
|
42
|
+
from pyrogram.raw.types import InputGroupCallStream
|
|
38
43
|
from pyrogram.raw.types import InputPeerChannel
|
|
39
44
|
from pyrogram.raw.types import InputPhoneCall
|
|
40
45
|
from pyrogram.raw.types import MessageActionChatDeleteUser
|
|
@@ -44,6 +49,7 @@ from pyrogram.raw.types import PeerChat
|
|
|
44
49
|
from pyrogram.raw.types import PhoneCall
|
|
45
50
|
from pyrogram.raw.types import PhoneCallAccepted
|
|
46
51
|
from pyrogram.raw.types import PhoneCallDiscarded
|
|
52
|
+
from pyrogram.raw.types import PhoneCallDiscardReasonBusy
|
|
47
53
|
from pyrogram.raw.types import PhoneCallDiscardReasonHangup
|
|
48
54
|
from pyrogram.raw.types import PhoneCallDiscardReasonMissed
|
|
49
55
|
from pyrogram.raw.types import PhoneCallProtocol
|
|
@@ -137,10 +143,16 @@ class PyrogramClient(BridgedClient):
|
|
|
137
143
|
self._cache.drop_phone_call(
|
|
138
144
|
user_id,
|
|
139
145
|
)
|
|
146
|
+
reason = ChatUpdate.Status.DISCARDED_CALL
|
|
147
|
+
if isinstance(
|
|
148
|
+
update.phone_call.reason,
|
|
149
|
+
PhoneCallDiscardReasonBusy,
|
|
150
|
+
):
|
|
151
|
+
reason |= ChatUpdate.Status.BUSY_CALL
|
|
140
152
|
await self._propagate(
|
|
141
153
|
ChatUpdate(
|
|
142
154
|
user_id,
|
|
143
|
-
|
|
155
|
+
reason,
|
|
144
156
|
),
|
|
145
157
|
)
|
|
146
158
|
if isinstance(update.phone_call, PhoneCallRequested):
|
|
@@ -396,7 +408,7 @@ class PyrogramClient(BridgedClient):
|
|
|
396
408
|
chat_id: int,
|
|
397
409
|
json_join: str,
|
|
398
410
|
invite_hash: str,
|
|
399
|
-
|
|
411
|
+
video_stopped: bool,
|
|
400
412
|
join_as: InputPeer,
|
|
401
413
|
) -> str:
|
|
402
414
|
chat_call = await self._cache.get_full_chat(chat_id)
|
|
@@ -407,7 +419,7 @@ class PyrogramClient(BridgedClient):
|
|
|
407
419
|
params=DataJSON(data=json_join),
|
|
408
420
|
muted=False,
|
|
409
421
|
join_as=join_as,
|
|
410
|
-
video_stopped=
|
|
422
|
+
video_stopped=video_stopped,
|
|
411
423
|
invite_hash=invite_hash,
|
|
412
424
|
),
|
|
413
425
|
)
|
|
@@ -463,14 +475,22 @@ class PyrogramClient(BridgedClient):
|
|
|
463
475
|
user_id: int,
|
|
464
476
|
g_a_hash: bytes,
|
|
465
477
|
protocol: Protocol,
|
|
478
|
+
has_video: bool,
|
|
466
479
|
):
|
|
467
|
-
await self._app.invoke(
|
|
480
|
+
update = await self._app.invoke(
|
|
468
481
|
RequestCall(
|
|
469
482
|
user_id=await self.resolve_peer(user_id),
|
|
470
483
|
random_id=self.rnd_id(),
|
|
471
484
|
g_a_hash=g_a_hash,
|
|
472
485
|
protocol=self.parse_protocol(protocol),
|
|
473
|
-
video=
|
|
486
|
+
video=has_video,
|
|
487
|
+
),
|
|
488
|
+
)
|
|
489
|
+
self._cache.set_phone_call(
|
|
490
|
+
user_id,
|
|
491
|
+
InputPhoneCall(
|
|
492
|
+
id=update.phone_call.id,
|
|
493
|
+
access_hash=update.phone_call.access_hash,
|
|
474
494
|
),
|
|
475
495
|
)
|
|
476
496
|
|
|
@@ -524,8 +544,8 @@ class PyrogramClient(BridgedClient):
|
|
|
524
544
|
)
|
|
525
545
|
|
|
526
546
|
async def create_group_call(
|
|
527
|
-
|
|
528
|
-
|
|
547
|
+
self,
|
|
548
|
+
chat_id: int,
|
|
529
549
|
):
|
|
530
550
|
result: Updates = await self._app.send(
|
|
531
551
|
CreateGroupCall(
|
|
@@ -552,8 +572,8 @@ class PyrogramClient(BridgedClient):
|
|
|
552
572
|
)
|
|
553
573
|
|
|
554
574
|
async def leave_group_call(
|
|
555
|
-
|
|
556
|
-
|
|
575
|
+
self,
|
|
576
|
+
chat_id: int,
|
|
557
577
|
):
|
|
558
578
|
chat_call = await self._cache.get_full_chat(chat_id)
|
|
559
579
|
if chat_call is not None:
|
|
@@ -605,6 +625,58 @@ class PyrogramClient(BridgedClient):
|
|
|
605
625
|
),
|
|
606
626
|
)
|
|
607
627
|
|
|
628
|
+
async def download_stream(
|
|
629
|
+
self,
|
|
630
|
+
chat_id: int,
|
|
631
|
+
timestamp: int,
|
|
632
|
+
limit: int,
|
|
633
|
+
video_channel: Optional[int],
|
|
634
|
+
video_quality: MediaSegmentQuality,
|
|
635
|
+
):
|
|
636
|
+
chat_call = await self._cache.get_full_chat(chat_id)
|
|
637
|
+
if chat_call is not None:
|
|
638
|
+
try:
|
|
639
|
+
return (
|
|
640
|
+
await self._app.send(
|
|
641
|
+
GetFile(
|
|
642
|
+
location=InputGroupCallStream(
|
|
643
|
+
call=chat_call,
|
|
644
|
+
time_ms=timestamp,
|
|
645
|
+
scale=0,
|
|
646
|
+
video_channel=video_channel,
|
|
647
|
+
video_quality=BridgedClient.parse_quality(
|
|
648
|
+
video_quality,
|
|
649
|
+
),
|
|
650
|
+
),
|
|
651
|
+
offset=0,
|
|
652
|
+
limit=limit,
|
|
653
|
+
),
|
|
654
|
+
sleep_threshold=0,
|
|
655
|
+
)
|
|
656
|
+
).bytes
|
|
657
|
+
except FloodWait:
|
|
658
|
+
pass
|
|
659
|
+
return None
|
|
660
|
+
|
|
661
|
+
async def get_stream_timestamp(
|
|
662
|
+
self,
|
|
663
|
+
chat_id: int,
|
|
664
|
+
):
|
|
665
|
+
chat_call = await self._cache.get_full_chat(chat_id)
|
|
666
|
+
if chat_call is not None:
|
|
667
|
+
# noinspection PyBroadException
|
|
668
|
+
channels = (
|
|
669
|
+
await self._app.send(
|
|
670
|
+
GetGroupCallStreamChannels(
|
|
671
|
+
call=chat_call,
|
|
672
|
+
),
|
|
673
|
+
)
|
|
674
|
+
).channels
|
|
675
|
+
if len(channels) > 0:
|
|
676
|
+
return channels[0].last_timestamp_ms
|
|
677
|
+
|
|
678
|
+
return 0
|
|
679
|
+
|
|
608
680
|
async def set_call_status(
|
|
609
681
|
self,
|
|
610
682
|
chat_id: int,
|
|
@@ -3,9 +3,11 @@ from typing import List
|
|
|
3
3
|
from typing import Optional
|
|
4
4
|
from typing import Union
|
|
5
5
|
|
|
6
|
+
from ntgcalls import MediaSegmentQuality
|
|
6
7
|
from ntgcalls import Protocol
|
|
7
8
|
from telethon import TelegramClient
|
|
8
9
|
from telethon.errors import ChannelPrivateError
|
|
10
|
+
from telethon.errors import FloodWaitError
|
|
9
11
|
from telethon.events import Raw
|
|
10
12
|
from telethon.tl.functions.channels import GetFullChannelRequest
|
|
11
13
|
from telethon.tl.functions.messages import GetDhConfigRequest
|
|
@@ -16,6 +18,7 @@ from telethon.tl.functions.phone import CreateGroupCallRequest
|
|
|
16
18
|
from telethon.tl.functions.phone import DiscardCallRequest
|
|
17
19
|
from telethon.tl.functions.phone import EditGroupCallParticipantRequest
|
|
18
20
|
from telethon.tl.functions.phone import GetGroupCallRequest
|
|
21
|
+
from telethon.tl.functions.phone import GetGroupCallStreamChannelsRequest
|
|
19
22
|
from telethon.tl.functions.phone import GetGroupParticipantsRequest
|
|
20
23
|
from telethon.tl.functions.phone import JoinGroupCallPresentationRequest
|
|
21
24
|
from telethon.tl.functions.phone import JoinGroupCallRequest
|
|
@@ -23,12 +26,14 @@ from telethon.tl.functions.phone import LeaveGroupCallPresentationRequest
|
|
|
23
26
|
from telethon.tl.functions.phone import LeaveGroupCallRequest
|
|
24
27
|
from telethon.tl.functions.phone import RequestCallRequest
|
|
25
28
|
from telethon.tl.functions.phone import SendSignalingDataRequest
|
|
29
|
+
from telethon.tl.functions.upload import GetFileRequest
|
|
26
30
|
from telethon.tl.types import ChatForbidden
|
|
27
31
|
from telethon.tl.types import DataJSON
|
|
28
32
|
from telethon.tl.types import GroupCall
|
|
29
33
|
from telethon.tl.types import GroupCallDiscarded
|
|
30
34
|
from telethon.tl.types import InputChannel
|
|
31
35
|
from telethon.tl.types import InputGroupCall
|
|
36
|
+
from telethon.tl.types import InputGroupCallStream
|
|
32
37
|
from telethon.tl.types import InputPeerChannel
|
|
33
38
|
from telethon.tl.types import InputPhoneCall
|
|
34
39
|
from telethon.tl.types import MessageActionChatDeleteUser
|
|
@@ -39,6 +44,7 @@ from telethon.tl.types import PeerChat
|
|
|
39
44
|
from telethon.tl.types import PhoneCall
|
|
40
45
|
from telethon.tl.types import PhoneCallAccepted
|
|
41
46
|
from telethon.tl.types import PhoneCallDiscarded
|
|
47
|
+
from telethon.tl.types import PhoneCallDiscardReasonBusy
|
|
42
48
|
from telethon.tl.types import PhoneCallDiscardReasonHangup
|
|
43
49
|
from telethon.tl.types import PhoneCallDiscardReasonMissed
|
|
44
50
|
from telethon.tl.types import PhoneCallProtocol
|
|
@@ -128,10 +134,16 @@ class TelethonClient(BridgedClient):
|
|
|
128
134
|
self._cache.drop_phone_call(
|
|
129
135
|
user_id,
|
|
130
136
|
)
|
|
137
|
+
reason = ChatUpdate.Status.DISCARDED_CALL
|
|
138
|
+
if isinstance(
|
|
139
|
+
update.phone_call.reason,
|
|
140
|
+
PhoneCallDiscardReasonBusy,
|
|
141
|
+
):
|
|
142
|
+
reason |= ChatUpdate.Status.BUSY_CALL
|
|
131
143
|
await self._propagate(
|
|
132
144
|
ChatUpdate(
|
|
133
145
|
user_id,
|
|
134
|
-
|
|
146
|
+
reason,
|
|
135
147
|
),
|
|
136
148
|
)
|
|
137
149
|
if isinstance(update.phone_call, PhoneCallRequested):
|
|
@@ -380,7 +392,7 @@ class TelethonClient(BridgedClient):
|
|
|
380
392
|
chat_id: int,
|
|
381
393
|
json_join: str,
|
|
382
394
|
invite_hash: str,
|
|
383
|
-
|
|
395
|
+
video_stopped: bool,
|
|
384
396
|
join_as: TypeInputPeer,
|
|
385
397
|
) -> str:
|
|
386
398
|
chat_call = await self._cache.get_full_chat(chat_id)
|
|
@@ -391,7 +403,7 @@ class TelethonClient(BridgedClient):
|
|
|
391
403
|
params=DataJSON(data=json_join),
|
|
392
404
|
muted=False,
|
|
393
405
|
join_as=join_as,
|
|
394
|
-
video_stopped=
|
|
406
|
+
video_stopped=video_stopped,
|
|
395
407
|
invite_hash=invite_hash,
|
|
396
408
|
),
|
|
397
409
|
)
|
|
@@ -447,14 +459,22 @@ class TelethonClient(BridgedClient):
|
|
|
447
459
|
user_id: int,
|
|
448
460
|
g_a_hash: bytes,
|
|
449
461
|
protocol: Protocol,
|
|
462
|
+
has_video: bool,
|
|
450
463
|
):
|
|
451
|
-
|
|
464
|
+
update = await self._app(
|
|
452
465
|
RequestCallRequest(
|
|
453
466
|
user_id=await self.resolve_peer(user_id),
|
|
454
467
|
random_id=self.rnd_id(),
|
|
455
468
|
g_a_hash=g_a_hash,
|
|
456
469
|
protocol=self.parse_protocol(protocol),
|
|
457
|
-
video=
|
|
470
|
+
video=has_video,
|
|
471
|
+
),
|
|
472
|
+
)
|
|
473
|
+
self._cache.set_phone_call(
|
|
474
|
+
user_id,
|
|
475
|
+
InputPhoneCall(
|
|
476
|
+
id=update.phone_call.id,
|
|
477
|
+
access_hash=update.phone_call.access_hash,
|
|
458
478
|
),
|
|
459
479
|
)
|
|
460
480
|
|
|
@@ -589,6 +609,58 @@ class TelethonClient(BridgedClient):
|
|
|
589
609
|
),
|
|
590
610
|
)
|
|
591
611
|
|
|
612
|
+
async def download_stream(
|
|
613
|
+
self,
|
|
614
|
+
chat_id: int,
|
|
615
|
+
timestamp: int,
|
|
616
|
+
limit: int,
|
|
617
|
+
video_channel: Optional[int],
|
|
618
|
+
video_quality: MediaSegmentQuality,
|
|
619
|
+
):
|
|
620
|
+
chat_call = await self._cache.get_full_chat(chat_id)
|
|
621
|
+
if chat_call is not None:
|
|
622
|
+
try:
|
|
623
|
+
return (
|
|
624
|
+
await self._app(
|
|
625
|
+
GetFileRequest(
|
|
626
|
+
location=InputGroupCallStream(
|
|
627
|
+
call=chat_call,
|
|
628
|
+
time_ms=timestamp,
|
|
629
|
+
scale=0,
|
|
630
|
+
video_channel=video_channel,
|
|
631
|
+
video_quality=BridgedClient.parse_quality(
|
|
632
|
+
video_quality,
|
|
633
|
+
),
|
|
634
|
+
),
|
|
635
|
+
offset=0,
|
|
636
|
+
limit=limit,
|
|
637
|
+
),
|
|
638
|
+
flood_sleep_threshold=0,
|
|
639
|
+
)
|
|
640
|
+
).bytes
|
|
641
|
+
except FloodWaitError:
|
|
642
|
+
pass
|
|
643
|
+
return None
|
|
644
|
+
|
|
645
|
+
async def get_stream_timestamp(
|
|
646
|
+
self,
|
|
647
|
+
chat_id: int,
|
|
648
|
+
):
|
|
649
|
+
chat_call = await self._cache.get_full_chat(chat_id)
|
|
650
|
+
if chat_call is not None:
|
|
651
|
+
# noinspection PyBroadException
|
|
652
|
+
channels = (
|
|
653
|
+
await self._app(
|
|
654
|
+
GetGroupCallStreamChannelsRequest(
|
|
655
|
+
call=chat_call,
|
|
656
|
+
),
|
|
657
|
+
)
|
|
658
|
+
).channels
|
|
659
|
+
if len(channels) > 0:
|
|
660
|
+
return channels[0].last_timestamp_ms
|
|
661
|
+
|
|
662
|
+
return 0
|
|
663
|
+
|
|
592
664
|
async def set_call_status(
|
|
593
665
|
self,
|
|
594
666
|
chat_id: int,
|
pytgcalls/mutex.py
CHANGED
|
@@ -1,10 +1,22 @@
|
|
|
1
1
|
from functools import wraps
|
|
2
2
|
|
|
3
|
+
from .wait_counter_lock import WaitCounterLock
|
|
4
|
+
|
|
3
5
|
|
|
4
6
|
def mutex(func):
|
|
5
7
|
@wraps(func)
|
|
6
8
|
async def async_wrapper(*args, **kwargs):
|
|
7
9
|
self = args[0]
|
|
10
|
+
chat_id = await self.resolve_chat_id(args[1])
|
|
11
|
+
async with self._lock:
|
|
12
|
+
self._calls_lock[chat_id] = self._calls_lock.get(
|
|
13
|
+
chat_id,
|
|
14
|
+
) or WaitCounterLock()
|
|
15
|
+
async with self._calls_lock[chat_id]:
|
|
16
|
+
result = await func(*args, **kwargs)
|
|
17
|
+
|
|
8
18
|
async with self._lock:
|
|
9
|
-
|
|
19
|
+
if not self._calls_lock[chat_id].waiters():
|
|
20
|
+
self._calls_lock.pop(chat_id, None)
|
|
21
|
+
return result
|
|
10
22
|
return async_wrapper
|
pytgcalls/pytgcalls.py
CHANGED
|
@@ -2,6 +2,7 @@ import asyncio
|
|
|
2
2
|
import os
|
|
3
3
|
from concurrent.futures import ThreadPoolExecutor
|
|
4
4
|
from typing import Any
|
|
5
|
+
from typing import Dict
|
|
5
6
|
|
|
6
7
|
from ntgcalls import NTgCalls
|
|
7
8
|
|
|
@@ -11,6 +12,7 @@ from .mtproto import MtProtoClient
|
|
|
11
12
|
from .scaffold import Scaffold
|
|
12
13
|
from .statictypes import statictypes
|
|
13
14
|
from .types import Cache
|
|
15
|
+
from .wait_counter_lock import WaitCounterLock
|
|
14
16
|
|
|
15
17
|
|
|
16
18
|
class PyTgCalls(Methods, Scaffold):
|
|
@@ -42,6 +44,7 @@ class PyTgCalls(Methods, Scaffold):
|
|
|
42
44
|
self.loop = asyncio.get_event_loop()
|
|
43
45
|
self.workers = workers
|
|
44
46
|
self._lock = asyncio.Lock()
|
|
47
|
+
self._calls_lock: Dict[str, WaitCounterLock] = {}
|
|
45
48
|
self.executor = ThreadPoolExecutor(
|
|
46
49
|
self.workers,
|
|
47
50
|
thread_name_prefix='Handler',
|
pytgcalls/scaffold.py
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
from typing import Optional
|
|
1
3
|
from typing import Union
|
|
2
4
|
|
|
5
|
+
from ntgcalls import Frame as RawFrame
|
|
6
|
+
from ntgcalls import MediaDescription
|
|
7
|
+
from ntgcalls import MediaState
|
|
8
|
+
from ntgcalls import NetworkInfo
|
|
9
|
+
from ntgcalls import SegmentPartRequest
|
|
10
|
+
from ntgcalls import StreamDevice
|
|
11
|
+
from ntgcalls import StreamMode
|
|
12
|
+
from ntgcalls import StreamType
|
|
13
|
+
|
|
3
14
|
from .handlers import HandlersHolder
|
|
15
|
+
from .types import CallConfig
|
|
16
|
+
from .types import GroupCallConfig
|
|
17
|
+
from .types import Update
|
|
4
18
|
|
|
5
19
|
|
|
6
20
|
class Scaffold(HandlersHolder):
|
|
@@ -25,6 +39,7 @@ class Scaffold(HandlersHolder):
|
|
|
25
39
|
self._call_sources = dict()
|
|
26
40
|
self._wait_connect = dict()
|
|
27
41
|
self._presentations = set()
|
|
42
|
+
self._pending_connections = dict()
|
|
28
43
|
|
|
29
44
|
def _handle_mtproto(self):
|
|
30
45
|
pass
|
|
@@ -47,9 +62,73 @@ class Scaffold(HandlersHolder):
|
|
|
47
62
|
async def _join_presentation(self, chat_id: Union[int, str], join: bool):
|
|
48
63
|
pass
|
|
49
64
|
|
|
65
|
+
async def _clear_call(self, chat_id: int):
|
|
66
|
+
pass
|
|
67
|
+
|
|
68
|
+
async def _update_status(self, chat_id: int, state: MediaState):
|
|
69
|
+
pass
|
|
70
|
+
|
|
71
|
+
async def _switch_connection(self, chat_id: int):
|
|
72
|
+
pass
|
|
73
|
+
|
|
74
|
+
async def _handle_stream_ended(
|
|
75
|
+
self,
|
|
76
|
+
chat_id: int,
|
|
77
|
+
stream_type: StreamType,
|
|
78
|
+
device: StreamDevice,
|
|
79
|
+
):
|
|
80
|
+
pass
|
|
81
|
+
|
|
82
|
+
async def _emit_sig_data(self, chat_id: int, data: bytes):
|
|
83
|
+
pass
|
|
84
|
+
|
|
85
|
+
async def _request_broadcast_timestamp(
|
|
86
|
+
self,
|
|
87
|
+
chat_id: int,
|
|
88
|
+
):
|
|
89
|
+
pass
|
|
90
|
+
|
|
91
|
+
async def _request_broadcast_part(
|
|
92
|
+
self,
|
|
93
|
+
chat_id: int,
|
|
94
|
+
part_request: SegmentPartRequest,
|
|
95
|
+
):
|
|
96
|
+
pass
|
|
97
|
+
|
|
98
|
+
async def _handle_stream_frame(
|
|
99
|
+
self,
|
|
100
|
+
chat_id: int,
|
|
101
|
+
mode: StreamMode,
|
|
102
|
+
device: StreamDevice,
|
|
103
|
+
frames: List[RawFrame],
|
|
104
|
+
):
|
|
105
|
+
pass
|
|
106
|
+
|
|
107
|
+
async def _handle_connection_changed(
|
|
108
|
+
self,
|
|
109
|
+
chat_id: int,
|
|
110
|
+
net_state: NetworkInfo,
|
|
111
|
+
):
|
|
112
|
+
pass
|
|
113
|
+
|
|
114
|
+
async def _handle_mtproto_updates(self, update: Update):
|
|
115
|
+
pass
|
|
116
|
+
|
|
117
|
+
async def _connect_call(
|
|
118
|
+
self,
|
|
119
|
+
chat_id: int,
|
|
120
|
+
media_description: MediaDescription,
|
|
121
|
+
config: Union[CallConfig, GroupCallConfig],
|
|
122
|
+
payload: Optional[str],
|
|
123
|
+
):
|
|
124
|
+
pass
|
|
125
|
+
|
|
50
126
|
@staticmethod
|
|
51
127
|
def _log_retries(r: int):
|
|
52
128
|
pass
|
|
53
129
|
|
|
130
|
+
def _clear_cache(self, chat_id: int):
|
|
131
|
+
pass
|
|
132
|
+
|
|
54
133
|
def on_update(self, filters=None):
|
|
55
134
|
pass
|
pytgcalls/types/__init__.py
CHANGED
|
@@ -5,6 +5,7 @@ from .calls import CallConfig
|
|
|
5
5
|
from .calls import CallData
|
|
6
6
|
from .calls import CallProtocol
|
|
7
7
|
from .calls import GroupCallConfig
|
|
8
|
+
from .calls import PendingConnection
|
|
8
9
|
from .calls import RawCallUpdate
|
|
9
10
|
from .chats import ChatUpdate
|
|
10
11
|
from .chats import GroupCallParticipant
|
|
@@ -35,6 +36,7 @@ __all__ = (
|
|
|
35
36
|
'CallProtocol',
|
|
36
37
|
'CallData',
|
|
37
38
|
'RawCallUpdate',
|
|
39
|
+
'PendingConnection',
|
|
38
40
|
'GroupCallConfig',
|
|
39
41
|
'GroupCallParticipant',
|
|
40
42
|
'RecordStream',
|
|
@@ -4,6 +4,7 @@ from .call_data import CallData
|
|
|
4
4
|
from .call_protocol import CallProtocol
|
|
5
5
|
from .call_sources import CallSources
|
|
6
6
|
from .group_call_config import GroupCallConfig
|
|
7
|
+
from .pending_connection import PendingConnection
|
|
7
8
|
from .raw_call_update import RawCallUpdate
|
|
8
9
|
|
|
9
10
|
__all__ = (
|
|
@@ -13,5 +14,6 @@ __all__ = (
|
|
|
13
14
|
'CallProtocol',
|
|
14
15
|
'CallSources',
|
|
15
16
|
'GroupCallConfig',
|
|
17
|
+
'PendingConnection',
|
|
16
18
|
'RawCallUpdate',
|
|
17
19
|
)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from ntgcalls import MediaDescription
|
|
2
|
+
|
|
3
|
+
from .group_call_config import GroupCallConfig
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class PendingConnection:
|
|
7
|
+
def __init__(
|
|
8
|
+
self,
|
|
9
|
+
media_description: MediaDescription,
|
|
10
|
+
config: GroupCallConfig,
|
|
11
|
+
payload: str,
|
|
12
|
+
presentation: bool,
|
|
13
|
+
):
|
|
14
|
+
self.media_description = media_description
|
|
15
|
+
self.config = config
|
|
16
|
+
self.payload = payload
|
|
17
|
+
self.presentation = presentation
|
|
@@ -13,7 +13,14 @@ class ChatUpdate(Update):
|
|
|
13
13
|
INVITED_VOICE_CHAT = auto()
|
|
14
14
|
DISCARDED_CALL = auto()
|
|
15
15
|
INCOMING_CALL = auto()
|
|
16
|
-
|
|
16
|
+
BUSY_CALL = auto()
|
|
17
|
+
LEFT_CALL = (
|
|
18
|
+
KICKED |
|
|
19
|
+
LEFT_GROUP |
|
|
20
|
+
CLOSED_VOICE_CHAT |
|
|
21
|
+
DISCARDED_CALL |
|
|
22
|
+
BUSY_CALL
|
|
23
|
+
)
|
|
17
24
|
|
|
18
25
|
def __init__(
|
|
19
26
|
self,
|
pytgcalls/types/py_object.py
CHANGED
|
@@ -13,16 +13,15 @@ class PyObject:
|
|
|
13
13
|
return repr(obj)
|
|
14
14
|
if isinstance(obj, Enum):
|
|
15
15
|
return repr(obj)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
return {}
|
|
16
|
+
return {
|
|
17
|
+
'_': obj.__class__.__name__,
|
|
18
|
+
**{
|
|
19
|
+
attr: getattr(obj, attr)
|
|
20
|
+
for attr in dir(obj)
|
|
21
|
+
if not attr.startswith('_') and
|
|
22
|
+
not callable(getattr(obj, attr)) and not attr == 'default'
|
|
23
|
+
},
|
|
24
|
+
}
|
|
26
25
|
|
|
27
26
|
def __str__(self) -> str:
|
|
28
27
|
return dumps(
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class WaitCounterLock:
|
|
5
|
+
def __init__(self):
|
|
6
|
+
self._lock = asyncio.Lock()
|
|
7
|
+
self._waiters = 0
|
|
8
|
+
|
|
9
|
+
def waiters(self):
|
|
10
|
+
return self._waiters
|
|
11
|
+
|
|
12
|
+
async def __aenter__(self):
|
|
13
|
+
self._waiters += 1
|
|
14
|
+
await self._lock.acquire()
|
|
15
|
+
self._waiters -= 1
|
|
16
|
+
return self
|
|
17
|
+
|
|
18
|
+
async def __aexit__(self, exc_type, exc, tb):
|
|
19
|
+
if self._lock.locked():
|
|
20
|
+
self._lock.release()
|