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
|
@@ -1,302 +1,19 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import logging
|
|
3
|
-
from typing import List
|
|
4
3
|
|
|
5
|
-
from ntgcalls import ConnectionError
|
|
6
|
-
from ntgcalls import ConnectionNotFound
|
|
7
|
-
from ntgcalls import ConnectionState
|
|
8
|
-
from ntgcalls import Frame as RawFrame
|
|
9
|
-
from ntgcalls import MediaState
|
|
10
|
-
from ntgcalls import NetworkInfo
|
|
11
|
-
from ntgcalls import StreamDevice
|
|
12
|
-
from ntgcalls import StreamMode
|
|
13
|
-
from ntgcalls import StreamType
|
|
14
|
-
from ntgcalls import TelegramServerError
|
|
15
|
-
|
|
16
|
-
from ...exceptions import CallDeclined
|
|
17
|
-
from ...exceptions import CallDiscarded
|
|
18
4
|
from ...exceptions import PyTgCallsAlreadyRunning
|
|
19
|
-
from ...mtproto import BridgedClient
|
|
20
5
|
from ...pytgcalls_session import PyTgCallsSession
|
|
21
6
|
from ...scaffold import Scaffold
|
|
22
|
-
from ...types import CallData
|
|
23
|
-
from ...types import ChatUpdate
|
|
24
|
-
from ...types import Device
|
|
25
|
-
from ...types import Direction
|
|
26
|
-
from ...types import Frame
|
|
27
|
-
from ...types import GroupCallParticipant
|
|
28
|
-
from ...types import RawCallUpdate
|
|
29
|
-
from ...types import StreamEnded
|
|
30
|
-
from ...types import StreamFrames
|
|
31
|
-
from ...types import Update
|
|
32
|
-
from ...types import UpdatedGroupCallParticipant
|
|
33
7
|
|
|
34
8
|
py_logger = logging.getLogger('pytgcalls')
|
|
35
9
|
|
|
36
10
|
|
|
37
11
|
class Start(Scaffold):
|
|
38
12
|
async def start(self):
|
|
39
|
-
|
|
40
|
-
@self._app.on_update()
|
|
41
|
-
async def update_handler(update: Update):
|
|
42
|
-
chat_id = update.chat_id
|
|
43
|
-
if update.chat_id in self._p2p_configs:
|
|
44
|
-
p2p_config = self._p2p_configs[chat_id]
|
|
45
|
-
if not p2p_config.wait_data.done():
|
|
46
|
-
if isinstance(update, RawCallUpdate):
|
|
47
|
-
if update.status & RawCallUpdate.Type.UPDATED_CALL:
|
|
48
|
-
p2p_config.wait_data.set_result(
|
|
49
|
-
update,
|
|
50
|
-
)
|
|
51
|
-
if isinstance(update, ChatUpdate) and \
|
|
52
|
-
p2p_config.outgoing:
|
|
53
|
-
if update.status & ChatUpdate.Status.DISCARDED_CALL:
|
|
54
|
-
self._wait_connect.pop(chat_id, None)
|
|
55
|
-
p2p_config.wait_data.set_exception(
|
|
56
|
-
CallDeclined(
|
|
57
|
-
chat_id,
|
|
58
|
-
),
|
|
59
|
-
)
|
|
60
|
-
if chat_id in self._wait_connect and \
|
|
61
|
-
not self._wait_connect[chat_id].done():
|
|
62
|
-
if isinstance(update, ChatUpdate):
|
|
63
|
-
if update.status & ChatUpdate.Status.DISCARDED_CALL:
|
|
64
|
-
self._wait_connect[chat_id].set_exception(
|
|
65
|
-
CallDiscarded(
|
|
66
|
-
chat_id,
|
|
67
|
-
),
|
|
68
|
-
)
|
|
69
|
-
if isinstance(update, RawCallUpdate):
|
|
70
|
-
if update.status & RawCallUpdate.Type.REQUESTED:
|
|
71
|
-
self._p2p_configs[chat_id] = CallData(
|
|
72
|
-
await self._app.get_dhc(),
|
|
73
|
-
self.loop,
|
|
74
|
-
update.g_a_or_b,
|
|
75
|
-
)
|
|
76
|
-
update = ChatUpdate(
|
|
77
|
-
chat_id,
|
|
78
|
-
ChatUpdate.Status.INCOMING_CALL,
|
|
79
|
-
)
|
|
80
|
-
if isinstance(update, RawCallUpdate):
|
|
81
|
-
if update.status & RawCallUpdate.Type.SIGNALING_DATA:
|
|
82
|
-
try:
|
|
83
|
-
await self._binding.send_signaling(
|
|
84
|
-
update.chat_id,
|
|
85
|
-
update.signaling_data,
|
|
86
|
-
)
|
|
87
|
-
except (ConnectionNotFound, ConnectionError):
|
|
88
|
-
pass
|
|
89
|
-
if isinstance(update, ChatUpdate):
|
|
90
|
-
if update.status & ChatUpdate.Status.LEFT_CALL:
|
|
91
|
-
await clear_call(chat_id)
|
|
92
|
-
if isinstance(update, UpdatedGroupCallParticipant):
|
|
93
|
-
participant = update.participant
|
|
94
|
-
action = participant.action
|
|
95
|
-
chat_peer = self._cache_user_peer.get(chat_id)
|
|
96
|
-
user_id = participant.user_id
|
|
97
|
-
if chat_id in self._call_sources:
|
|
98
|
-
call_sources = self._call_sources[chat_id]
|
|
99
|
-
was_camera = user_id in call_sources.camera
|
|
100
|
-
was_screen = user_id in call_sources.presentation
|
|
101
|
-
|
|
102
|
-
if was_camera != participant.video_camera:
|
|
103
|
-
if participant.video_info:
|
|
104
|
-
self._call_sources[chat_id].camera[
|
|
105
|
-
user_id
|
|
106
|
-
] = participant.video_info.endpoint
|
|
107
|
-
try:
|
|
108
|
-
await self._binding.add_incoming_video(
|
|
109
|
-
chat_id,
|
|
110
|
-
participant.video_info.endpoint,
|
|
111
|
-
participant.video_info.sources,
|
|
112
|
-
)
|
|
113
|
-
except (ConnectionNotFound, ConnectionError):
|
|
114
|
-
pass
|
|
115
|
-
elif user_id in self._call_sources[chat_id].camera:
|
|
116
|
-
try:
|
|
117
|
-
await self._binding.remove_incoming_video(
|
|
118
|
-
chat_id,
|
|
119
|
-
self._call_sources[
|
|
120
|
-
chat_id
|
|
121
|
-
].camera[user_id],
|
|
122
|
-
)
|
|
123
|
-
except (ConnectionNotFound, ConnectionError):
|
|
124
|
-
pass
|
|
125
|
-
self._call_sources[chat_id].camera.pop(
|
|
126
|
-
user_id, None,
|
|
127
|
-
)
|
|
128
|
-
|
|
129
|
-
if was_screen != participant.screen_sharing:
|
|
130
|
-
if participant.presentation_info:
|
|
131
|
-
self._call_sources[chat_id].presentation[
|
|
132
|
-
user_id
|
|
133
|
-
] = participant.presentation_info.endpoint
|
|
134
|
-
try:
|
|
135
|
-
await self._binding.add_incoming_video(
|
|
136
|
-
chat_id,
|
|
137
|
-
participant.presentation_info.endpoint,
|
|
138
|
-
participant.presentation_info.sources,
|
|
139
|
-
)
|
|
140
|
-
except (ConnectionNotFound, ConnectionError):
|
|
141
|
-
pass
|
|
142
|
-
elif user_id in self._call_sources[
|
|
143
|
-
chat_id
|
|
144
|
-
].presentation:
|
|
145
|
-
try:
|
|
146
|
-
await self._binding.remove_incoming_video(
|
|
147
|
-
chat_id,
|
|
148
|
-
self._call_sources[
|
|
149
|
-
chat_id
|
|
150
|
-
].presentation[user_id],
|
|
151
|
-
)
|
|
152
|
-
except (ConnectionNotFound, ConnectionError):
|
|
153
|
-
pass
|
|
154
|
-
self._call_sources[chat_id].presentation.pop(
|
|
155
|
-
user_id, None,
|
|
156
|
-
)
|
|
157
|
-
|
|
158
|
-
if chat_peer:
|
|
159
|
-
is_self = BridgedClient.chat_id(
|
|
160
|
-
chat_peer,
|
|
161
|
-
) == participant.user_id if chat_peer else False
|
|
162
|
-
if is_self:
|
|
163
|
-
if action == GroupCallParticipant.Action.LEFT:
|
|
164
|
-
if await clear_call(chat_id):
|
|
165
|
-
await self._propagate(
|
|
166
|
-
ChatUpdate(
|
|
167
|
-
chat_id,
|
|
168
|
-
ChatUpdate.Status.KICKED,
|
|
169
|
-
),
|
|
170
|
-
self,
|
|
171
|
-
)
|
|
172
|
-
if (
|
|
173
|
-
chat_id in self._need_unmute and
|
|
174
|
-
action == GroupCallParticipant.Action.UPDATED and
|
|
175
|
-
not participant.muted_by_admin
|
|
176
|
-
):
|
|
177
|
-
try:
|
|
178
|
-
await update_status(
|
|
179
|
-
chat_id,
|
|
180
|
-
await self._binding.get_state(chat_id),
|
|
181
|
-
)
|
|
182
|
-
except ConnectionNotFound:
|
|
183
|
-
pass
|
|
184
|
-
|
|
185
|
-
if (
|
|
186
|
-
participant.muted_by_admin and
|
|
187
|
-
action != GroupCallParticipant.Action.LEFT
|
|
188
|
-
):
|
|
189
|
-
self._need_unmute.add(chat_id)
|
|
190
|
-
else:
|
|
191
|
-
self._need_unmute.discard(chat_id)
|
|
192
|
-
if not isinstance(update, RawCallUpdate):
|
|
193
|
-
await self._propagate(
|
|
194
|
-
update,
|
|
195
|
-
self,
|
|
196
|
-
)
|
|
197
|
-
|
|
198
|
-
async def clear_call(chat_id) -> bool:
|
|
199
|
-
res = False
|
|
200
|
-
try:
|
|
201
|
-
await self._binding.stop(chat_id)
|
|
202
|
-
res = True
|
|
203
|
-
except ConnectionNotFound:
|
|
204
|
-
pass
|
|
205
|
-
await clear_cache(chat_id)
|
|
206
|
-
return res
|
|
207
|
-
|
|
208
|
-
async def update_status(chat_id: int, state: MediaState):
|
|
209
|
-
try:
|
|
210
|
-
await self._app.set_call_status(
|
|
211
|
-
chat_id,
|
|
212
|
-
state.muted,
|
|
213
|
-
state.video_paused,
|
|
214
|
-
state.video_stopped,
|
|
215
|
-
state.presentation_paused,
|
|
216
|
-
self._cache_user_peer.get(chat_id),
|
|
217
|
-
)
|
|
218
|
-
except Exception as e:
|
|
219
|
-
py_logger.debug(f'SetVideoCallStatus: {e}')
|
|
220
|
-
|
|
221
|
-
async def stream_ended(
|
|
222
|
-
chat_id: int,
|
|
223
|
-
stream_type: StreamType,
|
|
224
|
-
device: StreamDevice,
|
|
225
|
-
):
|
|
226
|
-
await self._propagate(
|
|
227
|
-
StreamEnded(
|
|
228
|
-
chat_id,
|
|
229
|
-
StreamEnded.Type.from_raw(stream_type),
|
|
230
|
-
Device.from_raw(device),
|
|
231
|
-
),
|
|
232
|
-
self,
|
|
233
|
-
)
|
|
234
|
-
|
|
235
|
-
async def emit_sig_data(chat_id: int, data: bytes):
|
|
236
|
-
try:
|
|
237
|
-
await self._app.send_signaling(
|
|
238
|
-
chat_id,
|
|
239
|
-
data,
|
|
240
|
-
)
|
|
241
|
-
except (ConnectionError, ConnectionNotFound):
|
|
242
|
-
pass
|
|
243
|
-
|
|
244
|
-
async def stream_frame(
|
|
245
|
-
chat_id: int,
|
|
246
|
-
mode: StreamMode,
|
|
247
|
-
device: StreamDevice,
|
|
248
|
-
frames: List[RawFrame],
|
|
249
|
-
):
|
|
250
|
-
await self._propagate(
|
|
251
|
-
StreamFrames(
|
|
252
|
-
chat_id,
|
|
253
|
-
Direction.from_raw(mode),
|
|
254
|
-
Device.from_raw(device),
|
|
255
|
-
[
|
|
256
|
-
Frame(
|
|
257
|
-
x.ssrc,
|
|
258
|
-
x.data,
|
|
259
|
-
Frame.Info(
|
|
260
|
-
x.frame_data.absolute_capture_timestamp_ms,
|
|
261
|
-
x.frame_data.width,
|
|
262
|
-
x.frame_data.height,
|
|
263
|
-
x.frame_data.rotation,
|
|
264
|
-
),
|
|
265
|
-
) for x in frames
|
|
266
|
-
],
|
|
267
|
-
),
|
|
268
|
-
self,
|
|
269
|
-
)
|
|
270
|
-
|
|
271
|
-
async def connection_changed(
|
|
272
|
-
chat_id: int,
|
|
273
|
-
net_state: NetworkInfo,
|
|
274
|
-
):
|
|
275
|
-
state = net_state.state
|
|
276
|
-
if state == ConnectionState.CONNECTING:
|
|
277
|
-
return
|
|
278
|
-
if chat_id in self._wait_connect:
|
|
279
|
-
if state == ConnectionState.CONNECTED:
|
|
280
|
-
self._wait_connect[chat_id].set_result(None)
|
|
281
|
-
else:
|
|
282
|
-
self._wait_connect[chat_id].set_exception(
|
|
283
|
-
TelegramServerError(),
|
|
284
|
-
)
|
|
285
|
-
await clear_cache(chat_id)
|
|
286
|
-
|
|
287
|
-
if state != ConnectionState.CONNECTED:
|
|
288
|
-
if chat_id > 0:
|
|
289
|
-
await self._app.discard_call(chat_id)
|
|
290
|
-
await clear_cache(chat_id)
|
|
291
|
-
|
|
292
|
-
async def clear_cache(chat_id: int):
|
|
293
|
-
self._p2p_configs.pop(chat_id, None)
|
|
294
|
-
self._cache_user_peer.pop(chat_id)
|
|
295
|
-
self._need_unmute.discard(chat_id)
|
|
296
|
-
|
|
297
13
|
if not self._is_running:
|
|
298
14
|
self._is_running = True
|
|
299
15
|
self._env_checker.check_environment()
|
|
16
|
+
self._app.add_handler(self._handle_mtproto_updates)
|
|
300
17
|
if not self._app.is_connected:
|
|
301
18
|
await self._app.start()
|
|
302
19
|
|
|
@@ -317,33 +34,33 @@ class Start(Scaffold):
|
|
|
317
34
|
self._binding.on_stream_end(
|
|
318
35
|
lambda chat_id, stream_type, device:
|
|
319
36
|
asyncio.run_coroutine_threadsafe(
|
|
320
|
-
|
|
37
|
+
self._handle_stream_ended(chat_id, stream_type, device),
|
|
321
38
|
self.loop,
|
|
322
39
|
),
|
|
323
40
|
)
|
|
324
41
|
self._binding.on_upgrade(
|
|
325
42
|
lambda chat_id, state:
|
|
326
43
|
asyncio.run_coroutine_threadsafe(
|
|
327
|
-
|
|
44
|
+
self._update_status(chat_id, state),
|
|
328
45
|
self.loop,
|
|
329
46
|
),
|
|
330
47
|
)
|
|
331
48
|
self._binding.on_connection_change(
|
|
332
49
|
lambda chat_id, net_state: asyncio.run_coroutine_threadsafe(
|
|
333
|
-
|
|
50
|
+
self._handle_connection_changed(chat_id, net_state),
|
|
334
51
|
self.loop,
|
|
335
52
|
),
|
|
336
53
|
)
|
|
337
54
|
self._binding.on_signaling(
|
|
338
55
|
lambda chat_id, data: asyncio.run_coroutine_threadsafe(
|
|
339
|
-
|
|
56
|
+
self._emit_sig_data(chat_id, data),
|
|
340
57
|
self.loop,
|
|
341
58
|
),
|
|
342
59
|
)
|
|
343
60
|
self._binding.on_frames(
|
|
344
61
|
lambda chat_id, mode, device, frames:
|
|
345
62
|
asyncio.run_coroutine_threadsafe(
|
|
346
|
-
|
|
63
|
+
self._handle_stream_frame(
|
|
347
64
|
chat_id,
|
|
348
65
|
mode,
|
|
349
66
|
device,
|
|
@@ -352,6 +69,23 @@ class Start(Scaffold):
|
|
|
352
69
|
self.loop,
|
|
353
70
|
),
|
|
354
71
|
)
|
|
72
|
+
self._binding.on_request_broadcast_part(
|
|
73
|
+
lambda chat_id, part_request:
|
|
74
|
+
asyncio.run_coroutine_threadsafe(
|
|
75
|
+
self._request_broadcast_part(
|
|
76
|
+
chat_id,
|
|
77
|
+
part_request,
|
|
78
|
+
),
|
|
79
|
+
self.loop,
|
|
80
|
+
),
|
|
81
|
+
)
|
|
82
|
+
self._binding.on_request_broadcast_timestamp(
|
|
83
|
+
lambda chat_id:
|
|
84
|
+
asyncio.run_coroutine_threadsafe(
|
|
85
|
+
self._request_broadcast_timestamp(chat_id),
|
|
86
|
+
self.loop,
|
|
87
|
+
),
|
|
88
|
+
)
|
|
355
89
|
await PyTgCallsSession().start()
|
|
356
90
|
else:
|
|
357
91
|
raise PyTgCallsAlreadyRunning()
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import random
|
|
2
2
|
from typing import Any
|
|
3
|
-
from typing import Callable
|
|
4
3
|
from typing import List
|
|
5
4
|
from typing import Optional
|
|
6
5
|
|
|
6
|
+
from ntgcalls import MediaSegmentQuality
|
|
7
7
|
from ntgcalls import Protocol
|
|
8
8
|
from ntgcalls import RTCServer
|
|
9
9
|
from ntgcalls import SsrcGroup
|
|
@@ -25,7 +25,7 @@ class BridgedClient(HandlersHolder):
|
|
|
25
25
|
chat_id: int,
|
|
26
26
|
json_join: str,
|
|
27
27
|
invite_hash: str,
|
|
28
|
-
|
|
28
|
+
video_stopped: bool,
|
|
29
29
|
join_as: Any,
|
|
30
30
|
):
|
|
31
31
|
pass
|
|
@@ -48,6 +48,7 @@ class BridgedClient(HandlersHolder):
|
|
|
48
48
|
user_id: int,
|
|
49
49
|
g_a_hash: bytes,
|
|
50
50
|
protocol: Protocol,
|
|
51
|
+
has_video: bool,
|
|
51
52
|
):
|
|
52
53
|
pass
|
|
53
54
|
|
|
@@ -108,6 +109,22 @@ class BridgedClient(HandlersHolder):
|
|
|
108
109
|
):
|
|
109
110
|
pass
|
|
110
111
|
|
|
112
|
+
async def download_stream(
|
|
113
|
+
self,
|
|
114
|
+
chat_id: int,
|
|
115
|
+
timestamp: int,
|
|
116
|
+
limit: int,
|
|
117
|
+
video_channel: Optional[int],
|
|
118
|
+
video_quality: MediaSegmentQuality,
|
|
119
|
+
):
|
|
120
|
+
pass
|
|
121
|
+
|
|
122
|
+
async def get_stream_timestamp(
|
|
123
|
+
self,
|
|
124
|
+
chat_id: int,
|
|
125
|
+
):
|
|
126
|
+
pass
|
|
127
|
+
|
|
111
128
|
async def set_call_status(
|
|
112
129
|
self,
|
|
113
130
|
chat_id: int,
|
|
@@ -232,6 +249,17 @@ class BridgedClient(HandlersHolder):
|
|
|
232
249
|
for server in servers
|
|
233
250
|
]
|
|
234
251
|
|
|
252
|
+
@staticmethod
|
|
253
|
+
def parse_quality(quality: MediaSegmentQuality) -> Optional[int]:
|
|
254
|
+
if quality == MediaSegmentQuality.THUMBNAIL:
|
|
255
|
+
return 0
|
|
256
|
+
elif quality == MediaSegmentQuality.MEDIUM:
|
|
257
|
+
return 1
|
|
258
|
+
elif quality == MediaSegmentQuality.FULL:
|
|
259
|
+
return 2
|
|
260
|
+
else:
|
|
261
|
+
return None
|
|
262
|
+
|
|
235
263
|
@staticmethod
|
|
236
264
|
def rnd_id() -> int:
|
|
237
265
|
return random.randint(0, 0x7FFFFFFF - 1)
|
|
@@ -239,12 +267,6 @@ class BridgedClient(HandlersHolder):
|
|
|
239
267
|
async def get_dhc(self):
|
|
240
268
|
pass
|
|
241
269
|
|
|
242
|
-
def on_update(self) -> Callable:
|
|
243
|
-
def decorator(func: Callable) -> Callable:
|
|
244
|
-
return self.add_handler(func)
|
|
245
|
-
|
|
246
|
-
return decorator
|
|
247
|
-
|
|
248
270
|
async def get_id(self):
|
|
249
271
|
pass
|
|
250
272
|
|
|
@@ -6,6 +6,7 @@ from typing import Union
|
|
|
6
6
|
|
|
7
7
|
from hydrogram import Client
|
|
8
8
|
from hydrogram import ContinuePropagation
|
|
9
|
+
from hydrogram.errors import FloodWait
|
|
9
10
|
from hydrogram.raw.base import InputPeer
|
|
10
11
|
from hydrogram.raw.base import InputUser
|
|
11
12
|
from hydrogram.raw.functions.channels import GetFullChannel
|
|
@@ -17,6 +18,7 @@ from hydrogram.raw.functions.phone import CreateGroupCall
|
|
|
17
18
|
from hydrogram.raw.functions.phone import DiscardCall
|
|
18
19
|
from hydrogram.raw.functions.phone import EditGroupCallParticipant
|
|
19
20
|
from hydrogram.raw.functions.phone import GetGroupCall
|
|
21
|
+
from hydrogram.raw.functions.phone import GetGroupCallStreamChannels
|
|
20
22
|
from hydrogram.raw.functions.phone import GetGroupParticipants
|
|
21
23
|
from hydrogram.raw.functions.phone import JoinGroupCall
|
|
22
24
|
from hydrogram.raw.functions.phone import JoinGroupCallPresentation
|
|
@@ -24,6 +26,7 @@ from hydrogram.raw.functions.phone import LeaveGroupCall
|
|
|
24
26
|
from hydrogram.raw.functions.phone import LeaveGroupCallPresentation
|
|
25
27
|
from hydrogram.raw.functions.phone import RequestCall
|
|
26
28
|
from hydrogram.raw.functions.phone import SendSignalingData
|
|
29
|
+
from hydrogram.raw.functions.upload import GetFile
|
|
27
30
|
from hydrogram.raw.types import Channel
|
|
28
31
|
from hydrogram.raw.types import ChannelForbidden
|
|
29
32
|
from hydrogram.raw.types import Chat
|
|
@@ -33,6 +36,7 @@ from hydrogram.raw.types import GroupCall
|
|
|
33
36
|
from hydrogram.raw.types import GroupCallDiscarded
|
|
34
37
|
from hydrogram.raw.types import InputChannel
|
|
35
38
|
from hydrogram.raw.types import InputGroupCall
|
|
39
|
+
from hydrogram.raw.types import InputGroupCallStream
|
|
36
40
|
from hydrogram.raw.types import InputPeerChannel
|
|
37
41
|
from hydrogram.raw.types import InputPhoneCall
|
|
38
42
|
from hydrogram.raw.types import MessageActionChatDeleteUser
|
|
@@ -42,6 +46,7 @@ from hydrogram.raw.types import PeerChat
|
|
|
42
46
|
from hydrogram.raw.types import PhoneCall
|
|
43
47
|
from hydrogram.raw.types import PhoneCallAccepted
|
|
44
48
|
from hydrogram.raw.types import PhoneCallDiscarded
|
|
49
|
+
from hydrogram.raw.types import PhoneCallDiscardReasonBusy
|
|
45
50
|
from hydrogram.raw.types import PhoneCallDiscardReasonHangup
|
|
46
51
|
from hydrogram.raw.types import PhoneCallDiscardReasonMissed
|
|
47
52
|
from hydrogram.raw.types import PhoneCallProtocol
|
|
@@ -57,6 +62,7 @@ from hydrogram.raw.types import UpdatePhoneCall
|
|
|
57
62
|
from hydrogram.raw.types import UpdatePhoneCallSignalingData
|
|
58
63
|
from hydrogram.raw.types import Updates
|
|
59
64
|
from hydrogram.raw.types.messages import DhConfig
|
|
65
|
+
from ntgcalls import MediaSegmentQuality
|
|
60
66
|
from ntgcalls import Protocol
|
|
61
67
|
|
|
62
68
|
from ..types import CallProtocol
|
|
@@ -129,10 +135,16 @@ class HydrogramClient(BridgedClient):
|
|
|
129
135
|
self._cache.drop_phone_call(
|
|
130
136
|
user_id,
|
|
131
137
|
)
|
|
138
|
+
reason = ChatUpdate.Status.DISCARDED_CALL
|
|
139
|
+
if isinstance(
|
|
140
|
+
update.phone_call.reason,
|
|
141
|
+
PhoneCallDiscardReasonBusy,
|
|
142
|
+
):
|
|
143
|
+
reason |= ChatUpdate.Status.BUSY_CALL
|
|
132
144
|
await self._propagate(
|
|
133
145
|
ChatUpdate(
|
|
134
146
|
user_id,
|
|
135
|
-
|
|
147
|
+
reason,
|
|
136
148
|
),
|
|
137
149
|
)
|
|
138
150
|
if isinstance(update.phone_call, PhoneCallRequested):
|
|
@@ -388,7 +400,7 @@ class HydrogramClient(BridgedClient):
|
|
|
388
400
|
chat_id: int,
|
|
389
401
|
json_join: str,
|
|
390
402
|
invite_hash: str,
|
|
391
|
-
|
|
403
|
+
video_stopped: bool,
|
|
392
404
|
join_as: InputPeer,
|
|
393
405
|
) -> str:
|
|
394
406
|
chat_call = await self._cache.get_full_chat(chat_id)
|
|
@@ -399,7 +411,7 @@ class HydrogramClient(BridgedClient):
|
|
|
399
411
|
params=DataJSON(data=json_join),
|
|
400
412
|
muted=False,
|
|
401
413
|
join_as=join_as,
|
|
402
|
-
video_stopped=
|
|
414
|
+
video_stopped=video_stopped,
|
|
403
415
|
invite_hash=invite_hash,
|
|
404
416
|
),
|
|
405
417
|
)
|
|
@@ -455,14 +467,22 @@ class HydrogramClient(BridgedClient):
|
|
|
455
467
|
user_id: int,
|
|
456
468
|
g_a_hash: bytes,
|
|
457
469
|
protocol: Protocol,
|
|
470
|
+
has_video: bool,
|
|
458
471
|
):
|
|
459
|
-
await self._app.invoke(
|
|
472
|
+
update = await self._app.invoke(
|
|
460
473
|
RequestCall(
|
|
461
474
|
user_id=await self.resolve_peer(user_id),
|
|
462
475
|
random_id=self.rnd_id(),
|
|
463
476
|
g_a_hash=g_a_hash,
|
|
464
477
|
protocol=self.parse_protocol(protocol),
|
|
465
|
-
video=
|
|
478
|
+
video=has_video,
|
|
479
|
+
),
|
|
480
|
+
)
|
|
481
|
+
self._cache.set_phone_call(
|
|
482
|
+
user_id,
|
|
483
|
+
InputPhoneCall(
|
|
484
|
+
id=update.phone_call.id,
|
|
485
|
+
access_hash=update.phone_call.access_hash,
|
|
466
486
|
),
|
|
467
487
|
)
|
|
468
488
|
|
|
@@ -597,6 +617,58 @@ class HydrogramClient(BridgedClient):
|
|
|
597
617
|
),
|
|
598
618
|
)
|
|
599
619
|
|
|
620
|
+
async def download_stream(
|
|
621
|
+
self,
|
|
622
|
+
chat_id: int,
|
|
623
|
+
timestamp: int,
|
|
624
|
+
limit: int,
|
|
625
|
+
video_channel: Optional[int],
|
|
626
|
+
video_quality: MediaSegmentQuality,
|
|
627
|
+
):
|
|
628
|
+
chat_call = await self._cache.get_full_chat(chat_id)
|
|
629
|
+
if chat_call is not None:
|
|
630
|
+
try:
|
|
631
|
+
return (
|
|
632
|
+
await self._app.invoke(
|
|
633
|
+
GetFile(
|
|
634
|
+
location=InputGroupCallStream(
|
|
635
|
+
call=chat_call,
|
|
636
|
+
time_ms=timestamp,
|
|
637
|
+
scale=0,
|
|
638
|
+
video_channel=video_channel,
|
|
639
|
+
video_quality=BridgedClient.parse_quality(
|
|
640
|
+
video_quality,
|
|
641
|
+
),
|
|
642
|
+
),
|
|
643
|
+
offset=0,
|
|
644
|
+
limit=limit,
|
|
645
|
+
),
|
|
646
|
+
sleep_threshold=0,
|
|
647
|
+
)
|
|
648
|
+
).bytes
|
|
649
|
+
except FloodWait:
|
|
650
|
+
pass
|
|
651
|
+
return None
|
|
652
|
+
|
|
653
|
+
async def get_stream_timestamp(
|
|
654
|
+
self,
|
|
655
|
+
chat_id: int,
|
|
656
|
+
):
|
|
657
|
+
chat_call = await self._cache.get_full_chat(chat_id)
|
|
658
|
+
if chat_call is not None:
|
|
659
|
+
# noinspection PyBroadException
|
|
660
|
+
channels = (
|
|
661
|
+
await self._app.invoke(
|
|
662
|
+
GetGroupCallStreamChannels(
|
|
663
|
+
call=chat_call,
|
|
664
|
+
),
|
|
665
|
+
)
|
|
666
|
+
).channels
|
|
667
|
+
if len(channels) > 0:
|
|
668
|
+
return channels[0].last_timestamp_ms
|
|
669
|
+
|
|
670
|
+
return 0
|
|
671
|
+
|
|
600
672
|
async def set_call_status(
|
|
601
673
|
self,
|
|
602
674
|
chat_id: int,
|
|
@@ -3,6 +3,7 @@ from typing import Callable
|
|
|
3
3
|
from typing import List
|
|
4
4
|
from typing import Optional
|
|
5
5
|
|
|
6
|
+
from ntgcalls import MediaSegmentQuality
|
|
6
7
|
from ntgcalls import Protocol
|
|
7
8
|
|
|
8
9
|
from ..exceptions import InvalidMTProtoClient
|
|
@@ -55,7 +56,7 @@ class MtProtoClient:
|
|
|
55
56
|
chat_id: int,
|
|
56
57
|
json_join: str,
|
|
57
58
|
invite_hash: str,
|
|
58
|
-
|
|
59
|
+
video_stopped: bool,
|
|
59
60
|
join_as: Any,
|
|
60
61
|
) -> str:
|
|
61
62
|
if self._bind_client is not None:
|
|
@@ -63,7 +64,7 @@ class MtProtoClient:
|
|
|
63
64
|
chat_id,
|
|
64
65
|
json_join,
|
|
65
66
|
invite_hash,
|
|
66
|
-
|
|
67
|
+
video_stopped,
|
|
67
68
|
join_as,
|
|
68
69
|
)
|
|
69
70
|
else:
|
|
@@ -98,12 +99,14 @@ class MtProtoClient:
|
|
|
98
99
|
user_id: int,
|
|
99
100
|
g_a_hash: bytes,
|
|
100
101
|
protocol: Protocol,
|
|
102
|
+
has_video: bool,
|
|
101
103
|
):
|
|
102
104
|
if self._bind_client is not None:
|
|
103
105
|
return await self._bind_client.request_call(
|
|
104
106
|
user_id,
|
|
105
107
|
g_a_hash,
|
|
106
108
|
protocol,
|
|
109
|
+
has_video,
|
|
107
110
|
)
|
|
108
111
|
else:
|
|
109
112
|
raise InvalidMTProtoClient()
|
|
@@ -209,6 +212,36 @@ class MtProtoClient:
|
|
|
209
212
|
else:
|
|
210
213
|
raise InvalidMTProtoClient()
|
|
211
214
|
|
|
215
|
+
async def download_stream(
|
|
216
|
+
self,
|
|
217
|
+
chat_id: int,
|
|
218
|
+
timestamp: int,
|
|
219
|
+
limit: int,
|
|
220
|
+
video_channel: Optional[int],
|
|
221
|
+
video_quality: MediaSegmentQuality,
|
|
222
|
+
):
|
|
223
|
+
if self._bind_client is not None:
|
|
224
|
+
return await self._bind_client.download_stream(
|
|
225
|
+
chat_id,
|
|
226
|
+
timestamp,
|
|
227
|
+
limit,
|
|
228
|
+
video_channel,
|
|
229
|
+
video_quality,
|
|
230
|
+
)
|
|
231
|
+
else:
|
|
232
|
+
raise InvalidMTProtoClient()
|
|
233
|
+
|
|
234
|
+
async def get_stream_timestamp(
|
|
235
|
+
self,
|
|
236
|
+
chat_id: int,
|
|
237
|
+
):
|
|
238
|
+
if self._bind_client is not None:
|
|
239
|
+
return await self._bind_client.get_stream_timestamp(
|
|
240
|
+
chat_id,
|
|
241
|
+
)
|
|
242
|
+
else:
|
|
243
|
+
raise InvalidMTProtoClient()
|
|
244
|
+
|
|
212
245
|
async def set_call_status(
|
|
213
246
|
self,
|
|
214
247
|
chat_id: int,
|
|
@@ -279,7 +312,7 @@ class MtProtoClient:
|
|
|
279
312
|
else:
|
|
280
313
|
raise InvalidMTProtoClient()
|
|
281
314
|
|
|
282
|
-
def
|
|
315
|
+
def add_handler(self, func: Callable):
|
|
283
316
|
if self._bind_client is not None:
|
|
284
|
-
return self._bind_client.
|
|
317
|
+
return self._bind_client.add_handler(func)
|
|
285
318
|
raise InvalidMTProtoClient()
|