maxapi-python 1.1.10__tar.gz → 1.1.11__tar.gz
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.1.10 → maxapi_python-1.1.11}/.github/FUNDING.yml +1 -1
- {maxapi_python-1.1.10 → maxapi_python-1.1.11}/PKG-INFO +1 -1
- {maxapi_python-1.1.10 → maxapi_python-1.1.11}/examples/example.py +20 -5
- {maxapi_python-1.1.10 → maxapi_python-1.1.11}/pyproject.toml +1 -1
- {maxapi_python-1.1.10 → maxapi_python-1.1.11}/src/pymax/mixins/handler.py +2 -1
- {maxapi_python-1.1.10 → maxapi_python-1.1.11}/src/pymax/mixins/message.py +148 -1
- {maxapi_python-1.1.10 → maxapi_python-1.1.11}/src/pymax/payloads.py +21 -0
- {maxapi_python-1.1.10 → maxapi_python-1.1.11}/src/pymax/types.py +42 -2
- {maxapi_python-1.1.10 → maxapi_python-1.1.11}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {maxapi_python-1.1.10 → maxapi_python-1.1.11}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {maxapi_python-1.1.10 → maxapi_python-1.1.11}/.github/ISSUE_TEMPLATE/refactor.md +0 -0
- {maxapi_python-1.1.10 → maxapi_python-1.1.11}/.github/pull_request_template.md +0 -0
- {maxapi_python-1.1.10 → maxapi_python-1.1.11}/.github/workflows/publish.yml +0 -0
- {maxapi_python-1.1.10 → maxapi_python-1.1.11}/.gitignore +0 -0
- {maxapi_python-1.1.10 → maxapi_python-1.1.11}/LICENSE +0 -0
- {maxapi_python-1.1.10 → maxapi_python-1.1.11}/README.md +0 -0
- {maxapi_python-1.1.10 → maxapi_python-1.1.11}/assets/icon.svg +0 -0
- {maxapi_python-1.1.10 → maxapi_python-1.1.11}/assets/logo.svg +0 -0
- {maxapi_python-1.1.10 → maxapi_python-1.1.11}/docs/api.md +0 -0
- {maxapi_python-1.1.10 → maxapi_python-1.1.11}/docs/assets/icon.svg +0 -0
- {maxapi_python-1.1.10 → maxapi_python-1.1.11}/docs/examples.md +0 -0
- {maxapi_python-1.1.10 → maxapi_python-1.1.11}/docs/index.md +0 -0
- {maxapi_python-1.1.10 → maxapi_python-1.1.11}/mkdocs.yml +0 -0
- {maxapi_python-1.1.10 → maxapi_python-1.1.11}/ruff.toml +0 -0
- {maxapi_python-1.1.10 → maxapi_python-1.1.11}/scripts/build.py +0 -0
- {maxapi_python-1.1.10 → maxapi_python-1.1.11}/src/pymax/__init__.py +0 -0
- {maxapi_python-1.1.10 → maxapi_python-1.1.11}/src/pymax/core.py +0 -0
- {maxapi_python-1.1.10 → maxapi_python-1.1.11}/src/pymax/crud.py +0 -0
- {maxapi_python-1.1.10 → maxapi_python-1.1.11}/src/pymax/exceptions.py +0 -0
- {maxapi_python-1.1.10 → maxapi_python-1.1.11}/src/pymax/files.py +0 -0
- {maxapi_python-1.1.10 → maxapi_python-1.1.11}/src/pymax/filters.py +0 -0
- {maxapi_python-1.1.10 → maxapi_python-1.1.11}/src/pymax/interfaces.py +0 -0
- {maxapi_python-1.1.10 → maxapi_python-1.1.11}/src/pymax/mixins/__init__.py +0 -0
- {maxapi_python-1.1.10 → maxapi_python-1.1.11}/src/pymax/mixins/auth.py +0 -0
- {maxapi_python-1.1.10 → maxapi_python-1.1.11}/src/pymax/mixins/channel.py +0 -0
- {maxapi_python-1.1.10 → maxapi_python-1.1.11}/src/pymax/mixins/group.py +0 -0
- {maxapi_python-1.1.10 → maxapi_python-1.1.11}/src/pymax/mixins/self.py +0 -0
- {maxapi_python-1.1.10 → maxapi_python-1.1.11}/src/pymax/mixins/socket.py +0 -0
- {maxapi_python-1.1.10 → maxapi_python-1.1.11}/src/pymax/mixins/telemetry.py +0 -0
- {maxapi_python-1.1.10 → maxapi_python-1.1.11}/src/pymax/mixins/user.py +0 -0
- {maxapi_python-1.1.10 → maxapi_python-1.1.11}/src/pymax/mixins/websocket.py +0 -0
- {maxapi_python-1.1.10 → maxapi_python-1.1.11}/src/pymax/models.py +0 -0
- {maxapi_python-1.1.10 → maxapi_python-1.1.11}/src/pymax/navigation.py +0 -0
- {maxapi_python-1.1.10 → maxapi_python-1.1.11}/src/pymax/static.py +0 -0
- {maxapi_python-1.1.10 → maxapi_python-1.1.11}/src/pymax/utils.py +0 -0
|
@@ -12,4 +12,4 @@ lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cl
|
|
|
12
12
|
polar: # Replace with a single Polar username
|
|
13
13
|
buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
|
|
14
14
|
thanks_dev: # Replace with a single thanks.dev username
|
|
15
|
-
custom: ['
|
|
15
|
+
custom: ['']
|
|
@@ -21,12 +21,27 @@ async def handle_message(message: Message) -> None:
|
|
|
21
21
|
@client.on_start
|
|
22
22
|
async def handle_start() -> None:
|
|
23
23
|
print("Client started successfully!")
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
react_info = await client.add_reaction(
|
|
25
|
+
chat_id=0, message_id="115368067020359151", reaction="👍"
|
|
26
|
+
)
|
|
27
|
+
if react_info:
|
|
28
|
+
print("Reaction added!")
|
|
29
|
+
print(react_info.total_count)
|
|
30
|
+
react_info = await client.get_reactions(
|
|
31
|
+
chat_id=0, message_ids=["115368067020359151"]
|
|
32
|
+
)
|
|
33
|
+
if react_info:
|
|
34
|
+
print("Reactions fetched!")
|
|
35
|
+
for msg_id, info in react_info.items():
|
|
36
|
+
print(f"Message ID: {msg_id}, Total Reactions: {info.total_count}")
|
|
37
|
+
react_info = await client.remove_reaction(
|
|
38
|
+
chat_id=0, message_id="115368067020359151"
|
|
39
|
+
)
|
|
40
|
+
if react_info:
|
|
41
|
+
print("Reaction removed!")
|
|
42
|
+
print(react_info.total_count)
|
|
27
43
|
# print(client.dialogs)
|
|
28
|
-
|
|
29
|
-
print(chat.title)
|
|
44
|
+
|
|
30
45
|
# if history:
|
|
31
46
|
# for message in history:
|
|
32
47
|
# if message.link:
|
|
@@ -6,20 +6,30 @@ from aiohttp import ClientSession
|
|
|
6
6
|
from pymax.files import File, Photo, Video
|
|
7
7
|
from pymax.interfaces import ClientProtocol
|
|
8
8
|
from pymax.payloads import (
|
|
9
|
+
AddReactionPayload,
|
|
9
10
|
AttachPhotoPayload,
|
|
10
11
|
DeleteMessagePayload,
|
|
11
12
|
EditMessagePayload,
|
|
12
13
|
FetchHistoryPayload,
|
|
13
14
|
GetFilePayload,
|
|
15
|
+
GetReactionsPayload,
|
|
14
16
|
GetVideoPayload,
|
|
15
17
|
PinMessagePayload,
|
|
18
|
+
ReactionInfoPayload,
|
|
19
|
+
RemoveReactionPayload,
|
|
16
20
|
ReplyLink,
|
|
17
21
|
SendMessagePayload,
|
|
18
22
|
SendMessagePayloadMessage,
|
|
19
23
|
UploadPhotoPayload,
|
|
20
24
|
)
|
|
21
25
|
from pymax.static import AttachType, Opcode
|
|
22
|
-
from pymax.types import
|
|
26
|
+
from pymax.types import (
|
|
27
|
+
Attach,
|
|
28
|
+
FileRequest,
|
|
29
|
+
Message,
|
|
30
|
+
ReactionInfo,
|
|
31
|
+
VideoRequest,
|
|
32
|
+
)
|
|
23
33
|
|
|
24
34
|
|
|
25
35
|
class MessageMixin(ClientProtocol):
|
|
@@ -375,3 +385,140 @@ class MessageMixin(ClientProtocol):
|
|
|
375
385
|
except Exception:
|
|
376
386
|
self.logger.exception("Get video error")
|
|
377
387
|
return None
|
|
388
|
+
|
|
389
|
+
async def add_reaction(
|
|
390
|
+
self,
|
|
391
|
+
chat_id: int,
|
|
392
|
+
message_id: str,
|
|
393
|
+
reaction: str,
|
|
394
|
+
) -> ReactionInfo | None:
|
|
395
|
+
"""
|
|
396
|
+
Добавляет реакцию к сообщению.
|
|
397
|
+
|
|
398
|
+
Args:
|
|
399
|
+
chat_id (int): ID чата
|
|
400
|
+
message_id (int): ID сообщения
|
|
401
|
+
reaction (str): Реакция (эмодзи)
|
|
402
|
+
|
|
403
|
+
Returns:
|
|
404
|
+
ReactionInfo | None: Информация о реакции или None при ошибке.
|
|
405
|
+
"""
|
|
406
|
+
try:
|
|
407
|
+
self.logger.info(
|
|
408
|
+
"Adding reaction to message chat_id=%s message_id=%s reaction=%s",
|
|
409
|
+
chat_id,
|
|
410
|
+
message_id,
|
|
411
|
+
reaction,
|
|
412
|
+
)
|
|
413
|
+
|
|
414
|
+
payload = AddReactionPayload(
|
|
415
|
+
chat_id=chat_id,
|
|
416
|
+
message_id=message_id,
|
|
417
|
+
reaction=ReactionInfoPayload(id=reaction),
|
|
418
|
+
).model_dump(by_alias=True)
|
|
419
|
+
|
|
420
|
+
data = await self._send_and_wait(
|
|
421
|
+
opcode=Opcode.MSG_REACTION, payload=payload
|
|
422
|
+
)
|
|
423
|
+
if error := data.get("payload", {}).get("error"):
|
|
424
|
+
self.logger.error("Add reaction error: %s", error)
|
|
425
|
+
return None
|
|
426
|
+
|
|
427
|
+
self.logger.debug("add_reaction success")
|
|
428
|
+
return (
|
|
429
|
+
ReactionInfo.from_dict(data["payload"]["reactionInfo"])
|
|
430
|
+
if data.get("payload")
|
|
431
|
+
else None
|
|
432
|
+
)
|
|
433
|
+
except Exception:
|
|
434
|
+
self.logger.exception("Add reaction failed")
|
|
435
|
+
return None
|
|
436
|
+
|
|
437
|
+
async def get_reactions(
|
|
438
|
+
self, chat_id: int, message_ids: list[str]
|
|
439
|
+
) -> dict[str, ReactionInfo] | None:
|
|
440
|
+
"""
|
|
441
|
+
Получает реакции на сообщения.
|
|
442
|
+
|
|
443
|
+
Args:
|
|
444
|
+
chat_id (int): ID чата
|
|
445
|
+
message_ids (list[str]): Список ID сообщений
|
|
446
|
+
|
|
447
|
+
Returns:
|
|
448
|
+
dict[str, ReactionInfo] | None: Словарь с ID сообщений и информацией о реакциях или None при ошибке.
|
|
449
|
+
"""
|
|
450
|
+
try:
|
|
451
|
+
self.logger.info(
|
|
452
|
+
"Getting reactions for messages chat_id=%s message_ids=%s",
|
|
453
|
+
chat_id,
|
|
454
|
+
message_ids,
|
|
455
|
+
)
|
|
456
|
+
|
|
457
|
+
payload = GetReactionsPayload(
|
|
458
|
+
chat_id=chat_id, message_ids=message_ids
|
|
459
|
+
).model_dump(by_alias=True)
|
|
460
|
+
|
|
461
|
+
data = await self._send_and_wait(
|
|
462
|
+
opcode=Opcode.MSG_GET_REACTIONS, payload=payload
|
|
463
|
+
)
|
|
464
|
+
if error := data.get("payload", {}).get("error"):
|
|
465
|
+
self.logger.error("Get reactions error: %s", error)
|
|
466
|
+
return None
|
|
467
|
+
|
|
468
|
+
reactions = {}
|
|
469
|
+
for msg_id, reaction_data in (
|
|
470
|
+
data.get("payload", {}).get("messagesReactions", {}).items()
|
|
471
|
+
):
|
|
472
|
+
reactions[msg_id] = ReactionInfo.from_dict(reaction_data)
|
|
473
|
+
|
|
474
|
+
self.logger.debug("get_reactions success")
|
|
475
|
+
|
|
476
|
+
return reactions
|
|
477
|
+
|
|
478
|
+
except Exception:
|
|
479
|
+
self.logger.exception("Get reactions failed")
|
|
480
|
+
return None
|
|
481
|
+
|
|
482
|
+
async def remove_reaction(
|
|
483
|
+
self,
|
|
484
|
+
chat_id: int,
|
|
485
|
+
message_id: str,
|
|
486
|
+
) -> ReactionInfo | None:
|
|
487
|
+
"""
|
|
488
|
+
Удаляет реакцию с сообщения.
|
|
489
|
+
|
|
490
|
+
Args:
|
|
491
|
+
chat_id (int): ID чата
|
|
492
|
+
message_id (str): ID сообщения
|
|
493
|
+
|
|
494
|
+
Returns:
|
|
495
|
+
ReactionInfo | None: Информация о реакции или None при ошибке.
|
|
496
|
+
"""
|
|
497
|
+
try:
|
|
498
|
+
self.logger.info(
|
|
499
|
+
"Removing reaction from message chat_id=%s message_id=%s",
|
|
500
|
+
chat_id,
|
|
501
|
+
message_id,
|
|
502
|
+
)
|
|
503
|
+
|
|
504
|
+
payload = RemoveReactionPayload(
|
|
505
|
+
chat_id=chat_id,
|
|
506
|
+
message_id=message_id,
|
|
507
|
+
).model_dump(by_alias=True)
|
|
508
|
+
|
|
509
|
+
data = await self._send_and_wait(
|
|
510
|
+
opcode=Opcode.MSG_CANCEL_REACTION, payload=payload
|
|
511
|
+
)
|
|
512
|
+
if error := data.get("payload", {}).get("error"):
|
|
513
|
+
self.logger.error("Remove reaction error: %s", error)
|
|
514
|
+
return None
|
|
515
|
+
|
|
516
|
+
self.logger.debug("remove_reaction success")
|
|
517
|
+
return (
|
|
518
|
+
ReactionInfo.from_dict(data["payload"]["reactionInfo"])
|
|
519
|
+
if data.get("payload")
|
|
520
|
+
else None
|
|
521
|
+
)
|
|
522
|
+
except Exception:
|
|
523
|
+
self.logger.exception("Remove reaction failed")
|
|
524
|
+
return None
|
|
@@ -213,3 +213,24 @@ class SearchByPhonePayload(CamelModel):
|
|
|
213
213
|
|
|
214
214
|
class JoinGroupPayload(CamelModel):
|
|
215
215
|
link: str
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
class ReactionInfoPayload(CamelModel):
|
|
219
|
+
reaction_type: str = "EMOJI"
|
|
220
|
+
id: str
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
class AddReactionPayload(CamelModel):
|
|
224
|
+
chat_id: int
|
|
225
|
+
message_id: str
|
|
226
|
+
reaction: ReactionInfoPayload
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
class GetReactionsPayload(CamelModel):
|
|
230
|
+
chat_id: int
|
|
231
|
+
message_ids: list[str]
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
class RemoveReactionPayload(CamelModel):
|
|
235
|
+
chat_id: int
|
|
236
|
+
message_id: str
|
|
@@ -317,13 +317,51 @@ class MessageLink:
|
|
|
317
317
|
return f"MessageLink: {self.chat_id}/{self.message.id}"
|
|
318
318
|
|
|
319
319
|
|
|
320
|
+
class ReactionCounter:
|
|
321
|
+
def __init__(self, count: int, reaction: str) -> None:
|
|
322
|
+
self.count = count
|
|
323
|
+
self.reaction = reaction
|
|
324
|
+
|
|
325
|
+
@classmethod
|
|
326
|
+
def from_dict(cls, data: dict[str, Any]) -> "ReactionCounter":
|
|
327
|
+
return cls(count=data["count"], reaction=data["reaction"])
|
|
328
|
+
|
|
329
|
+
@override
|
|
330
|
+
def __repr__(self) -> str:
|
|
331
|
+
return f"ReactionCounter(count={self.count!r}, reaction={self.reaction!r})"
|
|
332
|
+
|
|
333
|
+
@override
|
|
334
|
+
def __str__(self) -> str:
|
|
335
|
+
return f"{self.reaction}: {self.count}"
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
class ReactionInfo:
|
|
339
|
+
def __init__(
|
|
340
|
+
self,
|
|
341
|
+
total_count: int,
|
|
342
|
+
counters: list[ReactionCounter],
|
|
343
|
+
your_reaction: str | None = None,
|
|
344
|
+
) -> None:
|
|
345
|
+
self.total_count = total_count
|
|
346
|
+
self.counters = counters
|
|
347
|
+
self.your_reaction = your_reaction
|
|
348
|
+
|
|
349
|
+
@classmethod
|
|
350
|
+
def from_dict(cls, data: dict[str, Any]) -> "ReactionInfo":
|
|
351
|
+
return cls(
|
|
352
|
+
total_count=data.get("totalCount", 0),
|
|
353
|
+
counters=[ReactionCounter.from_dict(c) for c in data.get("counters", [])],
|
|
354
|
+
your_reaction=data.get("yourReaction"),
|
|
355
|
+
)
|
|
356
|
+
|
|
357
|
+
|
|
320
358
|
class Message:
|
|
321
359
|
def __init__(
|
|
322
360
|
self,
|
|
323
361
|
chat_id: int | None,
|
|
324
362
|
sender: int | None,
|
|
325
363
|
elements: list[Element] | None,
|
|
326
|
-
reaction_info:
|
|
364
|
+
reaction_info: ReactionInfo | None,
|
|
327
365
|
options: int | None,
|
|
328
366
|
id: int,
|
|
329
367
|
time: int,
|
|
@@ -373,7 +411,9 @@ class Message:
|
|
|
373
411
|
link=MessageLink.from_dict(message.get("link"))
|
|
374
412
|
if message.get("link")
|
|
375
413
|
else None,
|
|
376
|
-
reaction_info=message.get("reactionInfo")
|
|
414
|
+
reaction_info=ReactionInfo.from_dict(message.get("reactionInfo"))
|
|
415
|
+
if message.get("reactionInfo")
|
|
416
|
+
else None,
|
|
377
417
|
)
|
|
378
418
|
|
|
379
419
|
@override
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|