maxapi-python 1.2.3__py3-none-any.whl → 1.2.5__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.
- {maxapi_python-1.2.3.dist-info → maxapi_python-1.2.5.dist-info}/METADATA +9 -7
- maxapi_python-1.2.5.dist-info/RECORD +33 -0
- pymax/core.py +63 -38
- pymax/files.py +28 -7
- pymax/interfaces.py +417 -116
- pymax/mixins/auth.py +231 -5
- pymax/mixins/channel.py +3 -5
- pymax/mixins/group.py +2 -2
- pymax/mixins/handler.py +4 -10
- pymax/mixins/message.py +64 -88
- pymax/mixins/scheduler.py +1 -1
- pymax/mixins/self.py +2 -2
- pymax/mixins/socket.py +16 -340
- pymax/mixins/telemetry.py +10 -6
- pymax/mixins/user.py +3 -5
- pymax/mixins/websocket.py +16 -365
- pymax/payloads.py +44 -1
- pymax/protocols.py +123 -0
- pymax/static/constant.py +76 -8
- pymax/static/enum.py +65 -52
- pymax/types.py +25 -0
- pymax/utils.py +90 -0
- maxapi_python-1.2.3.dist-info/RECORD +0 -32
- pymax/mixins/utils.py +0 -27
- {maxapi_python-1.2.3.dist-info → maxapi_python-1.2.5.dist-info}/WHEEL +0 -0
- {maxapi_python-1.2.3.dist-info → maxapi_python-1.2.5.dist-info}/licenses/LICENSE +0 -0
pymax/mixins/message.py
CHANGED
|
@@ -10,8 +10,6 @@ from aiohttp import ClientSession, TCPConnector
|
|
|
10
10
|
from pymax.exceptions import Error
|
|
11
11
|
from pymax.files import File, Photo, Video
|
|
12
12
|
from pymax.formatting import Formatting
|
|
13
|
-
from pymax.interfaces import ClientProtocol
|
|
14
|
-
from pymax.mixins.utils import MixinsUtils
|
|
15
13
|
from pymax.payloads import (
|
|
16
14
|
AddReactionPayload,
|
|
17
15
|
AttachFilePayload,
|
|
@@ -25,6 +23,7 @@ from pymax.payloads import (
|
|
|
25
23
|
MessageElement,
|
|
26
24
|
PinMessagePayload,
|
|
27
25
|
ReactionInfoPayload,
|
|
26
|
+
ReadMessagesPayload,
|
|
28
27
|
RemoveReactionPayload,
|
|
29
28
|
ReplyLink,
|
|
30
29
|
SendMessagePayload,
|
|
@@ -32,15 +31,18 @@ from pymax.payloads import (
|
|
|
32
31
|
UploadPayload,
|
|
33
32
|
VideoAttachPayload,
|
|
34
33
|
)
|
|
34
|
+
from pymax.protocols import ClientProtocol
|
|
35
35
|
from pymax.static.constant import DEFAULT_TIMEOUT
|
|
36
|
-
from pymax.static.enum import AttachType, Opcode
|
|
36
|
+
from pymax.static.enum import AttachType, Opcode, ReadAction
|
|
37
37
|
from pymax.types import (
|
|
38
38
|
Attach,
|
|
39
39
|
FileRequest,
|
|
40
40
|
Message,
|
|
41
41
|
ReactionInfo,
|
|
42
|
+
ReadState,
|
|
42
43
|
VideoRequest,
|
|
43
44
|
)
|
|
45
|
+
from pymax.utils import MixinsUtils
|
|
44
46
|
|
|
45
47
|
|
|
46
48
|
class MessageMixin(ClientProtocol):
|
|
@@ -68,15 +70,11 @@ class MessageMixin(ClientProtocol):
|
|
|
68
70
|
|
|
69
71
|
if file.path:
|
|
70
72
|
file_size = Path(file.path).stat().st_size
|
|
71
|
-
self.logger.info(
|
|
72
|
-
"File size from path: %.2f MB", file_size / (1024 * 1024)
|
|
73
|
-
)
|
|
73
|
+
self.logger.info("File size from path: %.2f MB", file_size / (1024 * 1024))
|
|
74
74
|
else:
|
|
75
75
|
file_bytes = await file.read()
|
|
76
76
|
file_size = len(file_bytes)
|
|
77
|
-
self.logger.info(
|
|
78
|
-
"File size from URL: %.2f MB", file_size / (1024 * 1024)
|
|
79
|
-
)
|
|
77
|
+
self.logger.info("File size from URL: %.2f MB", file_size / (1024 * 1024))
|
|
80
78
|
|
|
81
79
|
connector = TCPConnector(limit=0)
|
|
82
80
|
timeout = aiohttp.ClientTimeout(total=None, sock_read=None, sock_connect=30)
|
|
@@ -161,9 +159,7 @@ class MessageMixin(ClientProtocol):
|
|
|
161
159
|
)
|
|
162
160
|
try:
|
|
163
161
|
await asyncio.wait_for(fut, timeout=DEFAULT_TIMEOUT)
|
|
164
|
-
self.logger.info(
|
|
165
|
-
"File upload completed successfully (fileId=%s)", file_id
|
|
166
|
-
)
|
|
162
|
+
self.logger.info("File upload completed successfully (fileId=%s)", file_id)
|
|
167
163
|
return Attach(_type=AttachType.FILE, file_id=file_id)
|
|
168
164
|
except asyncio.TimeoutError:
|
|
169
165
|
self.logger.warning(
|
|
@@ -190,9 +186,7 @@ class MessageMixin(ClientProtocol):
|
|
|
190
186
|
MixinsUtils.handle_error(data)
|
|
191
187
|
|
|
192
188
|
url = data.get("payload", {}).get("info", [None])[0].get("url", None)
|
|
193
|
-
video_id = (
|
|
194
|
-
data.get("payload", {}).get("info", [None])[0].get("videoId", None)
|
|
195
|
-
)
|
|
189
|
+
video_id = data.get("payload", {}).get("info", [None])[0].get("videoId", None)
|
|
196
190
|
if not url or not video_id:
|
|
197
191
|
self.logger.error("No upload URL or video ID received")
|
|
198
192
|
return None
|
|
@@ -207,9 +201,7 @@ class MessageMixin(ClientProtocol):
|
|
|
207
201
|
|
|
208
202
|
# Настройки для ClientSession
|
|
209
203
|
connector = TCPConnector(limit=0)
|
|
210
|
-
timeout = aiohttp.ClientTimeout(
|
|
211
|
-
total=900, sock_read=60
|
|
212
|
-
) # 15 минут на видео
|
|
204
|
+
timeout = aiohttp.ClientTimeout(total=900, sock_read=60) # 15 минут на видео
|
|
213
205
|
|
|
214
206
|
headers = {
|
|
215
207
|
"Content-Disposition": f"attachment; filename={video.file_name}",
|
|
@@ -226,26 +218,20 @@ class MessageMixin(ClientProtocol):
|
|
|
226
218
|
self.logger.exception("Failed to register file upload waiter")
|
|
227
219
|
|
|
228
220
|
try:
|
|
229
|
-
async with ClientSession(
|
|
230
|
-
connector=connector, timeout=timeout
|
|
231
|
-
) as session:
|
|
221
|
+
async with ClientSession(connector=connector, timeout=timeout) as session:
|
|
232
222
|
async with session.post(
|
|
233
223
|
url=url,
|
|
234
224
|
headers=headers,
|
|
235
225
|
data=file_bytes,
|
|
236
226
|
) as response:
|
|
237
227
|
if response.status != HTTPStatus.OK:
|
|
238
|
-
self.logger.error(
|
|
239
|
-
"Upload failed with status %s", response.status
|
|
240
|
-
)
|
|
228
|
+
self.logger.error("Upload failed with status %s", response.status)
|
|
241
229
|
self._file_upload_waiters.pop(int(video_id), None)
|
|
242
230
|
return None
|
|
243
231
|
|
|
244
232
|
try:
|
|
245
233
|
await asyncio.wait_for(fut, timeout=DEFAULT_TIMEOUT)
|
|
246
|
-
return Attach(
|
|
247
|
-
_type=AttachType.VIDEO, video_id=video_id, token=token
|
|
248
|
-
)
|
|
234
|
+
return Attach(_type=AttachType.VIDEO, video_id=video_id, token=token)
|
|
249
235
|
except asyncio.TimeoutError:
|
|
250
236
|
self.logger.warning(
|
|
251
237
|
"Timed out waiting for video processing notification for videoId=%s",
|
|
@@ -337,9 +323,7 @@ class MessageMixin(ClientProtocol):
|
|
|
337
323
|
elif isinstance(attach, File):
|
|
338
324
|
uploaded = await self._upload_file(attach)
|
|
339
325
|
if uploaded and uploaded.file_id:
|
|
340
|
-
return AttachFilePayload(file_id=uploaded.file_id).model_dump(
|
|
341
|
-
by_alias=True
|
|
342
|
-
)
|
|
326
|
+
return AttachFilePayload(file_id=uploaded.file_id).model_dump(by_alias=True)
|
|
343
327
|
elif isinstance(attach, Video):
|
|
344
328
|
uploaded = await self._upload_video(attach)
|
|
345
329
|
if uploaded and uploaded.video_id and uploaded.token:
|
|
@@ -391,9 +375,7 @@ class MessageMixin(ClientProtocol):
|
|
|
391
375
|
self.logger.info("Uploading attachment for message")
|
|
392
376
|
result = await self._upload_attachment(attachment)
|
|
393
377
|
if not result:
|
|
394
|
-
raise Error(
|
|
395
|
-
"upload_failed", "Failed to upload attachment", "Upload Error"
|
|
396
|
-
)
|
|
378
|
+
raise Error("upload_failed", "Failed to upload attachment", "Upload Error")
|
|
397
379
|
attaches.append(result)
|
|
398
380
|
|
|
399
381
|
elif attachments:
|
|
@@ -403,14 +385,10 @@ class MessageMixin(ClientProtocol):
|
|
|
403
385
|
if result:
|
|
404
386
|
attaches.append(result)
|
|
405
387
|
else:
|
|
406
|
-
raise Error(
|
|
407
|
-
"upload_failed", "Failed to upload attachment", "Upload Error"
|
|
408
|
-
)
|
|
388
|
+
raise Error("upload_failed", "Failed to upload attachment", "Upload Error")
|
|
409
389
|
|
|
410
390
|
if not attaches:
|
|
411
|
-
raise Error(
|
|
412
|
-
"upload_failed", "All attachments failed to upload", "Upload Error"
|
|
413
|
-
)
|
|
391
|
+
raise Error("upload_failed", "All attachments failed to upload", "Upload Error")
|
|
414
392
|
|
|
415
393
|
elements = []
|
|
416
394
|
clean_text = None
|
|
@@ -418,8 +396,7 @@ class MessageMixin(ClientProtocol):
|
|
|
418
396
|
if raw_elements:
|
|
419
397
|
clean_text = parsed_text
|
|
420
398
|
elements = [
|
|
421
|
-
MessageElement(type=e.type, length=e.length, from_=e.from_)
|
|
422
|
-
for e in raw_elements
|
|
399
|
+
MessageElement(type=e.type, length=e.length, from_=e.from_) for e in raw_elements
|
|
423
400
|
]
|
|
424
401
|
|
|
425
402
|
payload = SendMessagePayload(
|
|
@@ -447,9 +424,7 @@ class MessageMixin(ClientProtocol):
|
|
|
447
424
|
msg = Message.from_dict(data["payload"]) if data.get("payload") else None
|
|
448
425
|
self.logger.debug("send_message result: %r", msg)
|
|
449
426
|
if not msg:
|
|
450
|
-
raise Error(
|
|
451
|
-
"no_message", "Message data missing in response", "Message Error"
|
|
452
|
-
)
|
|
427
|
+
raise Error("no_message", "Message data missing in response", "Message Error")
|
|
453
428
|
|
|
454
429
|
return msg
|
|
455
430
|
|
|
@@ -481,9 +456,7 @@ class MessageMixin(ClientProtocol):
|
|
|
481
456
|
:rtype: Message | None
|
|
482
457
|
:raises Error: Если редактирование не удалось.
|
|
483
458
|
"""
|
|
484
|
-
self.logger.info(
|
|
485
|
-
"Editing message chat_id=%s message_id=%s", chat_id, message_id
|
|
486
|
-
)
|
|
459
|
+
self.logger.info("Editing message chat_id=%s message_id=%s", chat_id, message_id)
|
|
487
460
|
|
|
488
461
|
if attachments and attachment:
|
|
489
462
|
self.logger.warning("Both photo and photos provided; using photos")
|
|
@@ -494,9 +467,7 @@ class MessageMixin(ClientProtocol):
|
|
|
494
467
|
self.logger.info("Uploading attachment for message")
|
|
495
468
|
result = await self._upload_attachment(attachment)
|
|
496
469
|
if not result:
|
|
497
|
-
raise Error(
|
|
498
|
-
"upload_failed", "Failed to upload attachment", "Upload Error"
|
|
499
|
-
)
|
|
470
|
+
raise Error("upload_failed", "Failed to upload attachment", "Upload Error")
|
|
500
471
|
attaches.append(result)
|
|
501
472
|
|
|
502
473
|
elif attachments:
|
|
@@ -506,14 +477,10 @@ class MessageMixin(ClientProtocol):
|
|
|
506
477
|
if result:
|
|
507
478
|
attaches.append(result)
|
|
508
479
|
else:
|
|
509
|
-
raise Error(
|
|
510
|
-
"upload_failed", "Failed to upload attachment", "Upload Error"
|
|
511
|
-
)
|
|
480
|
+
raise Error("upload_failed", "Failed to upload attachment", "Upload Error")
|
|
512
481
|
|
|
513
482
|
if not attaches:
|
|
514
|
-
raise Error(
|
|
515
|
-
"upload_failed", "All attachments failed to upload", "Upload Error"
|
|
516
|
-
)
|
|
483
|
+
raise Error("upload_failed", "All attachments failed to upload", "Upload Error")
|
|
517
484
|
|
|
518
485
|
elements = []
|
|
519
486
|
clean_text = None
|
|
@@ -521,8 +488,7 @@ class MessageMixin(ClientProtocol):
|
|
|
521
488
|
if raw_elements:
|
|
522
489
|
clean_text = Formatting.get_elements_from_markdown(text)[1]
|
|
523
490
|
elements = [
|
|
524
|
-
MessageElement(type=e.type, length=e.length, from_=e.from_)
|
|
525
|
-
for e in raw_elements
|
|
491
|
+
MessageElement(type=e.type, length=e.length, from_=e.from_) for e in raw_elements
|
|
526
492
|
]
|
|
527
493
|
|
|
528
494
|
payload = EditMessagePayload(
|
|
@@ -546,9 +512,7 @@ class MessageMixin(ClientProtocol):
|
|
|
546
512
|
msg = Message.from_dict(data["payload"]) if data.get("payload") else None
|
|
547
513
|
self.logger.debug("edit_message result: %r", msg)
|
|
548
514
|
if not msg:
|
|
549
|
-
raise Error(
|
|
550
|
-
"no_message", "Message data missing in response", "Message Error"
|
|
551
|
-
)
|
|
515
|
+
raise Error("no_message", "Message data missing in response", "Message Error")
|
|
552
516
|
|
|
553
517
|
return msg
|
|
554
518
|
|
|
@@ -597,9 +561,7 @@ class MessageMixin(ClientProtocol):
|
|
|
597
561
|
self.logger.debug("delete_message success")
|
|
598
562
|
return True
|
|
599
563
|
|
|
600
|
-
async def pin_message(
|
|
601
|
-
self, chat_id: int, message_id: int, notify_pin: bool
|
|
602
|
-
) -> bool:
|
|
564
|
+
async def pin_message(self, chat_id: int, message_id: int, notify_pin: bool) -> bool:
|
|
603
565
|
"""
|
|
604
566
|
Закрепляет сообщение в чате.
|
|
605
567
|
|
|
@@ -667,16 +629,12 @@ class MessageMixin(ClientProtocol):
|
|
|
667
629
|
|
|
668
630
|
self.logger.debug("Payload dict keys: %s", list(payload.keys()))
|
|
669
631
|
|
|
670
|
-
data = await self._send_and_wait(
|
|
671
|
-
opcode=Opcode.CHAT_HISTORY, payload=payload, timeout=10
|
|
672
|
-
)
|
|
632
|
+
data = await self._send_and_wait(opcode=Opcode.CHAT_HISTORY, payload=payload, timeout=10)
|
|
673
633
|
|
|
674
634
|
if data.get("payload", {}).get("error"):
|
|
675
635
|
MixinsUtils.handle_error(data)
|
|
676
636
|
|
|
677
|
-
messages = [
|
|
678
|
-
Message.from_dict(msg) for msg in data["payload"].get("messages", [])
|
|
679
|
-
]
|
|
637
|
+
messages = [Message.from_dict(msg) for msg in data["payload"].get("messages", [])]
|
|
680
638
|
self.logger.debug("History fetched: %d messages", len(messages))
|
|
681
639
|
return messages
|
|
682
640
|
|
|
@@ -797,9 +755,7 @@ class MessageMixin(ClientProtocol):
|
|
|
797
755
|
reaction=ReactionInfoPayload(id=reaction),
|
|
798
756
|
).model_dump(by_alias=True)
|
|
799
757
|
|
|
800
|
-
data = await self._send_and_wait(
|
|
801
|
-
opcode=Opcode.MSG_REACTION, payload=payload
|
|
802
|
-
)
|
|
758
|
+
data = await self._send_and_wait(opcode=Opcode.MSG_REACTION, payload=payload)
|
|
803
759
|
|
|
804
760
|
if data.get("payload", {}).get("error"):
|
|
805
761
|
MixinsUtils.handle_error(data)
|
|
@@ -833,21 +789,17 @@ class MessageMixin(ClientProtocol):
|
|
|
833
789
|
message_ids,
|
|
834
790
|
)
|
|
835
791
|
|
|
836
|
-
payload = GetReactionsPayload(
|
|
837
|
-
|
|
838
|
-
).model_dump(by_alias=True)
|
|
839
|
-
|
|
840
|
-
data = await self._send_and_wait(
|
|
841
|
-
opcode=Opcode.MSG_GET_REACTIONS, payload=payload
|
|
792
|
+
payload = GetReactionsPayload(chat_id=chat_id, message_ids=message_ids).model_dump(
|
|
793
|
+
by_alias=True
|
|
842
794
|
)
|
|
843
795
|
|
|
796
|
+
data = await self._send_and_wait(opcode=Opcode.MSG_GET_REACTIONS, payload=payload)
|
|
797
|
+
|
|
844
798
|
if data.get("payload", {}).get("error"):
|
|
845
799
|
MixinsUtils.handle_error(data)
|
|
846
800
|
|
|
847
801
|
reactions = {}
|
|
848
|
-
for msg_id, reaction_data in (
|
|
849
|
-
data.get("payload", {}).get("messagesReactions", {}).items()
|
|
850
|
-
):
|
|
802
|
+
for msg_id, reaction_data in data.get("payload", {}).get("messagesReactions", {}).items():
|
|
851
803
|
reactions[msg_id] = ReactionInfo.from_dict(reaction_data)
|
|
852
804
|
|
|
853
805
|
self.logger.debug("get_reactions success")
|
|
@@ -879,18 +831,14 @@ class MessageMixin(ClientProtocol):
|
|
|
879
831
|
message_id=message_id,
|
|
880
832
|
).model_dump(by_alias=True)
|
|
881
833
|
|
|
882
|
-
data = await self._send_and_wait(
|
|
883
|
-
opcode=Opcode.MSG_CANCEL_REACTION, payload=payload
|
|
884
|
-
)
|
|
834
|
+
data = await self._send_and_wait(opcode=Opcode.MSG_CANCEL_REACTION, payload=payload)
|
|
885
835
|
|
|
886
836
|
if data.get("payload", {}).get("error"):
|
|
887
837
|
MixinsUtils.handle_error(data)
|
|
888
838
|
|
|
889
839
|
self.logger.debug("remove_reaction success")
|
|
890
840
|
if not data.get("payload"):
|
|
891
|
-
raise Error(
|
|
892
|
-
"no_reaction", "Reaction data missing in response", "Reaction Error"
|
|
893
|
-
)
|
|
841
|
+
raise Error("no_reaction", "Reaction data missing in response", "Reaction Error")
|
|
894
842
|
|
|
895
843
|
reaction = ReactionInfo.from_dict(data["payload"]["reactionInfo"])
|
|
896
844
|
if not reaction:
|
|
@@ -901,3 +849,31 @@ class MessageMixin(ClientProtocol):
|
|
|
901
849
|
)
|
|
902
850
|
|
|
903
851
|
return reaction
|
|
852
|
+
|
|
853
|
+
async def read_message(self, message_id: int, chat_id: int) -> ReadState:
|
|
854
|
+
"""
|
|
855
|
+
Отмечает сообщение как прочитанное.
|
|
856
|
+
|
|
857
|
+
:param message_id: ID сообщения
|
|
858
|
+
:type message_id: int
|
|
859
|
+
:param chat_id: ID чата
|
|
860
|
+
:type chat_id: int
|
|
861
|
+
:return: Объект ReadState
|
|
862
|
+
:rtype: ReadState
|
|
863
|
+
"""
|
|
864
|
+
self.logger.info("Marking message as read chat_id=%s message_id=%s", chat_id, message_id)
|
|
865
|
+
|
|
866
|
+
payload = ReadMessagesPayload(
|
|
867
|
+
type=ReadAction.READ_MESSAGE,
|
|
868
|
+
chat_id=chat_id,
|
|
869
|
+
message_id=str(message_id),
|
|
870
|
+
mark=int(time.time() * 1000),
|
|
871
|
+
).model_dump(by_alias=True)
|
|
872
|
+
|
|
873
|
+
data = await self._send_and_wait(opcode=Opcode.CHAT_MARK, payload=payload)
|
|
874
|
+
|
|
875
|
+
if data.get("payload", {}).get("error"):
|
|
876
|
+
MixinsUtils.handle_error(data)
|
|
877
|
+
|
|
878
|
+
self.logger.debug("read_message success")
|
|
879
|
+
return ReadState.from_dict(data["payload"])
|
pymax/mixins/scheduler.py
CHANGED
pymax/mixins/self.py
CHANGED
|
@@ -8,8 +8,6 @@ import aiohttp
|
|
|
8
8
|
|
|
9
9
|
from pymax.exceptions import Error
|
|
10
10
|
from pymax.files import Photo
|
|
11
|
-
from pymax.interfaces import ClientProtocol
|
|
12
|
-
from pymax.mixins.utils import MixinsUtils
|
|
13
11
|
from pymax.payloads import (
|
|
14
12
|
ChangeProfilePayload,
|
|
15
13
|
CreateFolderPayload,
|
|
@@ -18,8 +16,10 @@ from pymax.payloads import (
|
|
|
18
16
|
UpdateFolderPayload,
|
|
19
17
|
UploadPayload,
|
|
20
18
|
)
|
|
19
|
+
from pymax.protocols import ClientProtocol
|
|
21
20
|
from pymax.static.enum import Opcode
|
|
22
21
|
from pymax.types import Folder, FolderList, FolderUpdate, Me
|
|
22
|
+
from pymax.utils import MixinsUtils
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
class SelfMixin(ClientProtocol):
|