maxapi-python 1.2.3__py3-none-any.whl → 1.2.4__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/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
- chat_id=chat_id, message_ids=message_ids
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
@@ -3,7 +3,7 @@ import traceback
3
3
  from collections.abc import Awaitable, Callable
4
4
  from typing import Any
5
5
 
6
- from pymax.interfaces import ClientProtocol
6
+ from pymax.protocols import ClientProtocol
7
7
 
8
8
 
9
9
  class SchedulerMixin(ClientProtocol):
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):