py-tgcalls 2.2.0rc3__py3-none-any.whl → 2.2.2__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.0rc3
3
+ Version: 2.2.2
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<3.0.0,>=2.0.0rc5
23
+ Requires-Dist: ntgcalls<3.0.0,>=2.0.2
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.0rc3.dist-info/licenses/LICENSE,sha256=46mU2C5kSwOnkqkw9XQAJlhBL2JAf1_uCD8lVcXyMRg,7652
1
+ py_tgcalls-2.2.2.dist-info/licenses/LICENSE,sha256=46mU2C5kSwOnkqkw9XQAJlhBL2JAf1_uCD8lVcXyMRg,7652
2
2
  pytgcalls/__init__.py,sha256=qbfwN7rYwIdCegMOzdcbvwazeNjDzgmowgcqLFNqKIM,308
3
- pytgcalls/__version__.py,sha256=oggK25XEBVcFHD0dtA1CXYnNjf3ynxvgKwJFaniQ3Is,25
3
+ pytgcalls/__version__.py,sha256=jsJ9CNIuUt8dDFB4i0PiBf07nzBU0RtG1CVRQ7TdoQ0,22
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
@@ -30,19 +30,19 @@ pytgcalls/methods/__init__.py,sha256=KKnG3uI_3oKKBByQ96kHJiabjxk2J6YLs4HDfOKvQ0A
30
30
  pytgcalls/methods/calls/__init__.py,sha256=xg4DZZClEnxwaj-DAq3e8gSR-g-MiYBdUEBth64lSXA,214
31
31
  pytgcalls/methods/calls/change_volume_call.py,sha256=xMUszg44Gs1RgTXGCwcWEESnwu3XVkW8Kx9HGLDGSEo,842
32
32
  pytgcalls/methods/calls/get_participants.py,sha256=HDEMwZwNZM7KSb76P5XVH46qNONvBEVg4x_e-rgJscI,716
33
- pytgcalls/methods/calls/leave_call.py,sha256=Q7v0xT_CjbMIUBrugtvKbZqbSgmw5laMQYVhrMcaD1U,1475
33
+ pytgcalls/methods/calls/leave_call.py,sha256=XiaKrnSgaItbAIUpdGXCo1HgXY7V7FcDjRsi5OwNweY,1609
34
34
  pytgcalls/methods/decorators/__init__.py,sha256=TCGaEVZnHjtOwv-3PNfaCVm0kyFhJApUPUNntt6MwyM,78
35
35
  pytgcalls/methods/decorators/on_update.py,sha256=ZTL4YcQk0N4Ru56a5WItUvkSN5SAqr6_RDZvXmZMIHs,316
36
36
  pytgcalls/methods/internal/__init__.py,sha256=fcgIxUJKT6QJD30ltnOfzKsLhzTTTklD2qxKlwCvyv0,1057
37
- pytgcalls/methods/internal/clear_cache.py,sha256=qgG-oubmdtnefnXSiHSPHyBTyXypiLDC7G8l3FzjNw8,236
38
- pytgcalls/methods/internal/clear_call.py,sha256=yElvn3i6_j14Q7n9kcLSl-muYAGcaIZaEFHdp6MrN0g,549
39
- pytgcalls/methods/internal/connect_call.py,sha256=Py-sB_ImH839tK9GxNUCJDJUNizsZtlm5m6kuiUX3lE,5739
37
+ pytgcalls/methods/internal/clear_cache.py,sha256=IbcDOGiBJbmqOnGJWkIe8c6P_Y6e0YPkui4Hg4PAZlA,334
38
+ pytgcalls/methods/internal/clear_call.py,sha256=u84SdZb5z6lGRxf7dbrcAcf-xObGVgMsHqSmvdzoAbQ,487
39
+ pytgcalls/methods/internal/connect_call.py,sha256=O5BqayOFJInC0eWdeofAimyY6JONpe6Iw4TYtSSMf3c,5800
40
40
  pytgcalls/methods/internal/emit_sig_data.py,sha256=ucIsknhJHB-0x7lcymXvwQ647AJQ852zH2W6MdlC3ws,216
41
41
  pytgcalls/methods/internal/handle_connection_changed.py,sha256=_1u3J6_Pjl1Gs1u_WkhG2FAUl_hxlHUiflaMkKkJDsc,866
42
- pytgcalls/methods/internal/handle_mtproto_updates.py,sha256=Kt5ioQ-qYHNIwzEwLLsNJBG-INA4vk21t01zCzpy7k0,7722
42
+ pytgcalls/methods/internal/handle_mtproto_updates.py,sha256=mdj8N4GXX1J650dE7OThSAVCmtB4wwByqd95P6vmbU8,7461
43
43
  pytgcalls/methods/internal/handle_stream_ended.py,sha256=DllD1ZfGQbwVh-S0neocwnN-8lQtwwyrzWl9teSrZbY,561
44
44
  pytgcalls/methods/internal/handle_stream_frame.py,sha256=_FA782qlOT3tUqnySm7RBpjbgfEEzt1oEBDm-iADsbQ,1145
45
- pytgcalls/methods/internal/join_presentation.py,sha256=dLB2kolIIm2KQH8yBQbEPJi_y1_cVLWpE52_Rwjn_KE,2178
45
+ pytgcalls/methods/internal/join_presentation.py,sha256=sXhmzbLWwVFi3gFl1rv0OgJsRJBrvjzRcl60N6ydNW8,2184
46
46
  pytgcalls/methods/internal/log_retries.py,sha256=6nD9J3350t82I0PKzK1pVx3ZaCBHATReiXYMs3PuVhQ,342
47
47
  pytgcalls/methods/internal/request_broadcast_part.py,sha256=oh-SqMCuQQErpF9Gd8ubPDoZeaz4bKw0dK--JrjXK9s,1313
48
48
  pytgcalls/methods/internal/request_broadcast_timestamp.py,sha256=yEEmtyLR-EsEO1aXMcRAD34LRH63gY6ZQvn_Sw9quLk,642
@@ -52,7 +52,7 @@ pytgcalls/methods/internal/update_status.py,sha256=6zH7oMM_qPE-88mdC5CBhT07gPvay
52
52
  pytgcalls/methods/stream/__init__.py,sha256=mAcOih0-NT6T_Gspej6mySpJNPuEe46sUwgKV3vSvYM,336
53
53
  pytgcalls/methods/stream/mute.py,sha256=ZrZS_EeNUeUxb6UgbdhwXUdRX826u-qSjH5a6sg7LsE,557
54
54
  pytgcalls/methods/stream/pause.py,sha256=-kNvWQuv5VlssNcL-M6rkT5TKFmXlbOzJrDny95qsUc,560
55
- pytgcalls/methods/stream/play.py,sha256=fTZS-XHF50Wo9BNOZ373n4YhGqRuu7uZR-GJEACjqYY,2935
55
+ pytgcalls/methods/stream/play.py,sha256=XJB7o4Y5ImY1Qh_tHCkT70cUKOr9JlinWtVKwE8W5Rw,2951
56
56
  pytgcalls/methods/stream/record.py,sha256=geYSVtSbp0yRIR1Nmj-L1s-6nqQAh0x0IcA1OuFvuyA,1306
57
57
  pytgcalls/methods/stream/resume.py,sha256=AUHU3AtpXO2rtp2V1EKSC_KAWTk2KHMiHaqHluYy31M,563
58
58
  pytgcalls/methods/stream/send_frame.py,sha256=Kj9R8OqUM-g7pt-FiWP-US7sCFkH5ciPr9S8v-WPtLg,1062
@@ -68,22 +68,22 @@ pytgcalls/methods/utilities/ping.py,sha256=hhIMSHk2BzMB-IKpwLdZFVrsEvGm2ftJwKLs1
68
68
  pytgcalls/methods/utilities/resolve_chat_id.py,sha256=92x2LHbUlnJMm-kS3fXOYmzYpY2TZbqtQD2rw3eBXDY,382
69
69
  pytgcalls/methods/utilities/run.py,sha256=cnYQd2xB5Cr_WS0Q2cXJZPGiN6JOCULzj1r4xXVyrlg,152
70
70
  pytgcalls/methods/utilities/start.py,sha256=mn0kQZhTUuc-9CCJDbFIVsEtJ8kfnfZOGbVC505qVRM,3232
71
- pytgcalls/methods/utilities/stream_params.py,sha256=fOSloo1A7WTxaZEtOiPXjdexMeoJjq8CZtfHuIFX7Ns,3325
71
+ pytgcalls/methods/utilities/stream_params.py,sha256=PUnctLhdCeBAg52v95vpxj-42Le5XzbnRtcfm71FDBM,3341
72
72
  pytgcalls/mtproto/__init__.py,sha256=X4zvzFG7km7qHyE0fdvA550WcOVO_xl_p__gvIfDGmw,130
73
- pytgcalls/mtproto/bridged_client.py,sha256=gG69X4tbFKwPiMdBYMEYSSqqB6UcTp1B-HpqqGvOqR4,7005
74
- pytgcalls/mtproto/client_cache.py,sha256=73o111KTSxGSw1OrG8m4QBnz7VSzETiTLnhEt49IXw0,6595
75
- pytgcalls/mtproto/hydrogram_client.py,sha256=XKHsyWXJTe5jzo0m-hLxvYaJ_65TOA7W0i4vS3BtxcI,28591
76
- pytgcalls/mtproto/mtproto_client.py,sha256=95A13HsNJfpNslsY_gWiqYXj_Vvo-pl3rVrsJGxftgg,8517
77
- pytgcalls/mtproto/pyrogram_client.py,sha256=5KwGuHC21DjUCe_PVQzLyfxrBuxd2h_xlFPDfX813NU,28588
78
- pytgcalls/mtproto/telethon_client.py,sha256=6dzPXnfy4_fXjNWjRbH0RrWyD2LTQM3WREfFcBDXwEo,26711
73
+ pytgcalls/mtproto/bridged_client.py,sha256=Qb9-9FrUdh3g_sx-JOcvkzv3kJtS_pz2Qaos85DgVPI,8280
74
+ pytgcalls/mtproto/client_cache.py,sha256=5unu8sjLRaIKoEgJGaIRjhA4P3U2lFpfakTKFxKLzAM,5591
75
+ pytgcalls/mtproto/hydrogram_client.py,sha256=P2YpeIBYXzBHWLXyk9pGiO_TsIBuzDQ7LvorGZFFM-o,28974
76
+ pytgcalls/mtproto/mtproto_client.py,sha256=9SIG3DNeICIXvggyVE8DQWtToPtxV0t9ZKr6dEMnEMg,8783
77
+ pytgcalls/mtproto/pyrogram_client.py,sha256=CI9uPMdwwE3UGSNfenqIKUl-UD_caVZpCzYR1PB4drY,28970
78
+ pytgcalls/mtproto/telethon_client.py,sha256=w9PuqE_gsDKFYVpF5o_ULMSsYf5sAK8VgImVxbNz2Bc,27106
79
79
  pytgcalls/types/__init__.py,sha256=GlgBBXAwbNopXSeTTmiXktrEJhhN_rMBtuAllTBbN3k,1189
80
80
  pytgcalls/types/browsers.py,sha256=47Kr5q96n4Q4WvVhA6IUlS2egEcA9GRLlDeFcQYyc9M,9545
81
- pytgcalls/types/cache.py,sha256=FfsOcmYnsBGPlJoTPIXXYcUSpGE3rhx6cjIH77hyUL0,1059
82
- pytgcalls/types/dict.py,sha256=lAo9hu4VlVJa9S7P8Y81BYmKtvz0rH7hwpGcH3ynHUw,78
83
- pytgcalls/types/flag.py,sha256=dQPcQmTgTQzcOLTvGe8t_e9mY4qsVnCZFrrTk17b2Xw,132
84
- pytgcalls/types/list.py,sha256=UjP_XxxMpPkLlu6yEy29JYqOM5VITFwwJcDm0wZni1c,78
85
- pytgcalls/types/participant_list.py,sha256=60bDaLQ484qgxk2Mm4cxCtlzqVhUTS7itdfQmuZk4PA,949
86
- pytgcalls/types/py_object.py,sha256=__GNXgffGK4jhdF4QPqIknd0k2myObJf_7fW3pNGpbU,836
81
+ pytgcalls/types/cache.py,sha256=nJh6B7xnvAiLh0mDJYS9sYhnRvj0BqxrQBLXs4WEUMs,1235
82
+ pytgcalls/types/dict.py,sha256=-R1v5-v5WzhquPN25Bfw9Ow6q2lRRgpsq_FlsOawCUw,78
83
+ pytgcalls/types/flag.py,sha256=MeWDKkUAZa97fUPg5Ni5Rf4pDgiZ_OSRZPseEY7d3rw,104
84
+ pytgcalls/types/list.py,sha256=rGzD9LWAI2hUX71OL_pqZn08YHEbZ-AZ6RTjXPc9wJA,78
85
+ pytgcalls/types/participant_list.py,sha256=wG7a8dvcmcUkagmSo-g4thGMBrqMdEU0fA_zD4tCk2w,931
86
+ pytgcalls/types/py_object.py,sha256=jisGKqJINuzAjkIkLIps61uYM0eFSmC9TtgxGRDAMZI,934
87
87
  pytgcalls/types/update.py,sha256=wPCzWLhrsScZ3ksRTyt8IuDaaG5YI-ItG_Yw-OqzK2Y,157
88
88
  pytgcalls/types/user_agent.py,sha256=sSfeGqUe0v0wqBgdVszNFK0iOC_0Tdyto9CglBXlY4U,1086
89
89
  pytgcalls/types/calls/__init__.py,sha256=lgFG89_NGhWHOQqhfw3adPaQZMzsMKI1f-MyZW3TBBU,478
@@ -94,10 +94,10 @@ pytgcalls/types/calls/call_protocol.py,sha256=OVIQs1VgdY-DWbZbNr41hjLA4pGQvHx8Rg
94
94
  pytgcalls/types/calls/call_sources.py,sha256=sBhumPgEaN8uAKjBwb1Zf_Ag0qrceti2mURXqMhBusg,107
95
95
  pytgcalls/types/calls/group_call_config.py,sha256=auKH-hZJWj8PhTkyeQ_VK2z9NpNvNC7Scl_IhEUMnQM,353
96
96
  pytgcalls/types/calls/pending_connection.py,sha256=qRRmutInj70rtzbThM7CNznFhEPqTNZQc6LUWnqpZ9I,432
97
- pytgcalls/types/calls/raw_call_update.py,sha256=hpNw6HrTW8Z36Lh2HinS-wzprryRtsIxyIFbIfjGgeI,795
97
+ pytgcalls/types/calls/raw_call_update.py,sha256=pxcNY-spHXdATwdYu73HQuK2xUeoZ44naD10uoG_PtM,797
98
98
  pytgcalls/types/chats/__init__.py,sha256=v8pUp_vbr2kQpyHtAQc80N-YqzmXHe9SbllUsa6njkU,261
99
99
  pytgcalls/types/chats/chat_update.py,sha256=lzrqNDPv4a_yXpKIfUnhocXqZyIy8XgZladOQTYrrYA,730
100
- pytgcalls/types/chats/group_call_participant.py,sha256=alFtJcgK7aGJ6rl4QSmHD2pU3IwDVs0ajV6qInOrf0Q,1481
100
+ pytgcalls/types/chats/group_call_participant.py,sha256=aK7nvI0Jy93g9A_FVeFzk1BZFXzhFry06h8xR4VpqXY,1507
101
101
  pytgcalls/types/chats/updated_group_call_participant.py,sha256=bZQsZAsMv_i4k8DJW3phyuHqpa9Dp6IbheHCvj3M630,365
102
102
  pytgcalls/types/raw/__init__.py,sha256=ROHsKFeUMUtlFbx2rhfrdB-TuVm0zBuvNo29Ccn5614,308
103
103
  pytgcalls/types/raw/audio_parameters.py,sha256=1DsBPwdn_Ukd2Tbkb3whP_ILo9xJY_3XNNmbO4_NO9Q,449
@@ -115,8 +115,8 @@ pytgcalls/types/stream/media_stream.py,sha256=zcRVpNXfL8mhg-SEfAi-f0lDUZMyNYLeto
115
115
  pytgcalls/types/stream/record_stream.py,sha256=f4VQ6MY8HtOxt7vz0hWBFmbbAIvTRHpAIU2nmj0TF6Y,3197
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
- pytgcalls/types/stream/video_quality.py,sha256=HBfWq005kh-D19MaVE9VzVdnODzrXf4IJUimCfslfiU,231
119
- py_tgcalls-2.2.0rc3.dist-info/METADATA,sha256=xjnla12jR6aBXMOTcFV1k7gNUuQO7Lzdus2L7AX3EbM,5286
120
- py_tgcalls-2.2.0rc3.dist-info/WHEEL,sha256=DnLRTWE75wApRYVsjgc6wsVswC54sMSJhAEd4xhDpBk,91
121
- py_tgcalls-2.2.0rc3.dist-info/top_level.txt,sha256=IUDUwn0KkcbUYZbCe9R5AUb2Ob-lmllNUGQqyeXXd8A,10
122
- py_tgcalls-2.2.0rc3.dist-info/RECORD,,
118
+ pytgcalls/types/stream/video_quality.py,sha256=eMCBFPwh5meX3UVEaozcGlwmgaujfpiTa3vBVSBBP_8,275
119
+ py_tgcalls-2.2.2.dist-info/METADATA,sha256=7YhuQ_QZCvKS6g0emehCYG8Tb9RllKZrkZpNYjzVJEY,5280
120
+ py_tgcalls-2.2.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
121
+ py_tgcalls-2.2.2.dist-info/top_level.txt,sha256=IUDUwn0KkcbUYZbCe9R5AUb2Ob-lmllNUGQqyeXXd8A,10
122
+ py_tgcalls-2.2.2.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.4.0)
2
+ Generator: setuptools (80.9.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.0rc3'
1
+ __version__ = '2.2.2'
@@ -17,6 +17,7 @@ class LeaveCall(Scaffold):
17
17
  async def leave_call(
18
18
  self,
19
19
  chat_id: Union[int, str],
20
+ close: bool = False,
20
21
  ):
21
22
  chat_id = await self.resolve_chat_id(chat_id)
22
23
  is_p2p_waiting = (
@@ -48,3 +49,6 @@ class LeaveCall(Scaffold):
48
49
  self._need_unmute.discard(chat_id)
49
50
  self._presentations.discard(chat_id)
50
51
  self._call_sources.pop(chat_id, None)
52
+
53
+ if chat_id < 0 and close: # type: ignore
54
+ await self._app.close_voice_chat(chat_id)
@@ -6,3 +6,5 @@ class ClearCache(Scaffold):
6
6
  self._p2p_configs.pop(chat_id, None)
7
7
  self._cache_user_peer.pop(chat_id)
8
8
  self._need_unmute.discard(chat_id)
9
+ self._presentations.discard(chat_id)
10
+ self._pending_connections.pop(chat_id, None)
@@ -6,15 +6,12 @@ from ...scaffold import Scaffold
6
6
 
7
7
  class ClearCall(Scaffold):
8
8
  async def _clear_call(self, chat_id: int):
9
- res = False
10
9
  if chat_id in self._wait_connect:
11
10
  self._wait_connect[chat_id].set_exception(
12
11
  TelegramServerError(),
13
12
  )
14
13
  try:
15
14
  await self._binding.stop(chat_id)
16
- res = True
17
15
  except ConnectionNotFound:
18
16
  pass
19
17
  self._clear_cache(chat_id)
20
- return res
@@ -140,3 +140,4 @@ class ConnectCall(Scaffold):
140
140
  raise
141
141
  finally:
142
142
  self._wait_connect.pop(chat_id, None)
143
+ self._pending_connections.pop(chat_id, None)
@@ -1,3 +1,5 @@
1
+ import logging
2
+
1
3
  from ntgcalls import ConnectionError
2
4
  from ntgcalls import ConnectionNotFound
3
5
 
@@ -13,9 +15,12 @@ from ...types import RawCallUpdate
13
15
  from ...types import Update
14
16
  from ...types import UpdatedGroupCallParticipant
15
17
 
18
+ py_logger = logging.getLogger('pytgcalls')
19
+
16
20
 
17
21
  class HandleMTProtoUpdates(Scaffold):
18
22
  async def _handle_mtproto_updates(self, update: Update):
23
+ py_logger.debug('Received: %s', update)
19
24
  chat_id = update.chat_id
20
25
  if update.chat_id in self._p2p_configs:
21
26
  p2p_config = self._p2p_configs[chat_id]
@@ -28,7 +33,6 @@ class HandleMTProtoUpdates(Scaffold):
28
33
  if isinstance(update, ChatUpdate) and \
29
34
  p2p_config.outgoing:
30
35
  if update.status & ChatUpdate.Status.DISCARDED_CALL:
31
- self._wait_connect.pop(chat_id, None)
32
36
  p2p_config.wait_data.set_exception(
33
37
  CallBusy(
34
38
  chat_id,
@@ -141,15 +145,8 @@ class HandleMTProtoUpdates(Scaffold):
141
145
  chat_peer,
142
146
  ) == participant.user_id if chat_peer else False
143
147
  if is_self:
144
- if action == GroupCallParticipant.Action.LEFT:
145
- if await self._clear_call(chat_id):
146
- await self._propagate(
147
- ChatUpdate(
148
- chat_id,
149
- ChatUpdate.Status.KICKED,
150
- ),
151
- self,
152
- )
148
+ if action == GroupCallParticipant.Action.KICKED:
149
+ await self._clear_call(chat_id)
153
150
  if (
154
151
  chat_id in self._need_unmute and
155
152
  action == GroupCallParticipant.Action.UPDATED
@@ -1,7 +1,7 @@
1
1
  from typing import Union
2
2
 
3
- from ntgcalls import ConnectionError
4
3
  from ntgcalls import ConnectionMode
4
+ from ntgcalls import ConnectionNotFound
5
5
  from ntgcalls import TelegramServerError
6
6
 
7
7
  from ...scaffold import Scaffold
@@ -53,6 +53,6 @@ class JoinPresentation(Scaffold):
53
53
  try:
54
54
  await self._binding.stop_presentation(chat_id)
55
55
  await self._app.leave_presentation(chat_id)
56
- except ConnectionError:
56
+ except ConnectionNotFound:
57
57
  pass
58
58
  self._presentations.discard(chat_id)
@@ -44,11 +44,12 @@ class Play(Scaffold):
44
44
 
45
45
  if chat_id in await self._binding.calls():
46
46
  try:
47
- return await self._binding.set_stream_sources(
47
+ await self._binding.set_stream_sources(
48
48
  chat_id,
49
49
  StreamMode.CAPTURE,
50
50
  media_description,
51
51
  )
52
+ return
52
53
  except FileError as e:
53
54
  raise FileNotFoundError(e)
54
55
 
@@ -31,7 +31,7 @@ class StreamParams:
31
31
  'MediaStream or a raw Stream',
32
32
  )
33
33
 
34
- return StreamParams._parse_stream_description(stream)
34
+ return StreamParams._parse_stream_description(stream) # type: ignore
35
35
 
36
36
  @staticmethod
37
37
  def _parse_media_description(
@@ -11,6 +11,7 @@ from ntgcalls import SsrcGroup
11
11
 
12
12
  from ..handlers import HandlersHolder
13
13
  from ..types import GroupCallParticipant
14
+ from ..types import UpdatedGroupCallParticipant
14
15
 
15
16
 
16
17
  class BridgedClient(HandlersHolder):
@@ -96,6 +97,12 @@ class BridgedClient(HandlersHolder):
96
97
  ):
97
98
  pass
98
99
 
100
+ async def close_voice_chat(
101
+ self,
102
+ chat_id: int,
103
+ ):
104
+ pass
105
+
99
106
  async def get_group_call_participants(
100
107
  self,
101
108
  chat_id: int,
@@ -195,6 +202,42 @@ class BridgedClient(HandlersHolder):
195
202
  BridgedClient.parse_source(participant.presentation),
196
203
  )
197
204
 
205
+ @staticmethod
206
+ async def diff_participants_update(
207
+ cache,
208
+ chat_id: Optional[int],
209
+ participant,
210
+ ) -> List[UpdatedGroupCallParticipant]:
211
+ if chat_id is None:
212
+ return []
213
+ user_id = BridgedClient.chat_id(participant.peer)
214
+ participants = await cache.get_participant_list(
215
+ chat_id,
216
+ True,
217
+ )
218
+ updates = []
219
+ for p in participants:
220
+ if p.user_id == user_id:
221
+ if p.source != participant.source:
222
+ updates.append(
223
+ UpdatedGroupCallParticipant(
224
+ chat_id,
225
+ GroupCallParticipant.Action.KICKED,
226
+ p,
227
+ ),
228
+ )
229
+ participant.just_joined = True
230
+ break
231
+
232
+ updates.append(
233
+ UpdatedGroupCallParticipant(
234
+ chat_id,
235
+ BridgedClient.parse_participant_action(participant),
236
+ BridgedClient.parse_participant(participant),
237
+ ),
238
+ )
239
+ return updates
240
+
198
241
  @staticmethod
199
242
  def parse_participant_action(participant):
200
243
  if participant.just_joined:
@@ -1,5 +1,4 @@
1
1
  import logging
2
- from time import time
3
2
  from typing import Any
4
3
  from typing import List
5
4
  from typing import Optional
@@ -19,11 +18,12 @@ class ClientCache:
19
18
  app: BridgedClient,
20
19
  ):
21
20
  self._app: BridgedClient = app
22
- self._cache_duration = 1 if app.no_updates() else cache_duration
23
- self._full_chat_cache = Cache()
24
- self._call_participants_cache = Cache()
25
- self._dc_call_cache = Cache()
26
- self._phone_calls = Cache()
21
+ cache_duration = 0 if app.no_updates() else cache_duration
22
+ full_chat_duration = 1 if app.no_updates() else cache_duration
23
+ self._full_chat_cache = Cache(full_chat_duration)
24
+ self._call_participants_cache = Cache(cache_duration)
25
+ self._dc_call_cache = Cache(full_chat_duration)
26
+ self._phone_calls = Cache(full_chat_duration)
27
27
 
28
28
  async def get_full_chat(
29
29
  self,
@@ -46,97 +46,64 @@ class ClientCache:
46
46
  pass
47
47
  return None
48
48
 
49
- def set_participants_cache_call(
49
+ def set_participants_cache(
50
50
  self,
51
- input_id: int,
52
- action: GroupCallParticipant.Action,
53
- participant: GroupCallParticipant,
54
- ) -> Optional[GroupCallParticipant]:
55
- chat_id = self.get_chat_id(input_id)
56
- if chat_id is not None:
57
- return self._internal_set_participants_cache(
58
- chat_id,
59
- action,
60
- participant,
61
- )
62
- return None
63
-
64
- def set_participants_cache_chat(
65
- self,
66
- chat_id: int,
51
+ chat_id: Optional[int],
67
52
  call_id: int,
68
53
  action: GroupCallParticipant.Action,
69
54
  participant: GroupCallParticipant,
70
55
  ) -> Optional[GroupCallParticipant]:
71
- if self._call_participants_cache.get(chat_id) is None:
72
- self._call_participants_cache.put(
56
+ if chat_id is not None:
57
+ if self._call_participants_cache.get(chat_id) is None:
58
+ self._call_participants_cache.put(
59
+ chat_id,
60
+ ParticipantList(
61
+ call_id,
62
+ ),
63
+ )
64
+ participants: Optional[
65
+ ParticipantList
66
+ ] = self._call_participants_cache.get(
73
67
  chat_id,
74
- ParticipantList(
75
- call_id,
76
- ),
77
- )
78
- return self._internal_set_participants_cache(
79
- chat_id,
80
- action,
81
- participant,
82
- )
83
-
84
- def _internal_set_participants_cache(
85
- self,
86
- chat_id: int,
87
- action: GroupCallParticipant.Action,
88
- participant: GroupCallParticipant,
89
- ) -> Optional[GroupCallParticipant]:
90
- participants: Optional[
91
- ParticipantList
92
- ] = self._call_participants_cache.get(
93
- chat_id,
94
- )
95
- if participants is not None:
96
- participants.last_mtproto_update = (
97
- int(time()) + self._cache_duration
98
- )
99
- return participants.update_participant(
100
- action,
101
- participant,
102
68
  )
69
+ if participants is not None:
70
+ self._call_participants_cache.update_cache(chat_id)
71
+ return participants.update_participant(
72
+ action,
73
+ participant,
74
+ )
103
75
  return None
104
76
 
105
77
  async def get_participant_list(
106
78
  self,
107
79
  chat_id: int,
108
- ) -> Optional[List[GroupCallParticipant]]:
80
+ only_cached: bool = False,
81
+ ) -> List[GroupCallParticipant]:
109
82
  input_call = await self.get_full_chat(
110
83
  chat_id,
111
84
  )
112
85
  if input_call is not None:
113
- participants: Optional[
114
- ParticipantList
115
- ] = self._call_participants_cache.get(
116
- chat_id,
117
- )
118
- if participants is not None:
119
- last_update = participants.last_mtproto_update
120
- curr_time = int(time())
121
- if not (last_update - curr_time > 0):
122
- py_logger.debug(
123
- 'GetParticipant cache miss for %d', chat_id,
86
+ if self._call_participants_cache.get(chat_id) is None:
87
+ if only_cached:
88
+ return []
89
+ py_logger.debug(
90
+ 'GetParticipant cache miss for %d', chat_id,
91
+ )
92
+ list_participants = await self._app.get_participants(
93
+ input_call,
94
+ )
95
+ for participant in list_participants:
96
+ self.set_participants_cache(
97
+ chat_id,
98
+ input_call.id,
99
+ GroupCallParticipant.Action.UPDATED,
100
+ participant,
124
101
  )
125
- try:
126
- list_participants = await self._app.get_participants(
127
- input_call,
128
- )
129
- for participant in list_participants:
130
- self.set_participants_cache_call(
131
- input_call.id,
132
- GroupCallParticipant.Action.UPDATED,
133
- participant,
134
- )
135
- except Exception as e:
136
- py_logger.error('Error for %s in %d', e, chat_id)
137
- else:
138
- py_logger.debug('GetParticipant cache hit for %d', chat_id)
139
- return participants.get_participants()
102
+ else:
103
+ py_logger.debug('GetParticipant cache hit for %d', chat_id)
104
+ return self._call_participants_cache.get(
105
+ chat_id,
106
+ ).get_participants()
140
107
  return []
141
108
 
142
109
  def get_chat_id(
@@ -158,7 +125,6 @@ class ClientCache:
158
125
  self._full_chat_cache.put(
159
126
  chat_id,
160
127
  input_call,
161
- self._cache_duration,
162
128
  )
163
129
  if self._call_participants_cache.get(chat_id) is None:
164
130
  self._call_participants_cache.put(
@@ -21,6 +21,7 @@ from hydrogram.raw.functions.phone import AcceptCall
21
21
  from hydrogram.raw.functions.phone import ConfirmCall
22
22
  from hydrogram.raw.functions.phone import CreateGroupCall
23
23
  from hydrogram.raw.functions.phone import DiscardCall
24
+ from hydrogram.raw.functions.phone import DiscardGroupCall
24
25
  from hydrogram.raw.functions.phone import EditGroupCallParticipant
25
26
  from hydrogram.raw.functions.phone import GetGroupCall
26
27
  from hydrogram.raw.functions.phone import GetGroupCallStreamChannels
@@ -76,7 +77,6 @@ from ..types import CallProtocol
76
77
  from ..types import ChatUpdate
77
78
  from ..types import GroupCallParticipant
78
79
  from ..types import RawCallUpdate
79
- from ..types import UpdatedGroupCallParticipant
80
80
  from .bridged_client import BridgedClient
81
81
  from .client_cache import ClientCache
82
82
 
@@ -186,22 +186,22 @@ class HydrogramClient(BridgedClient):
186
186
  update,
187
187
  UpdateGroupCallParticipants,
188
188
  ):
189
- participants = update.participants
190
- for participant in participants:
191
- action = self.parse_participant_action(participant)
192
- result = self._cache.set_participants_cache_call(
193
- update.call.id,
194
- action,
195
- self.parse_participant(participant),
189
+ for participant in update.participants:
190
+ chat_id = self._cache.get_chat_id(update.call.id)
191
+ p_updates = await self.diff_participants_update(
192
+ self._cache,
193
+ chat_id,
194
+ participant,
196
195
  )
197
- if result is not None:
198
- await self._propagate(
199
- UpdatedGroupCallParticipant(
200
- self._cache.get_chat_id(update.call.id),
201
- action,
202
- result,
203
- ),
196
+ for p_update in p_updates:
197
+ result = self._cache.set_participants_cache(
198
+ chat_id,
199
+ update.call.id,
200
+ p_update.action,
201
+ p_update.participant,
204
202
  )
203
+ if result is not None:
204
+ await self._propagate(p_update)
205
205
  if isinstance(
206
206
  update,
207
207
  UpdateGroupCall,
@@ -355,7 +355,7 @@ class HydrogramClient(BridgedClient):
355
355
  call: GroupCall = raw_call.call
356
356
  participants: List[GroupCallParticipant] = raw_call.participants
357
357
  for participant in participants:
358
- self._cache.set_participants_cache_chat(
358
+ self._cache.set_participants_cache(
359
359
  chat_id,
360
360
  call.id,
361
361
  self.parse_participant_action(participant),
@@ -433,7 +433,8 @@ class HydrogramClient(BridgedClient):
433
433
  ):
434
434
  participants = update.participants
435
435
  for participant in participants:
436
- self._cache.set_participants_cache_call(
436
+ self._cache.set_participants_cache(
437
+ chat_id,
437
438
  update.call.id,
438
439
  self.parse_participant_action(participant),
439
440
  self.parse_participant(participant),
@@ -588,6 +589,19 @@ class HydrogramClient(BridgedClient):
588
589
  ),
589
590
  )
590
591
 
592
+ async def close_voice_chat(
593
+ self,
594
+ chat_id: int,
595
+ ):
596
+ chat_call = await self._cache.get_full_chat(chat_id)
597
+ if chat_call is not None:
598
+ await self._invoke(
599
+ DiscardGroupCall(
600
+ call=chat_call,
601
+ ),
602
+ )
603
+ self._cache.drop_cache(chat_id)
604
+
591
605
  async def discard_call(
592
606
  self,
593
607
  chat_id: int,
@@ -197,6 +197,17 @@ class MtProtoClient:
197
197
  else:
198
198
  raise InvalidMTProtoClient()
199
199
 
200
+ async def close_voice_chat(
201
+ self,
202
+ chat_id: int,
203
+ ):
204
+ if self._bind_client is not None:
205
+ await self._bind_client.close_voice_chat(
206
+ chat_id,
207
+ )
208
+ else:
209
+ raise InvalidMTProtoClient()
210
+
200
211
  async def change_volume(
201
212
  self,
202
213
  chat_id: int,
@@ -23,6 +23,7 @@ from pyrogram.raw.functions.phone import AcceptCall
23
23
  from pyrogram.raw.functions.phone import ConfirmCall
24
24
  from pyrogram.raw.functions.phone import CreateGroupCall
25
25
  from pyrogram.raw.functions.phone import DiscardCall
26
+ from pyrogram.raw.functions.phone import DiscardGroupCall
26
27
  from pyrogram.raw.functions.phone import EditGroupCallParticipant
27
28
  from pyrogram.raw.functions.phone import GetGroupCall
28
29
  from pyrogram.raw.functions.phone import GetGroupCallStreamChannels
@@ -76,7 +77,6 @@ from ..types import CallProtocol
76
77
  from ..types import ChatUpdate
77
78
  from ..types import GroupCallParticipant
78
79
  from ..types import RawCallUpdate
79
- from ..types import UpdatedGroupCallParticipant
80
80
  from .bridged_client import BridgedClient
81
81
  from .client_cache import ClientCache
82
82
 
@@ -186,22 +186,22 @@ class PyrogramClient(BridgedClient):
186
186
  update,
187
187
  UpdateGroupCallParticipants,
188
188
  ):
189
- participants = update.participants
190
- for participant in participants:
191
- action = self.parse_participant_action(participant)
192
- result = self._cache.set_participants_cache_call(
193
- update.call.id,
194
- action,
195
- self.parse_participant(participant),
189
+ for participant in update.participants:
190
+ chat_id = self._cache.get_chat_id(update.call.id)
191
+ p_updates = await self.diff_participants_update(
192
+ self._cache,
193
+ chat_id,
194
+ participant,
196
195
  )
197
- if result is not None:
198
- await self._propagate(
199
- UpdatedGroupCallParticipant(
200
- self._cache.get_chat_id(update.call.id),
201
- action,
202
- result,
203
- ),
196
+ for p_update in p_updates:
197
+ result = self._cache.set_participants_cache(
198
+ chat_id,
199
+ update.call.id,
200
+ p_update.action,
201
+ p_update.participant,
204
202
  )
203
+ if result is not None:
204
+ await self._propagate(p_update)
205
205
 
206
206
  if isinstance(
207
207
  update,
@@ -356,7 +356,7 @@ class PyrogramClient(BridgedClient):
356
356
  call: GroupCall = raw_call.call
357
357
  participants: List[GroupCallParticipant] = raw_call.participants
358
358
  for participant in participants:
359
- self._cache.set_participants_cache_chat(
359
+ self._cache.set_participants_cache(
360
360
  chat_id,
361
361
  call.id,
362
362
  self.parse_participant_action(participant),
@@ -434,7 +434,8 @@ class PyrogramClient(BridgedClient):
434
434
  ):
435
435
  participants = update.participants
436
436
  for participant in participants:
437
- self._cache.set_participants_cache_call(
437
+ self._cache.set_participants_cache(
438
+ chat_id,
438
439
  update.call.id,
439
440
  self.parse_participant_action(participant),
440
441
  self.parse_participant(participant),
@@ -589,6 +590,19 @@ class PyrogramClient(BridgedClient):
589
590
  ),
590
591
  )
591
592
 
593
+ async def close_voice_chat(
594
+ self,
595
+ chat_id: int,
596
+ ):
597
+ chat_call = await self._cache.get_full_chat(chat_id)
598
+ if chat_call is not None:
599
+ await self._invoke(
600
+ DiscardGroupCall(
601
+ call=chat_call,
602
+ ),
603
+ )
604
+ self._cache.drop_cache(chat_id)
605
+
592
606
  async def discard_call(
593
607
  self,
594
608
  chat_id: int,
@@ -18,6 +18,7 @@ from telethon.tl.functions.phone import AcceptCallRequest
18
18
  from telethon.tl.functions.phone import ConfirmCallRequest
19
19
  from telethon.tl.functions.phone import CreateGroupCallRequest
20
20
  from telethon.tl.functions.phone import DiscardCallRequest
21
+ from telethon.tl.functions.phone import DiscardGroupCallRequest
21
22
  from telethon.tl.functions.phone import EditGroupCallParticipantRequest
22
23
  from telethon.tl.functions.phone import GetGroupCallRequest
23
24
  from telethon.tl.functions.phone import GetGroupCallStreamChannelsRequest
@@ -70,7 +71,6 @@ from ..types import CallProtocol
70
71
  from ..types import ChatUpdate
71
72
  from ..types import GroupCallParticipant
72
73
  from ..types import RawCallUpdate
73
- from ..types import UpdatedGroupCallParticipant
74
74
  from .bridged_client import BridgedClient
75
75
  from .client_cache import ClientCache
76
76
 
@@ -180,22 +180,22 @@ class TelethonClient(BridgedClient):
180
180
  update,
181
181
  UpdateGroupCallParticipants,
182
182
  ):
183
- participants = update.participants
184
- for participant in participants:
185
- action = self.parse_participant_action(participant)
186
- result = self._cache.set_participants_cache_call(
187
- update.call.id,
188
- action,
189
- self.parse_participant(participant),
183
+ for participant in update.participants:
184
+ chat_id = self._cache.get_chat_id(update.call.id)
185
+ p_updates = await self.diff_participants_update(
186
+ self._cache,
187
+ chat_id,
188
+ participant,
190
189
  )
191
- if result is not None:
192
- await self._propagate(
193
- UpdatedGroupCallParticipant(
194
- self._cache.get_chat_id(update.call.id),
195
- action,
196
- result,
197
- ),
190
+ for p_update in p_updates:
191
+ result = self._cache.set_participants_cache(
192
+ chat_id,
193
+ update.call.id,
194
+ p_update.action,
195
+ p_update.participant,
198
196
  )
197
+ if result is not None:
198
+ await self._propagate(p_update)
199
199
  if isinstance(
200
200
  update,
201
201
  UpdateGroupCall,
@@ -342,7 +342,7 @@ class TelethonClient(BridgedClient):
342
342
  call: GroupCall = raw_call.call
343
343
  participants: List[GroupCallParticipant] = raw_call.participants
344
344
  for participant in participants:
345
- self._cache.set_participants_cache_chat(
345
+ self._cache.set_participants_cache(
346
346
  chat_id,
347
347
  call.id,
348
348
  self.parse_participant_action(participant),
@@ -420,7 +420,8 @@ class TelethonClient(BridgedClient):
420
420
  ):
421
421
  participants = update.participants
422
422
  for participant in participants:
423
- self._cache.set_participants_cache_call(
423
+ self._cache.set_participants_cache(
424
+ chat_id,
424
425
  update.call.id,
425
426
  self.parse_participant_action(participant),
426
427
  self.parse_participant(participant),
@@ -575,6 +576,19 @@ class TelethonClient(BridgedClient):
575
576
  ),
576
577
  )
577
578
 
579
+ async def close_voice_chat(
580
+ self,
581
+ chat_id: int,
582
+ ):
583
+ chat_call = await self._cache.get_full_chat(chat_id)
584
+ if chat_call is not None:
585
+ await self._invoke(
586
+ DiscardGroupCallRequest(
587
+ call=chat_call,
588
+ ),
589
+ )
590
+ self._cache.drop_cache(chat_id)
591
+
578
592
  async def discard_call(
579
593
  self,
580
594
  chat_id: int,
pytgcalls/types/cache.py CHANGED
@@ -8,13 +8,13 @@ from typing import Optional
8
8
  @dataclass
9
9
  class CacheEntry:
10
10
  time: int
11
- expiry_time: int
12
11
  data: Any
13
12
 
14
13
 
15
14
  class Cache:
16
- def __init__(self):
15
+ def __init__(self, expiry_time: int = 0):
17
16
  self._store: Dict[int, CacheEntry] = {} # type: ignore
17
+ self._expiry_time = expiry_time
18
18
 
19
19
  def get(self, chat_id: int):
20
20
  if chat_id in self._store:
@@ -25,13 +25,18 @@ class Cache:
25
25
  self._store.pop(chat_id, None)
26
26
  return None
27
27
 
28
- def put(self, chat_id: int, data: Any, expiry_time: int = 0) -> None:
28
+ def put(self, chat_id: int, data: Any) -> None:
29
29
  self._store[chat_id] = CacheEntry(
30
- time=0 if expiry_time == 0 else (int(time()) + expiry_time),
31
- expiry_time=expiry_time,
30
+ time=0
31
+ if self._expiry_time == 0 else
32
+ (int(time()) + self._expiry_time),
32
33
  data=data,
33
34
  )
34
35
 
36
+ def update_cache(self, chat_id: int) -> None:
37
+ if chat_id in self._store:
38
+ self._store[chat_id].time = int(time()) + self._expiry_time
39
+
35
40
  @property
36
41
  def keys(self):
37
42
  return list(self._store)
@@ -1,7 +1,7 @@
1
1
  from enum import auto
2
- from enum import Flag
3
2
  from typing import Optional
4
3
 
4
+ from ..flag import Flag
5
5
  from pytgcalls.types.update import Update
6
6
 
7
7
 
@@ -1,17 +1,18 @@
1
1
  from enum import auto
2
- from enum import Flag
3
2
  from typing import List
4
3
  from typing import Optional
5
4
 
6
5
  from ntgcalls import SsrcGroup
7
6
 
8
7
  from ...types.py_object import PyObject
8
+ from ..flag import Flag
9
9
 
10
10
 
11
11
  class GroupCallParticipant(PyObject):
12
12
  class Action(Flag):
13
13
  JOINED = auto()
14
14
  LEFT = auto()
15
+ KICKED = auto()
15
16
  UPDATED = auto()
16
17
 
17
18
  class SourceInfo(PyObject):
pytgcalls/types/dict.py CHANGED
@@ -1,5 +1,5 @@
1
1
  from ..types.py_object import PyObject
2
2
 
3
3
 
4
- class Dict(dict, PyObject):
4
+ class Dict(PyObject, dict):
5
5
  pass
pytgcalls/types/flag.py CHANGED
@@ -1,6 +1,7 @@
1
1
  from enum import Flag as _Flag
2
2
 
3
+ from .py_object import PyObject
3
4
 
4
- class Flag(_Flag):
5
- def __repr__(self):
6
- return f'{self.__class__.__name__}.{self.name}'
5
+
6
+ class Flag(PyObject, _Flag):
7
+ pass
pytgcalls/types/list.py CHANGED
@@ -1,5 +1,5 @@
1
1
  from ..types.py_object import PyObject
2
2
 
3
3
 
4
- class List(list, PyObject):
4
+ class List(PyObject, list):
5
5
  pass
@@ -10,14 +10,13 @@ class ParticipantList:
10
10
  input_id: int,
11
11
  ):
12
12
  self._list_participants: Dict[int, GroupCallParticipant] = {}
13
- self.last_mtproto_update: int = 0
14
13
  self.input_id: int = input_id
15
14
 
16
15
  def update_participant(
17
16
  self,
18
17
  action: GroupCallParticipant.Action,
19
18
  participant: GroupCallParticipant,
20
- ):
19
+ ) -> GroupCallParticipant:
21
20
  if action == GroupCallParticipant.Action.LEFT:
22
21
  if participant.user_id in self._list_participants:
23
22
  del self._list_participants[participant.user_id]
@@ -11,8 +11,10 @@ class PyObject:
11
11
  def default(obj) -> Union[str, Dict[str, str], List[Any]]:
12
12
  if isinstance(obj, bytes):
13
13
  return repr(obj)
14
- if isinstance(obj, Enum):
15
- return repr(obj)
14
+ elif isinstance(obj, Enum):
15
+ return ' | '.join(
16
+ [f"{obj.__class__.__name__}.{x}" for x in obj.name.split('|')],
17
+ )
16
18
  return {
17
19
  '_': obj.__class__.__name__,
18
20
  **{
@@ -1,7 +1,9 @@
1
1
  from enum import Enum
2
2
 
3
+ from ..py_object import PyObject
3
4
 
4
- class VideoQuality(Enum):
5
+
6
+ class VideoQuality(PyObject, Enum):
5
7
  UHD_4K = (3840, 2160, 60)
6
8
  QHD_2K = (2560, 1440, 60)
7
9
  FHD_1080p = (1920, 1080, 60)