maxapi-python 1.1.20__py3-none-any.whl → 1.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.
pymax/mixins/user.py CHANGED
@@ -15,13 +15,15 @@ from pymax.types import Contact, Session, User
15
15
  class UserMixin(ClientProtocol):
16
16
  def get_cached_user(self, user_id: int) -> User | None:
17
17
  """
18
- Получает юзера из кеша по его ID
18
+ Получает пользователя из кеша по его идентификатору.
19
19
 
20
- Args:
21
- user_id (int): ID пользователя.
20
+ Проверяет внутренний кеш пользователей и возвращает объект User
21
+ если пользователь был ранее загружен.
22
22
 
23
- Returns:
24
- User | None: Объект User или None при ошибке.
23
+ :param user_id: Идентификатор пользователя.
24
+ :type user_id: int
25
+ :return: Объект User из кеша или None, если пользователь не найден.
26
+ :rtype: User | None
25
27
  """
26
28
  user = self._users.get(user_id)
27
29
  self.logger.debug("get_cached_user id=%s hit=%s", user_id, bool(user))
@@ -29,7 +31,16 @@ class UserMixin(ClientProtocol):
29
31
 
30
32
  async def get_users(self, user_ids: list[int]) -> list[User]:
31
33
  """
32
- Получает информацию о пользователях по их ID (с кешем).
34
+ Получает информацию о пользователях по их идентификаторам.
35
+
36
+ Метод использует внутренний кеш для избежания повторных запросов.
37
+ Если пользователь уже загружен, берется из кеша, иначе выполняется
38
+ сетевой запрос к серверу.
39
+
40
+ :param user_ids: Список идентификаторов пользователей.
41
+ :type user_ids: list[int]
42
+ :return: Список объектов User в порядке, соответствующем входному списку.
43
+ :rtype: list[User]
33
44
  """
34
45
  self.logger.debug("get_users ids=%s", user_ids)
35
46
  cached = {uid: self._users[uid] for uid in user_ids if uid in self._users}
@@ -49,7 +60,15 @@ class UserMixin(ClientProtocol):
49
60
 
50
61
  async def get_user(self, user_id: int) -> User | None:
51
62
  """
52
- Получает информацию о пользователе по его ID (с кешем).
63
+ Получает информацию о пользователе по его идентификатору.
64
+
65
+ Метод использует внутренний кеш. Если пользователь уже загружен,
66
+ возвращает его из кеша, иначе выполняет запрос к серверу.
67
+
68
+ :param user_id: Идентификатор пользователя.
69
+ :type user_id: int
70
+ :return: Объект User или None, если пользователь не найден.
71
+ :rtype: User | None
53
72
  """
54
73
  self.logger.debug("get_user id=%s", user_id)
55
74
  if user_id in self._users:
@@ -63,7 +82,15 @@ class UserMixin(ClientProtocol):
63
82
 
64
83
  async def fetch_users(self, user_ids: list[int]) -> list[User]:
65
84
  """
66
- Получает информацию о пользователях по их ID.
85
+ Загружает информацию о пользователях с сервера.
86
+
87
+ Запрашивает данные о пользователях по их идентификаторам и добавляет
88
+ их в внутренний кеш.
89
+
90
+ :param user_ids: Список идентификаторов пользователей для загрузки.
91
+ :type user_ids: list[int]
92
+ :return: Список загруженных объектов User.
93
+ :rtype: list[User]
67
94
  """
68
95
  self.logger.info("Fetching users count=%d", len(user_ids))
69
96
 
@@ -83,13 +110,13 @@ class UserMixin(ClientProtocol):
83
110
 
84
111
  async def search_by_phone(self, phone: str) -> User:
85
112
  """
86
- Ищет пользователя по номеру телефона.
113
+ Выполняет поиск пользователя по номеру телефона.
87
114
 
88
- Args:
89
- phone (str): Номер телефона.
90
-
91
- Returns:
92
- User: Объект User.
115
+ :param phone: Номер телефона пользователя.
116
+ :type phone: str
117
+ :return: Объект User с найденными данными пользователя.
118
+ :rtype: User
119
+ :raises Error: Если пользователь не найден или произошла ошибка.
93
120
  """
94
121
  self.logger.info("Searching user by phone: %s", phone)
95
122
 
@@ -115,10 +142,13 @@ class UserMixin(ClientProtocol):
115
142
 
116
143
  async def get_sessions(self) -> list[Session]:
117
144
  """
118
- Получает информацию о сессиях.
145
+ Получает информацию о всех активных сессиях пользователя.
146
+
147
+ Возвращает список всех сессий, в которых авторизован пользователь.
119
148
 
120
- Returns:
121
- list[Session]: Список объектов Session.
149
+ :return: Список объектов Session.
150
+ :rtype: list[Session]
151
+ :raises Error: Если произошла ошибка при получении данных.
122
152
  """
123
153
  self.logger.info("Fetching sessions")
124
154
 
@@ -133,15 +163,6 @@ class UserMixin(ClientProtocol):
133
163
  return [Session.from_dict(s) for s in data["payload"].get("sessions", [])]
134
164
 
135
165
  async def _contact_action(self, payload: ContactActionPayload) -> dict[str, Any]:
136
- """
137
- Действия с контактом
138
-
139
- Args:
140
- payload (ContactActionPayload): Полезная нагрузка
141
-
142
- Return:
143
- Полезная нагрузка ответа
144
- """
145
166
  data = await self._send_and_wait(
146
167
  opcode=Opcode.CONTACT_UPDATE, # 34
147
168
  payload=payload.model_dump(by_alias=True),
@@ -157,11 +178,11 @@ class UserMixin(ClientProtocol):
157
178
  """
158
179
  Добавляет контакт в список контактов
159
180
 
160
- Args:
161
- contact_id (int): ID контакта
162
-
163
- Returns:
164
- Contact: Объект контакта, иначе будут выброшены исключения
181
+ :param contact_id: ID контакта
182
+ :type contact_id: int
183
+ :return: Объект контакта
184
+ :rtype: Contact
185
+ :raises ResponseStructureError: Если структура ответа неверна
165
186
  """
166
187
  payload = await self._contact_action(
167
188
  ContactActionPayload(contact_id=contact_id, action=ContactAction.ADD)
@@ -175,11 +196,11 @@ class UserMixin(ClientProtocol):
175
196
  """
176
197
  Удаляет контакт из списка контактов
177
198
 
178
- Args:
179
- contact_id (int): ID контакта
180
-
181
- Returns:
182
- True если успешно, иначе будут выброшены исключения
199
+ :param contact_id: ID контакта
200
+ :type contact_id: int
201
+ :return: True если успешно
202
+ :rtype: Literal[True]
203
+ :raises ResponseStructureError: Если структура ответа неверна
183
204
  """
184
205
  await self._contact_action(
185
206
  ContactActionPayload(contact_id=contact_id, action=ContactAction.REMOVE)
@@ -190,11 +211,11 @@ class UserMixin(ClientProtocol):
190
211
  """
191
212
  Получение айди лс (диалога)
192
213
 
193
- Args:
194
- first_user_id (int): ID первого пользователя
195
- second_user_id (int): ID второго пользователя
196
-
197
- Returns:
198
- int: Айди диалога
214
+ :param first_user_id: ID первого пользователя
215
+ :type first_user_id: int
216
+ :param second_user_id: ID второго пользователя
217
+ :type second_user_id: int
218
+ :return: Айди диалога
219
+ :rtype: int
199
220
  """
200
221
  return first_user_id ^ second_user_id
pymax/mixins/websocket.py CHANGED
@@ -8,7 +8,7 @@ import websockets
8
8
  from typing_extensions import override
9
9
 
10
10
  from pymax.exceptions import LoginError, WebSocketNotConnectedError
11
- from pymax.filters import Filter
11
+ from pymax.filters import BaseFilter
12
12
  from pymax.interfaces import ClientProtocol
13
13
  from pymax.mixins.utils import MixinsUtils
14
14
  from pymax.payloads import BaseWebSocketMessage, SyncPayload, UserAgentPayload
@@ -73,6 +73,11 @@ class WebSocketMixin(ClientProtocol):
73
73
  ) -> dict[str, Any] | None:
74
74
  """
75
75
  Устанавливает соединение WebSocket с сервером и выполняет handshake.
76
+
77
+ :param user_agent: Пользовательский агент для handshake. Если None, используется значение по умолчанию.
78
+ :type user_agent: UserAgentPayload | None
79
+ :return: Результат handshake.
80
+ :rtype: dict[str, Any] | None
76
81
  """
77
82
  if user_agent is None:
78
83
  user_agent = UserAgentPayload()
@@ -101,14 +106,13 @@ class WebSocketMixin(ClientProtocol):
101
106
  async def _handshake(self, user_agent: UserAgentPayload) -> dict[str, Any]:
102
107
  self.logger.debug(
103
108
  "Sending handshake with user_agent keys=%s",
104
- user_agent.model_dump().keys(),
109
+ user_agent.model_dump(by_alias=True).keys(),
105
110
  )
111
+
112
+ user_agent_json = user_agent.model_dump(by_alias=True)
106
113
  resp = await self._send_and_wait(
107
114
  opcode=Opcode.SESSION_INIT,
108
- payload={
109
- "deviceId": str(self._device_id),
110
- "userAgent": user_agent, # TODO: вынести в статик мб
111
- },
115
+ payload={"deviceId": str(self._device_id), "userAgent": user_agent_json},
112
116
  )
113
117
 
114
118
  if resp.get("payload", {}).get("error"):
@@ -120,12 +124,12 @@ class WebSocketMixin(ClientProtocol):
120
124
  async def _process_message_handler(
121
125
  self,
122
126
  handler: Callable[[Message], Any],
123
- filter: Filter | None,
127
+ filter: BaseFilter[Message] | None,
124
128
  message: Message,
125
129
  ):
126
130
  result = None
127
131
  if filter:
128
- if filter.match(message):
132
+ if filter(message):
129
133
  result = handler(message)
130
134
  else:
131
135
  return
@@ -134,6 +138,126 @@ class WebSocketMixin(ClientProtocol):
134
138
  if asyncio.iscoroutine(result):
135
139
  self._create_safe_task(result, name=f"handler-{handler.__name__}")
136
140
 
141
+ def _parse_json(self, raw: Any) -> dict[str, Any] | None:
142
+ try:
143
+ return json.loads(raw)
144
+ except Exception:
145
+ self.logger.warning("JSON parse error", exc_info=True)
146
+ return None
147
+
148
+ def _handle_pending(self, seq: int | None, data: dict) -> bool:
149
+ if isinstance(seq, int):
150
+ fut = self._pending.get(seq)
151
+ if fut and not fut.done():
152
+ fut.set_result(data)
153
+ self.logger.debug("Matched response for pending seq=%s", seq)
154
+ return True
155
+ return False
156
+
157
+ async def _handle_incoming_queue(self, data: dict[str, Any]) -> None:
158
+ if self._incoming:
159
+ try:
160
+ self._incoming.put_nowait(data)
161
+ except asyncio.QueueFull:
162
+ self.logger.warning(
163
+ "Incoming queue full; dropping message seq=%s", data.get("seq")
164
+ )
165
+
166
+ async def _handle_file_upload(self, data: dict[str, Any]) -> None:
167
+ if data.get("opcode") != Opcode.NOTIF_ATTACH:
168
+ return
169
+ payload = data.get("payload", {})
170
+ for key in ("fileId", "videoId"):
171
+ id_ = payload.get(key)
172
+ if id_ is not None:
173
+ fut = self._file_upload_waiters.pop(id_, None)
174
+ if fut and not fut.done():
175
+ fut.set_result(data)
176
+ self.logger.debug(
177
+ "Fulfilled file upload waiter for %s=%s", key, id_
178
+ )
179
+
180
+ async def _handle_message_notifications(self, data: dict) -> None:
181
+ if data.get("opcode") != Opcode.NOTIF_MESSAGE.value:
182
+ return
183
+ payload = data.get("payload", {})
184
+ msg = Message.from_dict(payload)
185
+ if not msg:
186
+ return
187
+ handlers_map = {
188
+ MessageStatus.EDITED: self._on_message_edit_handlers,
189
+ MessageStatus.REMOVED: self._on_message_delete_handlers,
190
+ }
191
+ if msg.status and msg.status in handlers_map:
192
+ for handler, filter in handlers_map[msg.status]:
193
+ await self._process_message_handler(handler, filter, msg)
194
+ if msg.status is None:
195
+ for handler, filter in self._on_message_handlers:
196
+ await self._process_message_handler(handler, filter, msg)
197
+
198
+ async def _handle_reactions(self, data: dict):
199
+ if data.get("opcode") != Opcode.NOTIF_MSG_REACTIONS_CHANGED:
200
+ return
201
+
202
+ payload = data.get("payload", {})
203
+ chat_id = payload.get("chatId")
204
+ message_id = payload.get("messageId")
205
+
206
+ if not (chat_id and message_id):
207
+ return
208
+
209
+ total_count = payload.get("totalCount")
210
+ your_reaction = payload.get("yourReaction")
211
+ counters = [ReactionCounter.from_dict(c) for c in payload.get("counters", [])]
212
+
213
+ reaction_info = ReactionInfo(
214
+ total_count=total_count,
215
+ your_reaction=your_reaction,
216
+ counters=counters,
217
+ )
218
+
219
+ for handler in self._on_reaction_change_handlers:
220
+ try:
221
+ result = handler(message_id, chat_id, reaction_info)
222
+ if asyncio.iscoroutine(result):
223
+ await result
224
+ except Exception as e:
225
+ self.logger.exception("Error in on_reaction_change_handler: %s", e)
226
+
227
+ async def _handle_chat_updates(self, data: dict) -> None:
228
+ if data.get("opcode") != Opcode.NOTIF_CHAT:
229
+ return
230
+
231
+ payload = data.get("payload", {})
232
+ chat_data = payload.get("chat", {})
233
+ chat = Chat.from_dict(chat_data)
234
+ if not chat:
235
+ return
236
+
237
+ for handler in self._on_chat_update_handlers:
238
+ try:
239
+ result = handler(chat)
240
+ if asyncio.iscoroutine(result):
241
+ await result
242
+ except Exception as e:
243
+ self.logger.exception("Error in on_chat_update_handler: %s", e)
244
+
245
+ async def _handle_raw_receive(self, data: dict[str, Any]) -> None:
246
+ for handler in self._on_raw_receive_handlers:
247
+ try:
248
+ result = handler(data)
249
+ if asyncio.iscoroutine(result):
250
+ await result
251
+ except Exception as e:
252
+ self.logger.exception("Error in on_raw_receive_handler: %s", e)
253
+
254
+ async def _dispatch_incoming(self, data: dict[str, Any]) -> None:
255
+ await self._handle_raw_receive(data)
256
+ await self._handle_file_upload(data)
257
+ await self._handle_message_notifications(data)
258
+ await self._handle_reactions(data)
259
+ await self._handle_chat_updates(data)
260
+
137
261
  async def _recv_loop(self) -> None:
138
262
  if self._ws is None:
139
263
  self.logger.warning("Recv loop started without websocket instance")
@@ -143,145 +267,22 @@ class WebSocketMixin(ClientProtocol):
143
267
  while True:
144
268
  try:
145
269
  raw = await self._ws.recv()
146
- try:
147
- data = json.loads(raw)
148
- except Exception:
149
- self.logger.warning("JSON parse error", exc_info=True)
270
+ data = self._parse_json(raw)
271
+
272
+ if data is None:
150
273
  continue
274
+
151
275
  seq = data.get("seq")
152
- fut = self._pending.get(seq) if isinstance(seq, int) else None
276
+ if self._handle_pending(seq, data):
277
+ continue
153
278
 
154
- if fut and not fut.done():
155
- fut.set_result(data)
156
- self.logger.debug("Matched response for pending seq=%s", seq)
157
- else:
158
- if self._incoming is not None:
159
- try:
160
- self._incoming.put_nowait(data)
161
- except asyncio.QueueFull:
162
- self.logger.warning(
163
- "Incoming queue full; dropping message seq=%s",
164
- data.get("seq"),
165
- )
166
-
167
- try: # TODO: переделать, временное решение
168
- if data.get("opcode") == Opcode.NOTIF_ATTACH:
169
- file_id = data.get("payload", {}).get("fileId", None)
170
- video_id = data.get("payload", {}).get("videoId", None)
171
- if file_id is not None:
172
- fut = self._file_upload_waiters.pop(file_id, None)
173
- if fut and not fut.done():
174
- fut.set_result(data)
175
- self.logger.debug(
176
- "Fulfilled file upload waiter for fileId=%s",
177
- file_id,
178
- )
179
- elif video_id is not None:
180
- fut = self._file_upload_waiters.pop(video_id, None)
181
- if fut and not fut.done():
182
- fut.set_result(data)
183
- self.logger.debug(
184
- "Fulfilled file upload waiter for videoId=%s",
185
- video_id,
186
- )
187
- except Exception:
188
- self.logger.exception("Error handling file upload notification")
189
-
190
- if data.get("opcode") == Opcode.NOTIF_MESSAGE.value and (
191
- self._on_message_handlers
192
- or self._on_message_edit_handlers
193
- or self._on_message_delete_handlers
194
- ):
195
- try:
196
- for handler, filter in self._on_message_handlers:
197
- payload = data.get("payload", {})
198
- msg = Message.from_dict(payload)
199
- if msg:
200
- if msg.status:
201
- if msg.status == MessageStatus.EDITED:
202
- for (
203
- edit_handler,
204
- edit_filter,
205
- ) in self._on_message_edit_handlers:
206
- await self._process_message_handler(
207
- edit_handler,
208
- edit_filter,
209
- msg,
210
- )
211
- elif msg.status == MessageStatus.REMOVED:
212
- for (
213
- remove_handler,
214
- remove_filter,
215
- ) in self._on_message_delete_handlers:
216
- await self._process_message_handler(
217
- remove_handler,
218
- remove_filter,
219
- msg,
220
- )
221
- await self._process_message_handler(
222
- handler, filter, msg
223
- )
224
- except Exception:
225
- self.logger.exception("Error in on_message_handler")
226
-
227
- if data.get("opcode") == Opcode.NOTIF_MSG_REACTIONS_CHANGED:
228
- try:
229
- for (
230
- reaction_handler,
231
- ) in self._on_reaction_change_handlers:
232
- payload = data.get("payload", {})
233
-
234
- chat_id = payload.get("chatId")
235
- message_id = payload.get("messageId")
236
-
237
- total_count = payload.get("totalCount")
238
- your_reaction = payload.get("yourReaction")
239
- counters = [
240
- ReactionCounter.from_dict(c)
241
- for c in payload.get("counters", [])
242
- ]
243
-
244
- if (
245
- chat_id
246
- and message_id
247
- and (
248
- total_count is not None
249
- or your_reaction
250
- or counters
251
- )
252
- ):
253
- reaction_info = ReactionInfo(
254
- total_count=total_count,
255
- your_reaction=your_reaction,
256
- counters=counters,
257
- )
258
- result = reaction_handler(
259
- message_id, chat_id, reaction_info
260
- )
261
- if asyncio.iscoroutine(result):
262
- await result
263
-
264
- except Exception as e:
265
- self.logger.exception(
266
- "Error in on_reaction_change_handler: %s", e
267
- )
268
-
269
- if data.get("opcode") == Opcode.NOTIF_CHAT:
270
- try:
271
- for (chat_update_handler,) in self._on_chat_update_handlers:
272
- payload = data.get("payload", {})
273
- chat = Chat.from_dict(payload.get("chat", {}))
274
- if chat:
275
- result = chat_update_handler(chat)
276
- if asyncio.iscoroutine(result):
277
- await result
278
- except Exception as e:
279
- self.logger.exception(
280
- "Error in on_chat_update_handler: %s", e
281
- )
282
-
283
- except websockets.exceptions.ConnectionClosed:
284
- self.logger.info("WebSocket connection closed; exiting recv loop")
279
+ await self._handle_incoming_queue(data)
280
+ await self._dispatch_incoming(data)
281
+
282
+ except websockets.exceptions.ConnectionClosed as e:
283
+ self.logger.info(
284
+ f"WebSocket connection closed with error: {e.code}, {e.reason}; exiting recv loop"
285
+ )
285
286
  for fut in self._pending.values():
286
287
  if not fut.done():
287
288
  fut.set_exception(WebSocketNotConnectedError)
@@ -303,7 +304,6 @@ class WebSocketMixin(ClientProtocol):
303
304
  pass
304
305
  except Exception as e:
305
306
  self.logger.exception("Error retrieving task exception: %s", e)
306
- pass
307
307
 
308
308
  async def _queue_message(
309
309
  self,
pymax/payloads.py CHANGED
@@ -4,6 +4,8 @@ from pydantic import AliasChoices, BaseModel, Field
4
4
 
5
5
  from pymax.static.constant import (
6
6
  DEFAULT_APP_VERSION,
7
+ DEFAULT_BUILD_NUMBER,
8
+ DEFAULT_CLIENT_SESSION_ID,
7
9
  DEFAULT_DEVICE_LOCALE,
8
10
  DEFAULT_DEVICE_NAME,
9
11
  DEFAULT_DEVICE_TYPE,
@@ -284,6 +286,8 @@ class UserAgentPayload(CamelModel):
284
286
  app_version: str = Field(default=DEFAULT_APP_VERSION)
285
287
  screen: str = Field(default=DEFAULT_SCREEN)
286
288
  timezone: str = Field(default=DEFAULT_TIMEZONE)
289
+ client_session_id: int = Field(default=DEFAULT_CLIENT_SESSION_ID)
290
+ build_number: int = Field(default=DEFAULT_BUILD_NUMBER)
287
291
 
288
292
 
289
293
  class ReworkInviteLinkPayload(CamelModel):
@@ -328,3 +332,11 @@ class UpdateFolderPayload(CamelModel):
328
332
 
329
333
  class DeleteFolderPayload(CamelModel):
330
334
  folder_ids: list[str]
335
+
336
+
337
+ class LeaveChatPayload(CamelModel):
338
+ chat_id: int
339
+
340
+
341
+ class FetchChatsPayload(CamelModel):
342
+ marker: int
pymax/static/constant.py CHANGED
@@ -8,17 +8,19 @@ WEBSOCKET_URI: Final[str] = "wss://ws-api.oneme.ru/websocket"
8
8
  WEBSOCKET_ORIGIN: Final[Origin] = Origin("https://web.max.ru")
9
9
  HOST: Final[str] = "api.oneme.ru"
10
10
  PORT: Final[int] = 443
11
- DEFAULT_TIMEOUT: Final[float] = 10.0
11
+ DEFAULT_TIMEOUT: Final[float] = 20.0
12
12
  DEFAULT_DEVICE_TYPE: Final[str] = "WEB"
13
13
  DEFAULT_LOCALE: Final[str] = "ru"
14
14
  DEFAULT_DEVICE_LOCALE: Final[str] = "ru"
15
15
  DEFAULT_DEVICE_NAME: Final[str] = "Chrome"
16
- DEFAULT_APP_VERSION: Final[str] = "25.10.13"
16
+ DEFAULT_APP_VERSION: Final[str] = "25.12.13"
17
17
  DEFAULT_SCREEN: Final[str] = "1080x1920 1.0x"
18
18
  DEFAULT_OS_VERSION: Final[str] = "Linux"
19
19
  DEFAULT_USER_AGENT: Final[str] = (
20
20
  "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36"
21
21
  )
22
+ DEFAULT_BUILD_NUMBER: Final[int] = 0x97CB
23
+ DEFAULT_CLIENT_SESSION_ID: Final[int] = 14
22
24
  DEFAULT_TIMEZONE: Final[str] = "Europe/Moscow"
23
25
  DEFAULT_CHAT_MEMBERS_LIMIT: Final[int] = 50
24
26
  DEFAULT_MARKER_VALUE: Final[int] = 0
pymax/static/enum.py CHANGED
@@ -141,6 +141,10 @@ class Opcode(int, Enum):
141
141
  FOLDERS_DELETE = 276
142
142
  NOTIF_FOLDERS = 277
143
143
 
144
+ GET_QR = 288
145
+ GET_QR_STATUS = 289
146
+ LOGIN_BY_QR = 291
147
+
144
148
 
145
149
  class ChatType(str, Enum):
146
150
  DIALOG = "DIALOG"
@@ -170,6 +174,7 @@ class AuthType(str, Enum):
170
174
  START_AUTH = "START_AUTH"
171
175
  CHECK_CODE = "CHECK_CODE"
172
176
  REGISTER = "REGISTER"
177
+ RESEND = "RESEND"
173
178
 
174
179
 
175
180
  class AccessType(str, Enum):
@@ -1,31 +0,0 @@
1
- pymax/__init__.py,sha256=6wUKKwsyxFpWG3b7kwptOvHd-w78C-ygw42iCDBYQvc,1915
2
- pymax/core.py,sha256=LqX56a5BagUYl1vpB55Y1pLZQdMoC86t6mIQVlkVByo,17322
3
- pymax/crud.py,sha256=wmJh8MPi3L_HbYp7MJP0eXfDcnjgfkLDa9rHAmXtkow,3219
4
- pymax/exceptions.py,sha256=nDUNx7bM-Yjugj-qfIllcrnwLg9JpZroYqfXapjYbMQ,3178
5
- pymax/files.py,sha256=AvFIr34Desq2p4CNWXIngRqeyTBKMT98VmcnI-zvUU0,3462
6
- pymax/filters.py,sha256=4hehzyQlyxBxLiEhtSH_KQdFHx8gYaqyIOOD72EsSS0,1536
7
- pymax/formatter.py,sha256=OVsTwambHhXlOMd0wVECJWuB_S2wSEVKdMLCzgPcvYQ,859
8
- pymax/formatting.py,sha256=hhtmakfcQDzQRsAckPunnJOCKY2lFdLeIp8Yw53yY8s,2522
9
- pymax/interfaces.py,sha256=Re8o5N7FSQ-5OgVlK4-WBltX27GheEbfFjoIYl9_u6I,3723
10
- pymax/models.py,sha256=PsPGbOkERxesZZltjNrmqhOfRcO44Is2ThbEToREcB8,201
11
- pymax/navigation.py,sha256=4ia6RGY2pXMArboNhHkbWlWX7LtcYK1VGVXorPX0Pb4,5747
12
- pymax/payloads.py,sha256=cEXY_cVL6SPyhoFTTZnn7dyUx9MMdtNT5SuQSQtL4rg,6983
13
- pymax/types.py,sha256=_ARcVXLGHyiGAJKYPd6EU9QDKzz4VwS6kjTu3YEH_u4,35523
14
- pymax/mixins/__init__.py,sha256=xvjcq-lFVHCPss_t8xxXya0OJnsh-owlBqtUlrXSCcw,695
15
- pymax/mixins/auth.py,sha256=Emv-0WVB_orwv9L_V5gAHfp-VYVaVcbW6AlclW_K6W4,6731
16
- pymax/mixins/channel.py,sha256=7c8GANyxZuNbIHNBVcPAmMa1qqA1IRf9cGPBS1oK_q4,5159
17
- pymax/mixins/group.py,sha256=XWXNWluCvq4KkZWqv4sxLpzkXfH33U1yEP20_ZFtSM4,10624
18
- pymax/mixins/handler.py,sha256=ZuYX8wSgNXJoSMArcwyHvY_bL9A7X0AXnAOz22ATA3k,5897
19
- pymax/mixins/message.py,sha256=wYvkMPE9ORCSFd_9J-6ltf__4ELG_zaZ_Uey4rmCzHg,25460
20
- pymax/mixins/self.py,sha256=3BdHfUyqw3dn3ctJX9_hilP1jOaTaunstZ7nH8Y_xcU,5436
21
- pymax/mixins/socket.py,sha256=GEOscQKKC48bOAXXiDLoz8GusCLtjdxQnB5AK5xJbms,26997
22
- pymax/mixins/telemetry.py,sha256=LWr68DNQkPhAjGRDYQ5lORXxC3Yw6M9E8sF0TCNISTE,3609
23
- pymax/mixins/user.py,sha256=5utoK7Z-7lySOg0PEO69b6h_3kjfcnV-YydTZIdNj8g,7120
24
- pymax/mixins/utils.py,sha256=s3FUf3i_wjn2Gbg5YY1rWZB-90ZEGrrcUuND_MqqSTE,853
25
- pymax/mixins/websocket.py,sha256=toiXt9qxx6yTgnWJdEOeNfp414MD4zmbHp1qVgVkjnY,19850
26
- pymax/static/constant.py,sha256=Q1NrmaRj17Gdhk3FmUp3HIwrad1TDorq3wFdQlOCzN8,1027
27
- pymax/static/enum.py,sha256=ddw5SEVfRb2J9TXOa5IGhssNd-7RyKfwZBKx_UionEM,4562
28
- maxapi_python-1.1.20.dist-info/METADATA,sha256=9yhRv1m8PbJJhnUdloacxlK0jAE0veq6zfXDP-Ok5nk,6245
29
- maxapi_python-1.1.20.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
30
- maxapi_python-1.1.20.dist-info/licenses/LICENSE,sha256=hOR249ItqMdcly1A0amqEWRNRTq4Gv5NJtmQ3A5qK4E,1070
31
- maxapi_python-1.1.20.dist-info/RECORD,,