py-tgcalls 2.2.0rc2__py3-none-any.whl → 2.2.1__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.0rc2
3
+ Version: 2.2.1
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.0rc4
23
+ Requires-Dist: ntgcalls<3.0.0,>=2.0.1
24
24
  Requires-Dist: deprecation
25
25
  Provides-Extra: pyrogram
26
26
  Requires-Dist: pyrogram>=1.2.20; extra == "pyrogram"
@@ -1,10 +1,10 @@
1
- py_tgcalls-2.2.0rc2.dist-info/licenses/LICENSE,sha256=46mU2C5kSwOnkqkw9XQAJlhBL2JAf1_uCD8lVcXyMRg,7652
1
+ py_tgcalls-2.2.1.dist-info/licenses/LICENSE,sha256=46mU2C5kSwOnkqkw9XQAJlhBL2JAf1_uCD8lVcXyMRg,7652
2
2
  pytgcalls/__init__.py,sha256=qbfwN7rYwIdCegMOzdcbvwazeNjDzgmowgcqLFNqKIM,308
3
- pytgcalls/__version__.py,sha256=Cyc7uLJxAPLnuEEaO5JtEF4rXv_8vGUZ7RKqg1iyVH8,25
3
+ pytgcalls/__version__.py,sha256=-7agX4LcQ0956RU4mY0EYyxYQU9llH7JklZOsqoujdU,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
7
- pytgcalls/filters.py,sha256=qTFDlt-23xnMh_Ug2WmmOUf13JPX6_yacrv7c3F9Pp0,6125
7
+ pytgcalls/filters.py,sha256=8Fq_gvHHdqhIk_XwMJ9wneeZOwrvBkSOKaMJ60r3bZU,6157
8
8
  pytgcalls/list_to_cmd.py,sha256=rGJLsejbAQdDb8pctMbLnwea5NkitlfVKc3IpoGi4V4,240
9
9
  pytgcalls/mtproto_required.py,sha256=6B-31p5qH_6oekUgypV4nK3hqPS6Nr-pA8S81wjnbaY,630
10
10
  pytgcalls/mutex.py,sha256=vWqji9IAHtAc61FUTVXncx5rZ3rVf5qG_7PI4LmuYmU,733
@@ -30,7 +30,7 @@ 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
@@ -39,7 +39,7 @@ pytgcalls/methods/internal/clear_call.py,sha256=yElvn3i6_j14Q7n9kcLSl-muYAGcaIZa
39
39
  pytgcalls/methods/internal/connect_call.py,sha256=Py-sB_ImH839tK9GxNUCJDJUNizsZtlm5m6kuiUX3lE,5739
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=Ev2Jake6Z_SrBodnjO5N16YiYaEY3Ti4tkU1pKOsJVM,7747
42
+ pytgcalls/methods/internal/handle_mtproto_updates.py,sha256=ZPGvenIjWiNnTTTKdhQdapHsxwYn4pSKOgKm8bPcPOk,7415
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
45
  pytgcalls/methods/internal/join_presentation.py,sha256=dLB2kolIIm2KQH8yBQbEPJi_y1_cVLWpE52_Rwjn_KE,2178
@@ -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
@@ -70,20 +70,20 @@ 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=b8RmasIkXBTn15tAWZJ2ptPfxhIh0rGoNuAYRUlaUyQ,6776
74
- pytgcalls/mtproto/client_cache.py,sha256=iVcBW9WE2w4s-yswOZpZ1eC2t0yR8TjppS86qOv6f2Q,6292
75
- pytgcalls/mtproto/hydrogram_client.py,sha256=hhQ2fnPR74G39hoJ3oKRP3jeC7c-QGSuVpyCMp2mqr4,28312
76
- pytgcalls/mtproto/mtproto_client.py,sha256=95A13HsNJfpNslsY_gWiqYXj_Vvo-pl3rVrsJGxftgg,8517
77
- pytgcalls/mtproto/pyrogram_client.py,sha256=vqFLbDLJt5rrhgTqmzuiW2mAVGl9-c9LK5cXgup0QAU,28308
78
- pytgcalls/mtproto/telethon_client.py,sha256=ZMyLEZvog5iojoRyjcuk6-WIUnaJrJGnO6nfaKSMwao,26411
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=LmGjU63MK1v3SS2_4xNbk04OOjmukNdAXYLRn2L-730,916
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
@@ -97,8 +97,8 @@ pytgcalls/types/calls/pending_connection.py,sha256=qRRmutInj70rtzbThM7CNznFhEPqT
97
97
  pytgcalls/types/calls/raw_call_update.py,sha256=hpNw6HrTW8Z36Lh2HinS-wzprryRtsIxyIFbIfjGgeI,795
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=XG4dekFV9GYHASqy_bSE6YnZ1uDunFFif-xLAy_eYGY,1711
101
- pytgcalls/types/chats/updated_group_call_participant.py,sha256=-KID-z-4e43fhYWQp0pNMKPfmZizbJHXyzn6yLtIGNg,291
100
+ pytgcalls/types/chats/group_call_participant.py,sha256=aK7nvI0Jy93g9A_FVeFzk1BZFXzhFry06h8xR4VpqXY,1507
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
104
104
  pytgcalls/types/raw/audio_stream.py,sha256=oN7Sx9oLbNFuNXiGYpoNabMwqWKGquLiHEywef28o7c,488
@@ -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.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,,
118
+ pytgcalls/types/stream/video_quality.py,sha256=eMCBFPwh5meX3UVEaozcGlwmgaujfpiTa3vBVSBBP_8,275
119
+ py_tgcalls-2.2.1.dist-info/METADATA,sha256=7j-AgY6sI6uL623U_ZbjjXwbfu7_KymRphdpSLssPJE,5280
120
+ py_tgcalls-2.2.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
121
+ py_tgcalls-2.2.1.dist-info/top_level.txt,sha256=IUDUwn0KkcbUYZbCe9R5AUb2Ob-lmllNUGQqyeXXd8A,10
122
+ py_tgcalls-2.2.1.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.0rc2'
1
+ __version__ = '2.2.1'
pytgcalls/filters.py CHANGED
@@ -158,6 +158,7 @@ class stream_end(Filter):
158
158
  self.device & update.device
159
159
  )
160
160
  )
161
+ return False
161
162
 
162
163
 
163
164
  # noinspection PyPep8Naming
@@ -190,14 +191,17 @@ class chat_update(Filter):
190
191
 
191
192
  # noinspection PyPep8Naming
192
193
  class call_participant(Filter):
193
- def __init__(self, flags: Optional[GroupCallParticipant.Action] = None):
194
+ def __init__(
195
+ self,
196
+ flags: Optional[GroupCallParticipant.Action] = None,
197
+ ):
194
198
  self.flags = flags
195
199
 
196
200
  async def __call__(self, client: PyTgCalls, update: Update):
197
201
  if isinstance(update, UpdatedGroupCallParticipant):
198
202
  if self.flags is None:
199
203
  return True
200
- return self.flags & update.participant.action
204
+ return self.flags & update.action
201
205
  return False
202
206
 
203
207
 
@@ -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)
@@ -72,7 +72,7 @@ class HandleMTProtoUpdates(Scaffold):
72
72
  await self._clear_call(chat_id)
73
73
  if isinstance(update, UpdatedGroupCallParticipant):
74
74
  participant = update.participant
75
- action = participant.action
75
+ action = update.action
76
76
  chat_peer = self._cache_user_peer.get(chat_id)
77
77
  user_id = participant.user_id
78
78
  if chat_id in self._call_sources:
@@ -141,19 +141,12 @@ class HandleMTProtoUpdates(Scaffold):
141
141
  chat_peer,
142
142
  ) == participant.user_id if chat_peer else False
143
143
  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
- )
144
+ if action == GroupCallParticipant.Action.KICKED:
145
+ await self._clear_call(chat_id)
153
146
  if (
154
- chat_id in self._need_unmute and
155
- action == GroupCallParticipant.Action.UPDATED and
156
- not participant.muted_by_admin
147
+ chat_id in self._need_unmute and
148
+ action == GroupCallParticipant.Action.UPDATED
149
+ and not participant.muted_by_admin
157
150
  ):
158
151
  await self._update_status(
159
152
  chat_id,
@@ -162,8 +155,8 @@ class HandleMTProtoUpdates(Scaffold):
162
155
  await self._switch_connection(chat_id)
163
156
 
164
157
  if (
165
- participant.muted_by_admin and
166
- action != GroupCallParticipant.Action.LEFT
158
+ participant.muted_by_admin and
159
+ action != GroupCallParticipant.Action.LEFT
167
160
  ):
168
161
  self._need_unmute.add(chat_id)
169
162
  else:
@@ -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
 
@@ -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,
@@ -190,13 +197,56 @@ class BridgedClient(HandlersHolder):
190
197
  bool(participant.raise_hand_rating),
191
198
  participant.volume // 100
192
199
  if participant.volume is not None else 100,
193
- bool(participant.just_joined),
194
- bool(participant.left),
195
200
  participant.source,
196
201
  BridgedClient.parse_source(participant.video),
197
202
  BridgedClient.parse_source(participant.presentation),
198
203
  )
199
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
+
241
+ @staticmethod
242
+ def parse_participant_action(participant):
243
+ if participant.just_joined:
244
+ return GroupCallParticipant.Action.JOINED
245
+ elif participant.left:
246
+ return GroupCallParticipant.Action.LEFT
247
+ else:
248
+ return GroupCallParticipant.Action.UPDATED
249
+
200
250
  @staticmethod
201
251
  def chat_id(input_peer) -> int:
202
252
  class_name = input_peer.__class__.__name__
@@ -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,88 +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
- participant: GroupCallParticipant,
53
- ) -> Optional[GroupCallParticipant]:
54
- chat_id = self.get_chat_id(input_id)
55
- if chat_id is not None:
56
- return self._internal_set_participants_cache(
57
- chat_id,
58
- participant,
59
- )
60
- return None
61
-
62
- def set_participants_cache_chat(
63
- self,
64
- chat_id: int,
51
+ chat_id: Optional[int],
65
52
  call_id: int,
53
+ action: GroupCallParticipant.Action,
66
54
  participant: GroupCallParticipant,
67
55
  ) -> Optional[GroupCallParticipant]:
68
- if self._call_participants_cache.get(chat_id) is None:
69
- 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(
70
67
  chat_id,
71
- ParticipantList(
72
- call_id,
73
- ),
74
- )
75
- return self._internal_set_participants_cache(
76
- chat_id,
77
- participant,
78
- )
79
-
80
- def _internal_set_participants_cache(
81
- self,
82
- chat_id: int,
83
- participant: GroupCallParticipant,
84
- ) -> Optional[GroupCallParticipant]:
85
- participants: Optional[
86
- ParticipantList
87
- ] = self._call_participants_cache.get(
88
- chat_id,
89
- )
90
- if participants is not None:
91
- participants.last_mtproto_update = (
92
- int(time()) + self._cache_duration
93
68
  )
94
- return participants.update_participant(participant)
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
+ )
95
75
  return None
96
76
 
97
77
  async def get_participant_list(
98
78
  self,
99
79
  chat_id: int,
100
- ) -> Optional[List[GroupCallParticipant]]:
80
+ only_cached: bool = False,
81
+ ) -> List[GroupCallParticipant]:
101
82
  input_call = await self.get_full_chat(
102
83
  chat_id,
103
84
  )
104
85
  if input_call is not None:
105
- participants: Optional[
106
- ParticipantList
107
- ] = self._call_participants_cache.get(
108
- chat_id,
109
- )
110
- if participants is not None:
111
- last_update = participants.last_mtproto_update
112
- curr_time = int(time())
113
- if not (last_update - curr_time > 0):
114
- py_logger.debug(
115
- '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,
116
101
  )
117
- try:
118
- list_participants = await self._app.get_participants(
119
- input_call,
120
- )
121
- for participant in list_participants:
122
- self.set_participants_cache_call(
123
- input_call.id,
124
- participant,
125
- )
126
- except Exception as e:
127
- py_logger.error('Error for %s in %d', e, chat_id)
128
- else:
129
- py_logger.debug('GetParticipant cache hit for %d', chat_id)
130
- 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()
131
107
  return []
132
108
 
133
109
  def get_chat_id(
@@ -149,7 +125,6 @@ class ClientCache:
149
125
  self._full_chat_cache.put(
150
126
  chat_id,
151
127
  input_call,
152
- self._cache_duration,
153
128
  )
154
129
  if self._call_participants_cache.get(chat_id) is None:
155
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
 
@@ -94,7 +94,7 @@ class HydrogramClient(BridgedClient):
94
94
  self,
95
95
  )
96
96
 
97
- @self._app.on_raw_update(group=-1)
97
+ @self._app.on_raw_update(group=-9999)
98
98
  async def on_update(_, update, __, chats):
99
99
  if isinstance(
100
100
  update,
@@ -186,19 +186,22 @@ class HydrogramClient(BridgedClient):
186
186
  update,
187
187
  UpdateGroupCallParticipants,
188
188
  ):
189
- participants = update.participants
190
- for participant in participants:
191
- result = self._cache.set_participants_cache_call(
192
- update.call.id,
193
- 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,
194
195
  )
195
- if result is not None:
196
- await self._propagate(
197
- UpdatedGroupCallParticipant(
198
- self._cache.get_chat_id(update.call.id),
199
- result,
200
- ),
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,
201
202
  )
203
+ if result is not None:
204
+ await self._propagate(p_update)
202
205
  if isinstance(
203
206
  update,
204
207
  UpdateGroupCall,
@@ -352,9 +355,10 @@ class HydrogramClient(BridgedClient):
352
355
  call: GroupCall = raw_call.call
353
356
  participants: List[GroupCallParticipant] = raw_call.participants
354
357
  for participant in participants:
355
- self._cache.set_participants_cache_chat(
358
+ self._cache.set_participants_cache(
356
359
  chat_id,
357
360
  call.id,
361
+ self.parse_participant_action(participant),
358
362
  self.parse_participant(participant),
359
363
  )
360
364
  if call.schedule_date is not None:
@@ -429,8 +433,10 @@ class HydrogramClient(BridgedClient):
429
433
  ):
430
434
  participants = update.participants
431
435
  for participant in participants:
432
- self._cache.set_participants_cache_call(
436
+ self._cache.set_participants_cache(
437
+ chat_id,
433
438
  update.call.id,
439
+ self.parse_participant_action(participant),
434
440
  self.parse_participant(participant),
435
441
  )
436
442
  if isinstance(update, UpdateGroupCallConnection):
@@ -583,6 +589,19 @@ class HydrogramClient(BridgedClient):
583
589
  ),
584
590
  )
585
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
+
586
605
  async def discard_call(
587
606
  self,
588
607
  chat_id: int,
@@ -734,7 +753,6 @@ class HydrogramClient(BridgedClient):
734
753
  ):
735
754
  if chat_id is not None:
736
755
  dc_id = self._cache.get_dc_call(chat_id)
737
-
738
756
  if dc_id is None:
739
757
  session = self._app
740
758
  else:
@@ -784,12 +802,12 @@ class HydrogramClient(BridgedClient):
784
802
  dc_new = BridgedClient.extract_dc(
785
803
  str(e),
786
804
  )
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
805
  if dc_new is not None:
806
+ if chat_id is not None:
807
+ self._cache.set_dc_call(
808
+ chat_id,
809
+ dc_new,
810
+ )
793
811
  return await self._invoke(
794
812
  request,
795
813
  dc_new,
@@ -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
 
@@ -94,7 +94,7 @@ class PyrogramClient(BridgedClient):
94
94
  self,
95
95
  )
96
96
 
97
- @self._app.on_raw_update(group=-1)
97
+ @self._app.on_raw_update(group=-9999)
98
98
  async def on_update(_, update, __, chats):
99
99
  if isinstance(
100
100
  update,
@@ -186,19 +186,23 @@ class PyrogramClient(BridgedClient):
186
186
  update,
187
187
  UpdateGroupCallParticipants,
188
188
  ):
189
- participants = update.participants
190
- for participant in participants:
191
- result = self._cache.set_participants_cache_call(
192
- update.call.id,
193
- 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,
194
195
  )
195
- if result is not None:
196
- await self._propagate(
197
- UpdatedGroupCallParticipant(
198
- self._cache.get_chat_id(update.call.id),
199
- result,
200
- ),
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,
201
202
  )
203
+ if result is not None:
204
+ await self._propagate(p_update)
205
+
202
206
  if isinstance(
203
207
  update,
204
208
  UpdateGroupCall,
@@ -352,9 +356,10 @@ class PyrogramClient(BridgedClient):
352
356
  call: GroupCall = raw_call.call
353
357
  participants: List[GroupCallParticipant] = raw_call.participants
354
358
  for participant in participants:
355
- self._cache.set_participants_cache_chat(
359
+ self._cache.set_participants_cache(
356
360
  chat_id,
357
361
  call.id,
362
+ self.parse_participant_action(participant),
358
363
  self.parse_participant(participant),
359
364
  )
360
365
  if call.schedule_date is not None:
@@ -429,8 +434,10 @@ class PyrogramClient(BridgedClient):
429
434
  ):
430
435
  participants = update.participants
431
436
  for participant in participants:
432
- self._cache.set_participants_cache_call(
437
+ self._cache.set_participants_cache(
438
+ chat_id,
433
439
  update.call.id,
440
+ self.parse_participant_action(participant),
434
441
  self.parse_participant(participant),
435
442
  )
436
443
  if isinstance(update, UpdateGroupCallConnection):
@@ -583,6 +590,19 @@ class PyrogramClient(BridgedClient):
583
590
  ),
584
591
  )
585
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
+
586
606
  async def discard_call(
587
607
  self,
588
608
  chat_id: int,
@@ -736,7 +756,6 @@ class PyrogramClient(BridgedClient):
736
756
  ):
737
757
  if chat_id is not None:
738
758
  dc_id = self._cache.get_dc_call(chat_id)
739
-
740
759
  if dc_id is None:
741
760
  session = self._app
742
761
  else:
@@ -786,12 +805,12 @@ class PyrogramClient(BridgedClient):
786
805
  dc_new = BridgedClient.extract_dc(
787
806
  str(e),
788
807
  )
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
808
  if dc_new is not None:
809
+ if chat_id is not None:
810
+ self._cache.set_dc_call(
811
+ chat_id,
812
+ dc_new,
813
+ )
795
814
  return await self._invoke(
796
815
  request,
797
816
  dc_new,
@@ -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,19 +180,22 @@ class TelethonClient(BridgedClient):
180
180
  update,
181
181
  UpdateGroupCallParticipants,
182
182
  ):
183
- participants = update.participants
184
- for participant in participants:
185
- result = self._cache.set_participants_cache_call(
186
- update.call.id,
187
- 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,
188
189
  )
189
- if result is not None:
190
- await self._propagate(
191
- UpdatedGroupCallParticipant(
192
- self._cache.get_chat_id(update.call.id),
193
- result,
194
- ),
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,
195
196
  )
197
+ if result is not None:
198
+ await self._propagate(p_update)
196
199
  if isinstance(
197
200
  update,
198
201
  UpdateGroupCall,
@@ -339,9 +342,10 @@ class TelethonClient(BridgedClient):
339
342
  call: GroupCall = raw_call.call
340
343
  participants: List[GroupCallParticipant] = raw_call.participants
341
344
  for participant in participants:
342
- self._cache.set_participants_cache_chat(
345
+ self._cache.set_participants_cache(
343
346
  chat_id,
344
347
  call.id,
348
+ self.parse_participant_action(participant),
345
349
  self.parse_participant(participant),
346
350
  )
347
351
  if call.schedule_date is not None:
@@ -416,8 +420,10 @@ class TelethonClient(BridgedClient):
416
420
  ):
417
421
  participants = update.participants
418
422
  for participant in participants:
419
- self._cache.set_participants_cache_call(
423
+ self._cache.set_participants_cache(
424
+ chat_id,
420
425
  update.call.id,
426
+ self.parse_participant_action(participant),
421
427
  self.parse_participant(participant),
422
428
  )
423
429
  if isinstance(update, UpdateGroupCallConnection):
@@ -570,6 +576,19 @@ class TelethonClient(BridgedClient):
570
576
  ),
571
577
  )
572
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
+
573
592
  async def discard_call(
574
593
  self,
575
594
  chat_id: int,
@@ -714,7 +733,7 @@ class TelethonClient(BridgedClient):
714
733
  def no_updates(self):
715
734
  return False
716
735
 
717
- # noinspection PyProtectedMember
736
+ # noinspection PyProtectedMember,PyUnresolvedReferences
718
737
  async def _invoke(
719
738
  self,
720
739
  request,
@@ -738,12 +757,12 @@ class TelethonClient(BridgedClient):
738
757
  dc_new = BridgedClient.extract_dc(
739
758
  str(e),
740
759
  )
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
760
  if dc_new is not None:
761
+ if chat_id is not None:
762
+ self._cache.set_dc_call(
763
+ chat_id,
764
+ dc_new,
765
+ )
747
766
  return await self._invoke(
748
767
  request,
749
768
  dc_new,
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)
@@ -12,6 +12,7 @@ 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):
@@ -33,8 +34,6 @@ class GroupCallParticipant(PyObject):
33
34
  video_camera: bool,
34
35
  raised_hand: bool,
35
36
  volume: int,
36
- joined: bool,
37
- left: bool,
38
37
  source: int,
39
38
  video_info: Optional[SourceInfo],
40
39
  presentation_info: Optional[SourceInfo],
@@ -48,12 +47,6 @@ class GroupCallParticipant(PyObject):
48
47
  self.video_camera: bool = video_camera
49
48
  self.raised_hand: bool = raised_hand
50
49
  self.volume: int = volume
51
- if joined:
52
- self.action = self.Action.JOINED
53
- elif left:
54
- self.action = self.Action.LEFT
55
- else:
56
- self.action = self.Action.UPDATED
57
50
  self.video_info: Optional[
58
51
  GroupCallParticipant.SourceInfo
59
52
  ] = video_info
@@ -6,7 +6,9 @@ class UpdatedGroupCallParticipant(Update):
6
6
  def __init__(
7
7
  self,
8
8
  chat_id: int,
9
+ action: GroupCallParticipant.Action,
9
10
  participant: GroupCallParticipant,
10
11
  ):
11
12
  super().__init__(chat_id)
13
+ self.action = action
12
14
  self.participant = participant
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,14 @@ 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,
17
+ action: GroupCallParticipant.Action,
18
18
  participant: GroupCallParticipant,
19
- ):
20
- if participant.action == GroupCallParticipant.Action.LEFT:
19
+ ) -> GroupCallParticipant:
20
+ if action == GroupCallParticipant.Action.LEFT:
21
21
  if participant.user_id in self._list_participants:
22
22
  del self._list_participants[participant.user_id]
23
23
  else:
@@ -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)