maxapi-python 0.1.1__py3-none-any.whl → 0.1.3__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/static.py ADDED
@@ -0,0 +1,210 @@
1
+ from enum import Enum, IntEnum
2
+
3
+
4
+ class Opcode(IntEnum):
5
+ PING = 1
6
+ DEBUG = 2
7
+ RECONNECT = 3
8
+ LOG = 5
9
+ SESSION_INIT = 6
10
+ PROFILE = 16
11
+ AUTH_REQUEST = 17
12
+ AUTH = 18
13
+ LOGIN = 19
14
+ LOGOUT = 20
15
+ SYNC = 21
16
+ CONFIG = 22
17
+ AUTH_CONFIRM = 23
18
+ PRESET_AVATARS = 25
19
+ ASSETS_GET = 26
20
+ UNKNOWN_26 = 26
21
+ ASSETS_UPDATE = 27
22
+ ASSETS_GET_BY_IDS = 28
23
+ ASSETS_ADD = 29
24
+ SEARCH_FEEDBACK = 31
25
+ CONTACT_INFO = 32
26
+ CONTACT_ADD = 33
27
+ CONTACT_UPDATE = 34
28
+ CONTACT_PRESENCE = 35
29
+ CONTACT_LIST = 36
30
+ CONTACT_SEARCH = 37
31
+ CONTACT_MUTUAL = 38
32
+ CONTACT_PHOTOS = 39
33
+ CONTACT_SORT = 40
34
+ CONTACT_VERIFY = 42
35
+ REMOVE_CONTACT_PHOTO = 43
36
+ CONTACT_INFO_BY_PHONE = 46
37
+ CHAT_INFO = 48
38
+ CHAT_HISTORY = 49
39
+ CHAT_MARK = 50
40
+ CHAT_MEDIA = 51
41
+ CHAT_DELETE = 52
42
+ CHATS_LIST = 53
43
+ CHAT_CLEAR = 54
44
+ CHAT_UPDATE = 55
45
+ CHAT_CHECK_LINK = 56
46
+ CHAT_JOIN = 57
47
+ CHAT_LEAVE = 58
48
+ CHAT_MEMBERS = 59
49
+ PUBLIC_SEARCH = 60
50
+ CHAT_CLOSE = 61
51
+ CHAT_CREATE = 63
52
+ MSG_SEND = 64
53
+ MSG_TYPING = 65
54
+ MSG_DELETE = 66
55
+ MSG_EDIT = 67
56
+ CHAT_SEARCH = 68
57
+ MSG_SHARE_PREVIEW = 70
58
+ MSG_GET = 71
59
+ MSG_SEARCH_TOUCH = 72
60
+ MSG_SEARCH = 73
61
+ MSG_GET_STAT = 74
62
+ CHAT_SUBSCRIBE = 75
63
+ VIDEO_CHAT_START = 76
64
+ CHAT_MEMBERS_UPDATE = 77
65
+ VIDEO_CHAT_HISTORY = 79
66
+ PHOTO_UPLOAD = 80
67
+ STICKER_UPLOAD = 81
68
+ VIDEO_UPLOAD = 82
69
+ VIDEO_PLAY = 83
70
+ CHAT_PIN_SET_VISIBILITY = 86
71
+ FILE_UPLOAD = 87
72
+ FILE_DOWNLOAD = 88
73
+ LINK_INFO = 89
74
+ MSG_DELETE_RANGE = 92
75
+ SESSIONS_INFO = 96
76
+ SESSIONS_CLOSE = 97
77
+ PHONE_BIND_REQUEST = 98
78
+ PHONE_BIND_CONFIRM = 99
79
+ CONFIRM_PRESENT = 101
80
+ GET_INBOUND_CALLS = 103
81
+ EXTERNAL_CALLBACK = 105
82
+ AUTH_VALIDATE_PASSWORD = 107
83
+ AUTH_VALIDATE_HINT = 108
84
+ AUTH_VERIFY_EMAIL = 109
85
+ AUTH_CHECK_EMAIL = 110
86
+ AUTH_SET_2FA = 111
87
+ AUTH_CREATE_TRACK = 112
88
+ AUTH_LOGIN_CHECK_PASSWORD = 115
89
+ CHAT_COMPLAIN = 117
90
+ MSG_SEND_CALLBACK = 118
91
+ SUSPEND_BOT = 119
92
+ LOCATION_STOP = 124
93
+ LOCATION_SEND = 125
94
+ LOCATION_REQUEST = 126
95
+ GET_LAST_MENTIONS = 127
96
+ NOTIF_MESSAGE = 128
97
+ NOTIF_TYPING = 129
98
+ NOTIF_MARK = 130
99
+ NOTIF_CONTACT = 131
100
+ NOTIF_PRESENCE = 132
101
+ NOTIF_CONFIG = 134
102
+ NOTIF_CHAT = 135
103
+ NOTIF_ATTACH = 136
104
+ NOTIF_CALL_START = 137
105
+ NOTIF_CONTACT_SORT = 139
106
+ NOTIF_MSG_DELETE_RANGE = 140
107
+ NOTIF_MSG_DELETE = 142
108
+ NOTIF_CALLBACK_ANSWER = 143
109
+ CHAT_BOT_COMMANDS = 144
110
+ BOT_INFO = 145
111
+ NOTIF_LOCATION = 147
112
+ NOTIF_LOCATION_REQUEST = 148
113
+ NOTIF_ASSETS_UPDATE = 150
114
+ NOTIF_DRAFT = 152
115
+ NOTIF_DRAFT_DISCARD = 153
116
+ NOTIF_MSG_DELAYED = 154
117
+ NOTIF_MSG_REACTIONS_CHANGED = 155
118
+ NOTIF_MSG_YOU_REACTED = 156
119
+ CALLS_TOKEN = 158
120
+ NOTIF_PROFILE = 159
121
+ WEB_APP_INIT_DATA = 160
122
+ DRAFT_SAVE = 176
123
+ DRAFT_DISCARD = 177
124
+ MSG_REACTION = 178
125
+ MSG_CANCEL_REACTION = 179
126
+ MSG_GET_REACTIONS = 180
127
+ MSG_GET_DETAILED_REACTIONS = 181
128
+ STICKER_CREATE = 193
129
+ STICKER_SUGGEST = 194
130
+ VIDEO_CHAT_MEMBERS = 195
131
+ CHAT_HIDE = 196
132
+ CHAT_SEARCH_COMMON_PARTICIPANTS = 198
133
+ PROFILE_DELETE = 199
134
+ PROFILE_DELETE_TIME = 200
135
+ ASSETS_REMOVE = 259
136
+ ASSETS_MOVE = 260
137
+ ASSETS_LIST_MODIFY = 261
138
+ FOLDERS_GET = 272
139
+ FOLDERS_GET_BY_ID = 273
140
+ FOLDERS_UPDATE = 274
141
+ FOLDERS_REORDER = 275
142
+ FOLDERS_DELETE = 276
143
+ NOTIF_FOLDERS = 277
144
+
145
+
146
+ class ChatType(str, Enum):
147
+ DIALOG = "DIALOG"
148
+ CHAT = "CHAT"
149
+ CHANNEL = "CHANNEL"
150
+
151
+
152
+ class MessageType(str, Enum):
153
+ TEXT = "TEXT"
154
+ SYSTEM = "SYSTEM"
155
+ SERVICE = "SERVICE"
156
+
157
+
158
+ class MessageStatus(str, Enum):
159
+ SENT = "SENT"
160
+ DELIVERED = "DELIVERED"
161
+ READ = "READ"
162
+ ERROR = "ERROR"
163
+
164
+
165
+ class ElementType(str, Enum):
166
+ TEXT = "text"
167
+ MENTION = "mention"
168
+ LINK = "link"
169
+ EMOJI = "emoji"
170
+
171
+
172
+ class AuthType(str, Enum):
173
+ START_AUTH = "START_AUTH"
174
+ CHECK_CODE = "CHECK_CODE"
175
+
176
+
177
+ class AccessType(str, Enum):
178
+ PUBLIC = "PUBLIC"
179
+ PRIVATE = "PRIVATE"
180
+ SECRET = "SECRET"
181
+
182
+
183
+ class DeviceType(str, Enum):
184
+ WEB = "WEB"
185
+ ANDROID = "ANDROID"
186
+ IOS = "IOS"
187
+
188
+
189
+ class AttachType(str, Enum):
190
+ PHOTO = "PHOTO"
191
+ VIDEO = "VIDEO"
192
+ FILE = "FILE"
193
+ STICKER = "STICKER"
194
+
195
+
196
+ class Constants(Enum):
197
+ PHONE_REGEX = r"^\+?\d{10,15}$"
198
+ WEBSOCKET_URI = "wss://ws-api.oneme.ru/websocket"
199
+ DEFAULT_TIMEOUT = 10.0
200
+ DEFAULT_USER_AGENT = {
201
+ "deviceType": "WEB",
202
+ "locale": "ru",
203
+ "deviceLocale": "ru",
204
+ "osVersion": "Linux",
205
+ "deviceName": "Chrome",
206
+ "headerUserAgent": "Mozilla/5.0 ...",
207
+ "appVersion": "25.8.5",
208
+ "screen": "1080x1920 1.0x",
209
+ "timezone": "Europe/Moscow",
210
+ }
pymax/types.py ADDED
@@ -0,0 +1,434 @@
1
+ from typing import Any
2
+
3
+ from typing_extensions import override
4
+
5
+ from .static import (
6
+ AccessType,
7
+ AttachType,
8
+ ChatType,
9
+ ElementType,
10
+ MessageStatus,
11
+ MessageType,
12
+ )
13
+
14
+
15
+ class Names:
16
+ def __init__(
17
+ self, name: str, first_name: str, last_name: str | None, type: str
18
+ ) -> None:
19
+ self.name = name
20
+ self.first_name = first_name
21
+ self.last_name = last_name
22
+ self.type = type
23
+
24
+ @classmethod
25
+ def from_dict(cls, data: dict[str, Any]) -> "Names":
26
+ return cls(
27
+ name=data["name"],
28
+ first_name=data["firstName"],
29
+ last_name=data.get("lastName"),
30
+ type=data["type"],
31
+ )
32
+
33
+ @override
34
+ def __repr__(self) -> str:
35
+ return f"Names(name={self.name!r}, first_name={self.first_name!r}, last_name={self.last_name!r}, type={self.type!r})"
36
+
37
+ @override
38
+ def __str__(self) -> str:
39
+ return self.name
40
+
41
+
42
+ class Me:
43
+ def __init__(
44
+ self,
45
+ id: int,
46
+ account_status: int,
47
+ phone: str,
48
+ names: list[Names],
49
+ update_time: int,
50
+ options: list[str] | None = None,
51
+ ) -> None:
52
+ self.id = id
53
+ self.account_status = account_status
54
+ self.phone = phone
55
+ self.update_time = update_time
56
+ self.options = options
57
+ self.names = names
58
+
59
+ @classmethod
60
+ def from_dict(cls, data: dict[str, Any]) -> "Me":
61
+ return cls(
62
+ id=data["id"],
63
+ account_status=data["accountStatus"],
64
+ phone=data["phone"],
65
+ names=[Names.from_dict(n) for n in data["names"]],
66
+ update_time=data["updateTime"],
67
+ options=data.get("options"),
68
+ )
69
+
70
+ @override
71
+ def __repr__(self) -> str:
72
+ return f"Me(id={self.id!r}, account_status={self.account_status!r}, phone={self.phone!r}, names={self.names!r}, update_time={self.update_time!r}, options={self.options!r})"
73
+
74
+ @override
75
+ def __str__(self) -> str:
76
+ return f"Me {self.id}: {', '.join(str(n) for n in self.names)}"
77
+
78
+
79
+ class Element:
80
+ def __init__(
81
+ self, type: ElementType | str, length: int, from_: int | None = None
82
+ ) -> None:
83
+ self.type = type
84
+ self.length = length
85
+ self.from_ = from_
86
+
87
+ @classmethod
88
+ def from_dict(cls, data: dict[Any, Any]) -> "Element":
89
+ return cls(type=data["type"], length=data["length"], from_=data.get("from"))
90
+
91
+ @override
92
+ def __repr__(self) -> str:
93
+ return (
94
+ f"Element(type={self.type!r}, length={self.length!r}, from_={self.from_!r})"
95
+ )
96
+
97
+ @override
98
+ def __str__(self) -> str:
99
+ return f"{self.type}({self.length})"
100
+
101
+
102
+ class Message:
103
+ def __init__(
104
+ self,
105
+ sender: int | None,
106
+ elements: list[Element] | None,
107
+ reaction_info: dict[str, Any] | None,
108
+ options: int | None,
109
+ id: int,
110
+ time: int,
111
+ text: str,
112
+ status: MessageStatus | str | None,
113
+ type: MessageType | str,
114
+ attaches: list[Any],
115
+ ) -> None:
116
+ self.sender = sender
117
+ self.elements = elements
118
+ self.options = options
119
+ self.id = id
120
+ self.time = time
121
+ self.text = text
122
+ self.type = type
123
+ self.attaches = attaches
124
+ self.status = status
125
+ self.reactionInfo = reaction_info
126
+
127
+ @classmethod
128
+ def from_dict(cls, data: dict[Any, Any]) -> "Message":
129
+ return cls(
130
+ sender=data.get("sender"),
131
+ elements=[Element.from_dict(e) for e in data.get("elements", [])],
132
+ options=data.get("options"),
133
+ id=data["id"],
134
+ time=data["time"],
135
+ text=data["text"],
136
+ type=data["type"],
137
+ attaches=data.get("attaches", []),
138
+ status=data.get("status"),
139
+ reaction_info=data.get("reactionInfo"),
140
+ )
141
+
142
+ @override
143
+ def __repr__(self) -> str:
144
+ return (
145
+ f"Message(id={self.id!r}, sender={self.sender!r}, text={self.text!r}, "
146
+ f"type={self.type!r}, status={self.status!r}, elements={self.elements!r})"
147
+ )
148
+
149
+ @override
150
+ def __str__(self) -> str:
151
+ return f"Message {self.id} from {self.sender}: {self.text}"
152
+
153
+
154
+ class Dialog:
155
+ def __init__(
156
+ self,
157
+ cid: int | None,
158
+ owner: int,
159
+ has_bots: bool | None,
160
+ join_time: int,
161
+ created: int,
162
+ last_message: Message | None,
163
+ type: ChatType | str,
164
+ last_fire_delayed_error_time: int,
165
+ last_delayed_update_time: int,
166
+ prev_message_id: str | None,
167
+ options: dict[str, bool],
168
+ modified: int,
169
+ last_event_time: int,
170
+ id: int,
171
+ status: str,
172
+ participants: dict[str, int],
173
+ ) -> None:
174
+ self.cid = cid
175
+ self.owner = owner
176
+ self.has_bots = has_bots
177
+ self.join_time = join_time
178
+ self.created = created
179
+ self.last_message = last_message
180
+ self.type = type
181
+ self.last_fire_delayed_error_time = last_fire_delayed_error_time
182
+ self.last_delayed_update_time = last_delayed_update_time
183
+ self.prev_message_id = prev_message_id
184
+ self.options = options
185
+ self.modified = modified
186
+ self.last_event_time = last_event_time
187
+ self.id = id
188
+ self.status = status
189
+ self.participants = participants
190
+
191
+ @classmethod
192
+ def from_dict(cls, data: dict[Any, Any]) -> "Dialog":
193
+ return cls(
194
+ cid=data.get("cid"),
195
+ owner=data["owner"],
196
+ has_bots=data.get("hasBots"),
197
+ join_time=data["joinTime"],
198
+ created=data["created"],
199
+ last_message=Message.from_dict(data["lastMessage"])
200
+ if data.get("lastMessage")
201
+ else None,
202
+ type=ChatType(data["type"]),
203
+ last_fire_delayed_error_time=data["lastFireDelayedErrorTime"],
204
+ last_delayed_update_time=data["lastDelayedUpdateTime"],
205
+ prev_message_id=data.get("prevMessageId"),
206
+ options=data.get("options", {}),
207
+ modified=data["modified"],
208
+ last_event_time=data["lastEventTime"],
209
+ id=data["id"],
210
+ status=data["status"],
211
+ participants=data["participants"],
212
+ )
213
+
214
+ @override
215
+ def __repr__(self) -> str:
216
+ return f"Dialog(id={self.id!r}, owner={self.owner!r}, type={self.type!r}, last_message={self.last_message!r})"
217
+
218
+ @override
219
+ def __str__(self) -> str:
220
+ return f"Dialog {self.id} ({self.type})"
221
+
222
+
223
+ class Chat:
224
+ def __init__(
225
+ self,
226
+ participants_count: int,
227
+ access: AccessType | str,
228
+ invited_by: int | None,
229
+ link: str | None,
230
+ chat_type: ChatType | str,
231
+ title: str | None,
232
+ last_fire_delayed_error_time: int,
233
+ last_delayed_update_time: int,
234
+ options: dict[str, bool],
235
+ base_raw_icon_url: str | None,
236
+ base_icon_url: str | None,
237
+ description: str | None,
238
+ modified: int,
239
+ id_: int,
240
+ admin_participants: dict[int, dict[Any, Any]],
241
+ participants: dict[int, int],
242
+ owner: int,
243
+ join_time: int,
244
+ created: int,
245
+ last_message: Message | None,
246
+ prev_message_id: str | None,
247
+ last_event_time: int,
248
+ messages_count: int,
249
+ admins: list[int],
250
+ restrictions: int | None,
251
+ status: str,
252
+ cid: int,
253
+ ) -> None:
254
+ self.participants_count = participants_count
255
+ self.access = access
256
+ self.invited_by = invited_by
257
+ self.link = link
258
+ self.type = chat_type
259
+ self.title = title
260
+ self.last_fire_delayed_error_time = last_fire_delayed_error_time
261
+ self.last_delayed_update_time = last_delayed_update_time
262
+ self.options = options
263
+ self.base_raw_icon_url = base_raw_icon_url
264
+ self.base_icon_url = base_icon_url
265
+ self.description = description
266
+ self.modified = modified
267
+ self.id = id_
268
+ self.admin_participants = admin_participants
269
+ self.participants = participants
270
+ self.owner = owner
271
+ self.join_time = join_time
272
+ self.created = created
273
+ self.last_message = last_message
274
+ self.prev_message_id = prev_message_id
275
+ self.last_event_time = last_event_time
276
+ self.messages_count = messages_count
277
+ self.admins = admins
278
+ self.restrictions = restrictions
279
+ self.status = status
280
+ self.cid = cid
281
+
282
+ @classmethod
283
+ def from_dict(cls, data: dict[Any, Any]) -> "Chat":
284
+ raw_admins = data.get("adminParticipants", {}) or {}
285
+ admin_participants: dict[int, dict[Any, Any]] = {
286
+ int(k): v for k, v in raw_admins.items()
287
+ }
288
+ raw_participants = data.get("participants", {}) or {}
289
+ participants: dict[int, int] = {int(k): v for k, v in raw_participants.items()}
290
+ last_msg = (
291
+ Message.from_dict(data["lastMessage"]) if data.get("lastMessage") else None
292
+ )
293
+ return cls(
294
+ participants_count=data.get("participantsCount", 0),
295
+ access=AccessType(data.get("access", AccessType.PUBLIC.value)),
296
+ invited_by=data.get("invitedBy"),
297
+ link=data.get("link"),
298
+ base_raw_icon_url=data.get("baseRawIconUrl"),
299
+ base_icon_url=data.get("baseIconUrl"),
300
+ description=data.get("description"),
301
+ chat_type=ChatType(data.get("type", ChatType.CHAT.value)),
302
+ title=data.get("title"),
303
+ last_fire_delayed_error_time=data.get("lastFireDelayedErrorTime", 0),
304
+ last_delayed_update_time=data.get("lastDelayedUpdateTime", 0),
305
+ options=data.get("options", {}),
306
+ modified=data.get("modified", 0),
307
+ id_=data.get("id", 0),
308
+ admin_participants=admin_participants,
309
+ participants=participants,
310
+ owner=data.get("owner", 0),
311
+ join_time=data.get("joinTime", 0),
312
+ created=data.get("created", 0),
313
+ last_message=last_msg,
314
+ prev_message_id=data.get("prevMessageId"),
315
+ last_event_time=data.get("lastEventTime", 0),
316
+ messages_count=data.get("messagesCount", 0),
317
+ admins=data.get("admins", []),
318
+ restrictions=data.get("restrictions"),
319
+ status=data.get("status", ""),
320
+ cid=data.get("cid", 0),
321
+ )
322
+
323
+ @override
324
+ def __repr__(self) -> str:
325
+ return f"Chat(id={self.id!r}, title={self.title!r}, type={self.type!r})"
326
+
327
+ @override
328
+ def __str__(self) -> str:
329
+ return f"{self.title} ({self.type})"
330
+
331
+
332
+ class Channel(Chat):
333
+ @override
334
+ def __repr__(self) -> str:
335
+ return f"Channel(id={self.id!r}, title={self.title!r})"
336
+
337
+ @override
338
+ def __str__(self) -> str:
339
+ return f"Channel: {self.title}"
340
+
341
+
342
+ class User:
343
+ def __init__(
344
+ self,
345
+ account_status: int,
346
+ update_time: int,
347
+ id: int,
348
+ names: list[Names],
349
+ options: list[str] | None = None,
350
+ base_url: str | None = None,
351
+ base_raw_url: str | None = None,
352
+ photo_id: int | None = None,
353
+ description: str | None = None,
354
+ gender: int | None = None,
355
+ link: str | None = None,
356
+ web_app: str | None = None,
357
+ menu_button: dict[str, Any] | None = None,
358
+ ) -> None:
359
+ self.account_status = account_status
360
+ self.update_time = update_time
361
+ self.id = id
362
+ self.names = names
363
+ self.options = options or []
364
+ self.base_url = base_url
365
+ self.base_raw_url = base_raw_url
366
+ self.photo_id = photo_id
367
+ self.description = description
368
+ self.gender = gender
369
+ self.link = link
370
+ self.web_app = web_app
371
+ self.menu_button = menu_button
372
+
373
+ @classmethod
374
+ def from_dict(cls, data: dict[str, Any]) -> "User":
375
+ return cls(
376
+ account_status=data["accountStatus"],
377
+ update_time=data["updateTime"],
378
+ id=data["id"],
379
+ names=[Names.from_dict(n) for n in data.get("names", [])],
380
+ options=data.get("options"),
381
+ base_url=data.get("baseUrl"),
382
+ base_raw_url=data.get("baseRawUrl"),
383
+ photo_id=data.get("photoId"),
384
+ description=data.get("description"),
385
+ gender=data.get("gender"),
386
+ link=data.get("link"),
387
+ web_app=data.get("webApp"),
388
+ menu_button=data.get("menuButton"),
389
+ )
390
+
391
+ @override
392
+ def __repr__(self) -> str:
393
+ return f"User(id={self.id!r}, names={self.names!r}, status={self.account_status!r})"
394
+
395
+ @override
396
+ def __str__(self) -> str:
397
+ return f"User {self.id}: {', '.join(str(n) for n in self.names)}"
398
+
399
+
400
+ class Attach:
401
+ def __init__(
402
+ self,
403
+ _type: AttachType,
404
+ video_id: int | None = None,
405
+ photo_token: str | None = None,
406
+ file_id: int | None = None,
407
+ token: str | None = None,
408
+ ) -> None:
409
+ self.type = _type
410
+ self.video_id = video_id
411
+ self.photo_token = photo_token
412
+ self.file_id = file_id
413
+ self.token = token
414
+
415
+ @classmethod
416
+ def from_dict(cls, data: dict[str, Any]) -> "Attach":
417
+ return cls(
418
+ _type=AttachType(data["type"]),
419
+ video_id=data.get("videoId"),
420
+ photo_token=data.get("photoToken"),
421
+ file_id=data.get("fileId"),
422
+ token=data.get("token"),
423
+ )
424
+
425
+ @override
426
+ def __repr__(self) -> str:
427
+ return (
428
+ f"Attach(type={self.type!r}, video_id={self.video_id!r}, "
429
+ f"photo_token={self.photo_token!r}, file_id={self.file_id!r}, token={self.token!r})"
430
+ )
431
+
432
+ @override
433
+ def __str__(self) -> str:
434
+ return f"Attach: {self.type}"
pymax/utils.py ADDED
@@ -0,0 +1,38 @@
1
+ from typing import Any
2
+
3
+ import lz4.block
4
+ import msgpack
5
+
6
+
7
+ def unpack_packet(data: bytes) -> None | dict[str, Any]:
8
+ ver = int.from_bytes(data[0:1], "big")
9
+ cmd = int.from_bytes(data[1:3], "big")
10
+ seq = int.from_bytes(data[3:4], "big")
11
+ opcode = int.from_bytes(data[4:6], "big")
12
+ packed_len = int.from_bytes(data[6:10], "big", signed=False)
13
+ comp_flag = packed_len >> 24
14
+ payload_length = packed_len & 0xFFFFFF
15
+ payload_bytes = data[10 : 10 + payload_length]
16
+ if comp_flag != 0:
17
+ compressed_data = payload_bytes
18
+ try:
19
+ payload_bytes = lz4.block.decompress(compressed_data, uncompressed_size=255)
20
+ except lz4.block.LZ4BlockError:
21
+ return None
22
+ payload = msgpack.unpackb(payload_bytes, raw=False)
23
+ return {"ver": ver, "cmd": cmd, "seq": seq, "opcode": opcode, "payload": payload}
24
+
25
+
26
+ # ToDo: Add lz4 compression
27
+ def pack_packet(
28
+ ver: int, cmd: int, seq: int, opcode: int, payload: dict[str, Any]
29
+ ) -> bytes:
30
+ ver_b = ver.to_bytes(1, "big")
31
+ cmd_b = cmd.to_bytes(2, "big")
32
+ seq_b = seq.to_bytes(1, "big")
33
+ opcode_b = opcode.to_bytes(2, "big")
34
+ payload_bytes = msgpack.packb(payload)
35
+ if payload_bytes is None:
36
+ payload_bytes = b""
37
+ payload_len_b = len(payload_bytes).to_bytes(4, "big")
38
+ return ver_b + cmd_b + seq_b + opcode_b + payload_len_b + payload_bytes
@@ -1,4 +0,0 @@
1
- maxapi_python-0.1.1.dist-info/METADATA,sha256=uHYKlxE6NA9cmATGtlE_AhF05iJarJmX4fKLCnuMbkE,4812
2
- maxapi_python-0.1.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
3
- maxapi_python-0.1.1.dist-info/licenses/LICENSE,sha256=Ud-0SKeXO_yA02Bb1nMDnEaSGwz2OqNlfGQbk0IzqPI,1085
4
- maxapi_python-0.1.1.dist-info/RECORD,,