maxapi-python 1.2.4__py3-none-any.whl → 2.0.0__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.
Files changed (168) hide show
  1. maxapi_python-2.0.0.dist-info/METADATA +217 -0
  2. maxapi_python-2.0.0.dist-info/RECORD +140 -0
  3. {maxapi_python-1.2.4.dist-info → maxapi_python-2.0.0.dist-info}/WHEEL +1 -1
  4. pymax/__init__.py +50 -105
  5. pymax/api/__init__.py +17 -0
  6. pymax/api/auth/__init__.py +1 -0
  7. pymax/api/auth/enums.py +17 -0
  8. pymax/api/auth/payloads.py +129 -0
  9. pymax/api/auth/service.py +313 -0
  10. pymax/api/auth/types.py +13 -0
  11. pymax/api/chats/__init__.py +8 -0
  12. pymax/api/chats/enums.py +27 -0
  13. pymax/api/chats/payloads.py +103 -0
  14. pymax/api/chats/service.py +277 -0
  15. pymax/api/facade.py +32 -0
  16. pymax/api/messages/__init__.py +1 -0
  17. pymax/api/messages/enums.py +17 -0
  18. pymax/api/messages/payloads.py +92 -0
  19. pymax/api/messages/service.py +337 -0
  20. pymax/api/models.py +13 -0
  21. pymax/api/response.py +123 -0
  22. pymax/api/self/__init__.py +2 -0
  23. pymax/api/self/enums.py +11 -0
  24. pymax/api/self/payloads.py +41 -0
  25. pymax/api/self/service.py +142 -0
  26. pymax/api/session/__init__.py +1 -0
  27. pymax/api/session/enums.py +10 -0
  28. pymax/api/session/payloads.py +76 -0
  29. pymax/api/session/service.py +72 -0
  30. pymax/api/uploads/__init__.py +1 -0
  31. pymax/api/uploads/models.py +49 -0
  32. pymax/api/uploads/payloads.py +25 -0
  33. pymax/api/uploads/service.py +458 -0
  34. pymax/api/users/__init__.py +2 -0
  35. pymax/api/users/enums.py +12 -0
  36. pymax/api/users/payloads.py +16 -0
  37. pymax/api/users/service.py +124 -0
  38. pymax/app.py +273 -0
  39. pymax/auth/__init__.py +25 -0
  40. pymax/auth/base.py +37 -0
  41. pymax/auth/email.py +0 -0
  42. pymax/auth/models.py +5 -0
  43. pymax/auth/providers.py +127 -0
  44. pymax/auth/qr.py +135 -0
  45. pymax/auth/service.py +25 -0
  46. pymax/auth/sms.py +122 -0
  47. pymax/base.py +204 -0
  48. pymax/client.py +106 -0
  49. pymax/client_web.py +83 -0
  50. pymax/config.py +215 -0
  51. pymax/connection/__init__.py +1 -0
  52. pymax/connection/connection.py +205 -0
  53. pymax/connection/pending.py +46 -0
  54. pymax/connection/readers/__init__.py +2 -0
  55. pymax/connection/readers/base.py +6 -0
  56. pymax/connection/readers/tcp.py +29 -0
  57. pymax/connection/readers/ws.py +14 -0
  58. pymax/dispatch/__init__.py +10 -0
  59. pymax/dispatch/dispatcher.py +222 -0
  60. pymax/dispatch/enums.py +12 -0
  61. pymax/dispatch/mapping.py +73 -0
  62. pymax/dispatch/resolvers.py +52 -0
  63. pymax/dispatch/router.py +216 -0
  64. pymax/exceptions.py +22 -89
  65. pymax/files/__init__.py +9 -0
  66. pymax/files/base.py +82 -0
  67. pymax/files/file.py +76 -0
  68. pymax/files/photo.py +108 -0
  69. pymax/files/static.py +10 -0
  70. pymax/files/video.py +74 -0
  71. pymax/formatting/__init__.py +0 -0
  72. pymax/formatting/markdown.py +217 -0
  73. pymax/infra/__init__.py +1 -0
  74. pymax/infra/auth.py +55 -0
  75. pymax/infra/base.py +15 -0
  76. pymax/infra/chat.py +240 -0
  77. pymax/infra/message.py +252 -0
  78. pymax/infra/protocol.py +9 -0
  79. pymax/infra/self.py +139 -0
  80. pymax/infra/user.py +107 -0
  81. pymax/logging.py +129 -0
  82. pymax/protocol/__init__.py +11 -0
  83. pymax/protocol/base.py +13 -0
  84. pymax/{static/enum.py → protocol/enums.py} +36 -79
  85. pymax/protocol/models.py +33 -0
  86. pymax/protocol/tcp/__init__.py +1 -0
  87. pymax/protocol/tcp/compression.py +97 -0
  88. pymax/protocol/tcp/framing.py +68 -0
  89. pymax/protocol/tcp/payload.py +127 -0
  90. pymax/protocol/tcp/protocol.py +68 -0
  91. pymax/protocol/ws/__init__.py +1 -0
  92. pymax/protocol/ws/protocol.py +27 -0
  93. pymax/py.typed +0 -0
  94. pymax/routers.py +8 -0
  95. pymax/session/__init__.py +3 -0
  96. pymax/session/models.py +11 -0
  97. pymax/session/protocol.py +14 -0
  98. pymax/session/store.py +232 -0
  99. pymax/telemetry/__init__.py +3 -0
  100. pymax/telemetry/navigation.py +181 -0
  101. pymax/telemetry/payloads.py +142 -0
  102. pymax/telemetry/service.py +225 -0
  103. pymax/transport/__init__.py +0 -0
  104. pymax/transport/base.py +14 -0
  105. pymax/transport/tcp.py +93 -0
  106. pymax/transport/websocket.py +50 -0
  107. pymax/types/__init__.py +2 -0
  108. pymax/types/domain/__init__.py +11 -0
  109. pymax/types/domain/attachments/__init__.py +11 -0
  110. pymax/types/domain/attachments/audio.py +35 -0
  111. pymax/types/domain/attachments/call.py +26 -0
  112. pymax/types/domain/attachments/contact.py +32 -0
  113. pymax/types/domain/attachments/control.py +20 -0
  114. pymax/types/domain/attachments/enums.py +27 -0
  115. pymax/types/domain/attachments/file.py +56 -0
  116. pymax/types/domain/attachments/keyboards/__init__.py +1 -0
  117. pymax/types/domain/attachments/keyboards/inline.py +19 -0
  118. pymax/types/domain/attachments/photo.py +45 -0
  119. pymax/types/domain/attachments/share.py +29 -0
  120. pymax/types/domain/attachments/sticker.py +50 -0
  121. pymax/types/domain/attachments/video.py +90 -0
  122. pymax/types/domain/auth.py +161 -0
  123. pymax/types/domain/base.py +17 -0
  124. pymax/types/domain/chat.py +426 -0
  125. pymax/types/domain/element.py +24 -0
  126. pymax/types/domain/enums.py +24 -0
  127. pymax/types/domain/error.py +20 -0
  128. pymax/types/domain/folder.py +74 -0
  129. pymax/types/domain/login.py +35 -0
  130. pymax/types/domain/message.py +378 -0
  131. pymax/types/domain/name.py +20 -0
  132. pymax/types/domain/profile.py +15 -0
  133. pymax/types/domain/session.py +52 -0
  134. pymax/types/domain/sync.py +80 -0
  135. pymax/types/domain/user.py +117 -0
  136. pymax/types/events/__init__.py +3 -0
  137. pymax/types/events/file.py +5 -0
  138. pymax/types/events/message.py +37 -0
  139. pymax/types/events/video.py +5 -0
  140. maxapi_python-1.2.4.dist-info/METADATA +0 -205
  141. maxapi_python-1.2.4.dist-info/RECORD +0 -33
  142. pymax/core.py +0 -390
  143. pymax/crud.py +0 -96
  144. pymax/files.py +0 -138
  145. pymax/filters.py +0 -164
  146. pymax/formatter.py +0 -31
  147. pymax/formatting.py +0 -74
  148. pymax/interfaces.py +0 -552
  149. pymax/mixins/__init__.py +0 -40
  150. pymax/mixins/auth.py +0 -368
  151. pymax/mixins/channel.py +0 -130
  152. pymax/mixins/group.py +0 -458
  153. pymax/mixins/handler.py +0 -285
  154. pymax/mixins/message.py +0 -879
  155. pymax/mixins/scheduler.py +0 -28
  156. pymax/mixins/self.py +0 -259
  157. pymax/mixins/socket.py +0 -297
  158. pymax/mixins/telemetry.py +0 -112
  159. pymax/mixins/user.py +0 -219
  160. pymax/mixins/websocket.py +0 -142
  161. pymax/models.py +0 -8
  162. pymax/navigation.py +0 -187
  163. pymax/payloads.py +0 -367
  164. pymax/protocols.py +0 -123
  165. pymax/static/constant.py +0 -89
  166. pymax/types.py +0 -1220
  167. pymax/utils.py +0 -90
  168. {maxapi_python-1.2.4.dist-info → maxapi_python-2.0.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,129 @@
1
+ from pydantic import Field, field_serializer
2
+
3
+ from pymax.api.models import CamelModel
4
+ from pymax.api.session.payloads import MobileUserAgentPayload
5
+ from pymax.types.domain.sync import DEFAULT_CONFIG_HASH, ConfigHash, SyncState
6
+
7
+ from .enums import AuthType, Capability
8
+
9
+
10
+ class RequestCodePayload(CamelModel):
11
+ phone: str
12
+ type: AuthType = AuthType.START_AUTH
13
+ language: str = "ru"
14
+
15
+
16
+ class SendCodePayload(CamelModel):
17
+ token: str
18
+ verify_code: str
19
+ auth_token_type: AuthType = AuthType.CHECK_CODE
20
+
21
+
22
+ class CheckPasswordChallengePayload(CamelModel):
23
+ track_id: str
24
+ password: str
25
+
26
+
27
+ class Exp(CamelModel):
28
+ chats_count_groups: bytearray = bytearray.fromhex("0a32")
29
+
30
+
31
+ class WebSyncPayload(CamelModel):
32
+ token: str
33
+ chats_count: int = 40
34
+ interactive: bool = True
35
+ chats_sync: int = -1
36
+ contacts_sync: int = -1
37
+ presence_sync: int = -1
38
+ drafts_sync: int = -1
39
+
40
+ @classmethod
41
+ def from_sync_state(
42
+ cls,
43
+ token: str,
44
+ sync: SyncState,
45
+ ) -> "WebSyncPayload":
46
+ return cls(
47
+ token=token,
48
+ chats_sync=sync.chats_sync,
49
+ contacts_sync=sync.contacts_sync,
50
+ drafts_sync=sync.drafts_sync,
51
+ )
52
+
53
+
54
+ class SyncPayload(CamelModel):
55
+ user_agent: MobileUserAgentPayload
56
+ token: str
57
+ chat_hash_fingerprint: str | None = None
58
+ chats_count: int | None = None
59
+ chats_sync: int = -1
60
+ contacts_sync: int = -1
61
+ drafts_sync: int = -1
62
+ interactive: bool = True
63
+ presence_sync: int = -1
64
+ exp: Exp = Field(default_factory=Exp)
65
+ config_hash: ConfigHash = DEFAULT_CONFIG_HASH
66
+
67
+ @classmethod
68
+ def from_sync_state(
69
+ cls,
70
+ user_agent: MobileUserAgentPayload,
71
+ token: str,
72
+ sync: SyncState,
73
+ ) -> "SyncPayload":
74
+ return cls(
75
+ user_agent=user_agent,
76
+ token=token,
77
+ chats_sync=sync.chats_sync,
78
+ contacts_sync=sync.contacts_sync,
79
+ drafts_sync=sync.drafts_sync,
80
+ presence_sync=sync.presence_sync,
81
+ config_hash=sync.config_hash,
82
+ )
83
+
84
+
85
+ class CheckQrPayload(CamelModel):
86
+ track_id: str
87
+
88
+
89
+ class ConfirmQrPayload(CamelModel):
90
+ track_id: str
91
+
92
+
93
+ class CreateAuthTrackPayload(CamelModel):
94
+ type: int = 0
95
+
96
+
97
+ class SetPasswordPayload(CamelModel):
98
+ track_id: str
99
+ password: str
100
+
101
+
102
+ class RequestEmailCodePayload(CamelModel):
103
+ track_id: str
104
+ email: str
105
+
106
+
107
+ class SendEmailCodePayload(CamelModel):
108
+ track_id: str
109
+ verify_code: str
110
+
111
+
112
+ class SetHintPayload(CamelModel):
113
+ track_id: str
114
+ hint: str
115
+
116
+
117
+ class SetTwoFactorPayload(CamelModel):
118
+ expected_capabilities: list[Capability]
119
+ track_id: str
120
+ password: str
121
+ hint: str | None = None
122
+
123
+
124
+ class RemoveTwoFactorPayload(CamelModel):
125
+ track_id: str
126
+ remove2fa: bool = True
127
+ expected_capabilities: list[Capability] = Field(
128
+ default_factory=lambda: [Capability.REMOVE_2FA]
129
+ )
@@ -0,0 +1,313 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from pymax.api.response import payload_item, payload_keys, require_payload_model
6
+ from pymax.api.session.enums import DeviceType
7
+ from pymax.auth import EmailCodeProvider
8
+ from pymax.auth.providers import ConsoleEmailCodeProvider
9
+ from pymax.logging import get_logger
10
+ from pymax.protocol import Opcode
11
+ from pymax.types.domain.auth import (
12
+ CheckCodeResponse,
13
+ CheckPasswordResponse,
14
+ CheckQrResponse,
15
+ RequestQrResponse,
16
+ StartAuthResponse,
17
+ )
18
+ from pymax.types.domain.login import LoginResponse
19
+
20
+ from .enums import Capability
21
+ from .payloads import (
22
+ CheckPasswordChallengePayload,
23
+ CheckQrPayload,
24
+ ConfirmQrPayload,
25
+ CreateAuthTrackPayload,
26
+ MobileUserAgentPayload,
27
+ RemoveTwoFactorPayload,
28
+ RequestCodePayload,
29
+ RequestEmailCodePayload,
30
+ SendCodePayload,
31
+ SendEmailCodePayload,
32
+ SetHintPayload,
33
+ SetPasswordPayload,
34
+ SetTwoFactorPayload,
35
+ SyncPayload,
36
+ WebSyncPayload,
37
+ )
38
+ from .types import MISSING, Missing
39
+
40
+ if TYPE_CHECKING:
41
+ from pymax.app import App
42
+
43
+
44
+ logger = get_logger(__name__)
45
+
46
+
47
+ class AuthService:
48
+ def __init__(self, app: App) -> None:
49
+ self.app = app
50
+
51
+ async def request_code(self, phone: str) -> StartAuthResponse:
52
+ logger.info("requesting sms code phone_set=%s", bool(phone))
53
+ frame = RequestCodePayload(phone=phone)
54
+ response = await self.app.invoke(Opcode.AUTH_REQUEST, frame.to_payload())
55
+ logger.debug(
56
+ "sms code request accepted payload_keys=%s",
57
+ payload_keys(response),
58
+ )
59
+ return require_payload_model(response, StartAuthResponse)
60
+
61
+ async def send_code(self, token: str, verify_code: str) -> CheckCodeResponse:
62
+ logger.info(
63
+ "sending sms code token_set=%s code_set=%s",
64
+ bool(token),
65
+ bool(verify_code),
66
+ )
67
+ frame = SendCodePayload(token=token, verify_code=verify_code)
68
+ response = await self.app.invoke(Opcode.AUTH, frame.to_payload())
69
+ logger.debug(
70
+ "sms code response payload_keys=%s",
71
+ payload_keys(response),
72
+ )
73
+ return require_payload_model(response, CheckCodeResponse)
74
+
75
+ async def check_password(
76
+ self,
77
+ track_id: str,
78
+ password: str,
79
+ ) -> CheckPasswordResponse:
80
+ logger.info(
81
+ "checking 2fa password track_id_set=%s password_set=%s",
82
+ bool(track_id),
83
+ bool(password),
84
+ )
85
+ frame = CheckPasswordChallengePayload(
86
+ track_id=track_id,
87
+ password=password,
88
+ )
89
+ response = await self.app.invoke(
90
+ Opcode.AUTH_LOGIN_CHECK_PASSWORD,
91
+ frame.to_payload(),
92
+ )
93
+ logger.debug(
94
+ "2fa password response payload_keys=%s",
95
+ payload_keys(response),
96
+ )
97
+ return require_payload_model(response, CheckPasswordResponse)
98
+
99
+ async def login(self, user_agent: MobileUserAgentPayload) -> LoginResponse:
100
+ if user_agent.device_type == DeviceType.WEB:
101
+ return await self.web_login()
102
+
103
+ return await self.mobile_login()
104
+
105
+ async def mobile_login(self) -> LoginResponse:
106
+ session = self.app.session
107
+ if session is None:
108
+ logger.error("login requested without session")
109
+ raise RuntimeError("No session available for login")
110
+
111
+ logger.info("logging in")
112
+ sync = self.app.config.sync.resolve(session.sync)
113
+ frame = SyncPayload.from_sync_state(
114
+ user_agent=self.app.config.device.user_agent,
115
+ token=session.token,
116
+ sync=sync,
117
+ )
118
+ response = await self.app.invoke(Opcode.LOGIN, frame.to_payload())
119
+
120
+ logger.debug("login response payload_keys=%s", payload_keys(response))
121
+
122
+ login_response = require_payload_model(response, LoginResponse)
123
+ await self._update_session(login_response)
124
+ return login_response
125
+
126
+ async def web_login(self) -> LoginResponse:
127
+ session = self.app.session
128
+ if session is None:
129
+ logger.error("login requested without session")
130
+ raise RuntimeError("No session available for login")
131
+
132
+ logger.info("logging in")
133
+ sync = self.app.config.sync.resolve(session.sync)
134
+
135
+ frame = WebSyncPayload.from_sync_state(
136
+ token=session.token,
137
+ sync=sync,
138
+ )
139
+ response = await self.app.invoke(Opcode.LOGIN, frame.to_payload())
140
+
141
+ logger.debug("login response payload_keys=%s", payload_keys(response))
142
+
143
+ login_response = require_payload_model(response, LoginResponse)
144
+ await self._update_session(login_response)
145
+ return login_response
146
+
147
+ async def request_qr(self) -> RequestQrResponse:
148
+ response = await self.app.invoke(Opcode.GET_QR, {})
149
+
150
+ return require_payload_model(response, RequestQrResponse)
151
+
152
+ async def check_qr(self, track_id: str) -> CheckQrResponse:
153
+ frame = CheckQrPayload(track_id=track_id)
154
+
155
+ response = await self.app.invoke(Opcode.GET_QR_STATUS, frame.to_payload())
156
+
157
+ return require_payload_model(response, CheckQrResponse)
158
+
159
+ async def confirm_qr(self, track_id: str) -> CheckCodeResponse:
160
+ frame = ConfirmQrPayload(track_id=track_id)
161
+
162
+ response = await self.app.invoke(Opcode.LOGIN_BY_QR, frame.to_payload())
163
+
164
+ return require_payload_model(response, CheckCodeResponse)
165
+
166
+ async def _update_session(self, response: LoginResponse) -> None:
167
+ session = self.app.session
168
+ if session is None:
169
+ return
170
+
171
+ sync = response.update_sync_state(session.sync)
172
+ updated = session.model_copy(
173
+ update={
174
+ "mt_instance_id": self.app.config.device.mt_instance_id,
175
+ "sync": sync,
176
+ },
177
+ )
178
+ self.app.session = updated
179
+ await self.app.store.save_session(updated)
180
+
181
+ async def _get_track_id(self) -> str | None:
182
+ logger.debug("creating auth track")
183
+ frame = CreateAuthTrackPayload()
184
+
185
+ response = await self.app.invoke(Opcode.AUTH_CREATE_TRACK, frame.to_payload())
186
+
187
+ return payload_item(response, "trackId", str)
188
+
189
+ async def _set_email(self, track_id: str, email: str, provider: EmailCodeProvider) -> bool:
190
+ logger.info("setting 2fa email email_set=%s", bool(email))
191
+
192
+ frame = RequestEmailCodePayload(
193
+ track_id=track_id,
194
+ email=email,
195
+ )
196
+
197
+ await self.app.invoke(Opcode.AUTH_VERIFY_EMAIL, frame.to_payload())
198
+
199
+ code = await provider.get_code(email)
200
+
201
+ frame = SendEmailCodePayload(
202
+ track_id=track_id,
203
+ verify_code=code,
204
+ )
205
+
206
+ await self.app.invoke(Opcode.AUTH_CHECK_EMAIL, frame.to_payload())
207
+
208
+ return True
209
+
210
+ async def _set_hint(self, track_id: str, hint: str) -> bool:
211
+ logger.info("setting 2fa hint hint_set=%s", bool(hint))
212
+
213
+ frame = SetHintPayload(
214
+ track_id=track_id,
215
+ hint=hint,
216
+ )
217
+ await self.app.invoke(Opcode.AUTH_VALIDATE_HINT, frame.to_payload())
218
+
219
+ return True
220
+
221
+ async def _set_password(self, track_id: str, password: str) -> bool:
222
+ logger.info("setting 2fa password password_set=%s", bool(password))
223
+
224
+ frame = SetPasswordPayload(
225
+ track_id=track_id,
226
+ password=password,
227
+ )
228
+ await self.app.invoke(Opcode.AUTH_VALIDATE_PASSWORD, frame.to_payload())
229
+
230
+ return True
231
+
232
+ async def set_2fa(
233
+ self,
234
+ password: str,
235
+ email: str | Missing = MISSING,
236
+ hint: str | Missing = MISSING,
237
+ email_code_provider: EmailCodeProvider | None = None,
238
+ ) -> bool:
239
+ logger.info(
240
+ "setting 2fa password password_set=%s email_set=%s hint_set=%s",
241
+ bool(password),
242
+ bool(email),
243
+ bool(hint),
244
+ )
245
+
246
+ track_id = await self._get_track_id()
247
+
248
+ if track_id is None:
249
+ logger.error("missing track_id in auth create track response")
250
+ raise RuntimeError("Failed to create auth track")
251
+
252
+ has_hint = False
253
+ has_email = False
254
+
255
+ await self._set_password(track_id, password)
256
+
257
+ if email is not MISSING:
258
+ provider = email_code_provider or ConsoleEmailCodeProvider()
259
+ await self._set_email(track_id, str(email), provider)
260
+ has_email = True
261
+
262
+ if hint is not MISSING:
263
+ await self._set_hint(track_id, str(hint))
264
+ has_hint = True
265
+
266
+ expected_capabilities = [Capability.DEFAULT]
267
+
268
+ if has_hint:
269
+ expected_capabilities.append(Capability.SECOND_FACTOR_HAS_HINT)
270
+
271
+ if has_email:
272
+ expected_capabilities.append(Capability.SECOND_FACTOR_HAS_EMAIL)
273
+
274
+ frame = SetTwoFactorPayload(
275
+ track_id=track_id,
276
+ password=password,
277
+ hint=str(hint) if has_hint else None,
278
+ expected_capabilities=expected_capabilities,
279
+ )
280
+
281
+ await self.app.invoke(Opcode.AUTH_SET_2FA, frame.to_payload())
282
+ logger.info("2fa password set successfully")
283
+ return True
284
+
285
+ async def _check_2fa_password(self, track_id: str, password: str) -> bool:
286
+ logger.info("entering 2fa password password_set=%s", bool(password))
287
+
288
+ frame = SetPasswordPayload(
289
+ track_id=track_id,
290
+ password=password,
291
+ )
292
+ await self.app.invoke(Opcode.AUTH_CHECK_PASSWORD, frame.to_payload())
293
+
294
+ return True
295
+
296
+ async def remove_2fa(self, password: str) -> bool:
297
+ logger.info("removing 2fa password_set=%s", bool(password))
298
+
299
+ track_id = await self._get_track_id()
300
+
301
+ if track_id is None:
302
+ logger.error("missing track_id in auth create track response")
303
+ raise RuntimeError("Failed to create auth track")
304
+
305
+ await self._check_2fa_password(track_id, password)
306
+
307
+ frame = RemoveTwoFactorPayload(
308
+ track_id=track_id,
309
+ )
310
+
311
+ await self.app.invoke(Opcode.AUTH_SET_2FA, frame.to_payload())
312
+
313
+ return True
@@ -0,0 +1,13 @@
1
+ from typing import Final, TypeAlias
2
+
3
+
4
+ class MissingType:
5
+ __slots__ = ()
6
+
7
+ def __repr__(self) -> str:
8
+ return "MISSING"
9
+
10
+
11
+ MISSING: Final = MissingType()
12
+
13
+ Missing: TypeAlias = MissingType
@@ -0,0 +1,8 @@
1
+ from .enums import (
2
+ ChatLinkPrefix,
3
+ ChatMemberOperation,
4
+ ChatOption,
5
+ ChatPayloadKey,
6
+ ControlEvent,
7
+ )
8
+ from .service import ChatService
@@ -0,0 +1,27 @@
1
+ from enum import Enum
2
+
3
+
4
+ class ControlEvent(str, Enum):
5
+ NEW = "new"
6
+
7
+
8
+ class ChatMemberOperation(str, Enum):
9
+ ADD = "add"
10
+ REMOVE = "remove"
11
+
12
+
13
+ class ChatOption(str, Enum):
14
+ ONLY_OWNER_CAN_CHANGE_ICON_TITLE = "ONLY_OWNER_CAN_CHANGE_ICON_TITLE"
15
+ ALL_CAN_PIN_MESSAGE = "ALL_CAN_PIN_MESSAGE"
16
+ ONLY_ADMIN_CAN_ADD_MEMBER = "ONLY_ADMIN_CAN_ADD_MEMBER"
17
+ ONLY_ADMIN_CAN_CALL = "ONLY_ADMIN_CAN_CALL"
18
+ MEMBERS_CAN_SEE_PRIVATE_LINK = "MEMBERS_CAN_SEE_PRIVATE_LINK"
19
+
20
+
21
+ class ChatPayloadKey(str, Enum):
22
+ CHAT = "chat"
23
+ CHATS = "chats"
24
+
25
+
26
+ class ChatLinkPrefix(str, Enum):
27
+ JOIN = "join/"
@@ -0,0 +1,103 @@
1
+ from typing import Literal
2
+
3
+ from pydantic import Field
4
+
5
+ from pymax.api.models import CamelModel
6
+ from pymax.types.domain.attachments.enums import AttachmentType
7
+ from pymax.types.domain.enums import ChatType
8
+
9
+ from .enums import ChatMemberOperation, ChatOption, ControlEvent
10
+
11
+
12
+ class CreateGroupAttach(CamelModel):
13
+ type: Literal[AttachmentType.CONTROL] = Field(
14
+ default=AttachmentType.CONTROL,
15
+ alias="_type",
16
+ )
17
+ event: ControlEvent = ControlEvent.NEW
18
+ chat_type: Literal[ChatType.CHAT] = ChatType.CHAT
19
+ title: str
20
+ user_ids: list[int]
21
+
22
+
23
+ class CreateGroupMessage(CamelModel):
24
+ cid: int
25
+ attaches: list[CreateGroupAttach]
26
+
27
+
28
+ class CreateGroupPayload(CamelModel):
29
+ message: CreateGroupMessage
30
+ notify: bool = True
31
+
32
+
33
+ class InviteUsersPayload(CamelModel):
34
+ chat_id: int
35
+ user_ids: list[int]
36
+ show_history: bool
37
+ operation: ChatMemberOperation = ChatMemberOperation.ADD
38
+
39
+
40
+ class RemoveUsersPayload(CamelModel):
41
+ chat_id: int
42
+ user_ids: list[int]
43
+ operation: ChatMemberOperation = ChatMemberOperation.REMOVE
44
+ clean_msg_period: int
45
+
46
+
47
+ class ChangeGroupSettingsOptions(CamelModel):
48
+ only_owner_can_change_icon_title: bool | None = Field(
49
+ default=None,
50
+ serialization_alias=ChatOption.ONLY_OWNER_CAN_CHANGE_ICON_TITLE.value,
51
+ )
52
+ all_can_pin_message: bool | None = Field(
53
+ default=None,
54
+ serialization_alias=ChatOption.ALL_CAN_PIN_MESSAGE.value,
55
+ )
56
+ only_admin_can_add_member: bool | None = Field(
57
+ default=None,
58
+ serialization_alias=ChatOption.ONLY_ADMIN_CAN_ADD_MEMBER.value,
59
+ )
60
+ only_admin_can_call: bool | None = Field(
61
+ default=None,
62
+ serialization_alias=ChatOption.ONLY_ADMIN_CAN_CALL.value,
63
+ )
64
+ members_can_see_private_link: bool | None = Field(
65
+ default=None,
66
+ serialization_alias=ChatOption.MEMBERS_CAN_SEE_PRIVATE_LINK.value,
67
+ )
68
+
69
+
70
+ class ChangeGroupSettingsPayload(CamelModel):
71
+ chat_id: int
72
+ options: ChangeGroupSettingsOptions
73
+
74
+
75
+ class ChangeGroupProfilePayload(CamelModel):
76
+ chat_id: int
77
+ theme: str | None
78
+ description: str | None = None
79
+
80
+
81
+ class JoinChatPayload(CamelModel):
82
+ link: str
83
+
84
+
85
+ class LinkInfoPayload(CamelModel):
86
+ link: str
87
+
88
+
89
+ class ReworkInviteLinkPayload(CamelModel):
90
+ revoke_private_link: bool = True
91
+ chat_id: int
92
+
93
+
94
+ class GetChatInfoPayload(CamelModel):
95
+ chat_ids: list[int]
96
+
97
+
98
+ class LeaveChatPayload(CamelModel):
99
+ chat_id: int
100
+
101
+
102
+ class FetchChatsPayload(CamelModel):
103
+ marker: int