maxapi-python 2.4.1__py3-none-any.whl → 2.5.0__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-2.4.1.dist-info → maxapi_python-2.5.0.dist-info}/METADATA +17 -9
- {maxapi_python-2.4.1.dist-info → maxapi_python-2.5.0.dist-info}/RECORD +74 -45
- {maxapi_python-2.4.1.dist-info → maxapi_python-2.5.0.dist-info}/WHEEL +1 -1
- pymax/__init__.py +5 -1
- pymax/_data/apk_fingerprints.json +272 -0
- pymax/api/auth/service.py +1 -0
- pymax/api/auth/types.py +3 -0
- pymax/api/bots/payloads.py +8 -0
- pymax/api/bots/service.py +13 -1
- pymax/api/chats/enums.py +1 -0
- pymax/api/chats/payloads.py +19 -0
- pymax/api/chats/service.py +34 -0
- pymax/api/facade.py +2 -0
- pymax/api/messages/enums.py +1 -0
- pymax/api/messages/payloads.py +47 -1
- pymax/api/messages/service.py +308 -24
- pymax/api/response.py +1 -1
- pymax/api/stories/__init__.py +2 -0
- pymax/api/stories/enums.py +19 -0
- pymax/api/stories/payloads.py +64 -0
- pymax/api/stories/service.py +198 -0
- pymax/api/uploads/payloads.py +10 -1
- pymax/api/uploads/service.py +94 -7
- pymax/app.py +16 -4
- pymax/auth/qr.py +11 -1
- pymax/base.py +70 -25
- pymax/client.py +0 -1
- pymax/config.py +38 -32
- pymax/di/__init__.py +2 -0
- pymax/di/context.py +18 -0
- pymax/di/resolver.py +38 -0
- pymax/dispatch/__init__.py +7 -0
- pymax/dispatch/dispatcher.py +143 -40
- pymax/dispatch/enums.py +1 -0
- pymax/dispatch/mapping.py +23 -3
- pymax/dispatch/middleware.py +32 -0
- pymax/dispatch/resolvers.py +4 -0
- pymax/dispatch/router.py +86 -34
- pymax/filters/__init__.py +15 -0
- pymax/filters/base.py +218 -0
- pymax/filters/containers/__init__.py +2 -0
- pymax/filters/containers/chat.py +240 -0
- pymax/filters/containers/message.py +147 -0
- pymax/filters/filters.py +211 -0
- pymax/filters/magic.py +13 -0
- pymax/filters/presets.py +88 -0
- pymax/filters/refs.py +56 -0
- pymax/formatting/markdown.py +55 -1
- pymax/fsm/__init__.py +3 -0
- pymax/fsm/context.py +30 -0
- pymax/fsm/protocol.py +14 -0
- pymax/fsm/state.py +70 -0
- pymax/fsm/storage.py +151 -0
- pymax/infra/base.py +2 -0
- pymax/infra/bots.py +20 -0
- pymax/infra/chat.py +28 -0
- pymax/infra/message.py +195 -6
- pymax/infra/stories.py +205 -0
- pymax/integrations/__init__.py +0 -0
- pymax/integrations/dishka.py +109 -0
- pymax/protocol/enums.py +2 -0
- pymax/protocol/tcp/payload.py +0 -2
- pymax/types/domain/__init__.py +23 -1
- pymax/types/domain/attachments/__init__.py +1 -1
- pymax/types/domain/attachments/keyboards/__init__.py +1 -1
- pymax/types/domain/attachments/keyboards/enums.py +12 -0
- pymax/types/domain/attachments/keyboards/inline.py +74 -3
- pymax/types/domain/callback.py +19 -0
- pymax/types/domain/comment.py +24 -0
- pymax/types/domain/message.py +91 -22
- pymax/types/domain/story.py +126 -0
- pymax/types/events/reaction.py +28 -4
- pymax/versions/catalog.py +1 -1
- {maxapi_python-2.4.1.dist-info → maxapi_python-2.5.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,10 +1,78 @@
|
|
|
1
|
-
from typing import
|
|
1
|
+
from typing import Annotated, Literal, TypeAlias
|
|
2
2
|
|
|
3
3
|
from pydantic import Field
|
|
4
4
|
|
|
5
5
|
from pymax.types.domain.attachments import AttachmentType
|
|
6
6
|
from pymax.types.domain.base import CamelModel
|
|
7
7
|
|
|
8
|
+
from .enums import ButtonType
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class BaseButton(CamelModel):
|
|
12
|
+
text: str
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class CallbackButton(BaseButton):
|
|
16
|
+
type: Literal[ButtonType.CALLBACK] = ButtonType.CALLBACK
|
|
17
|
+
text: str
|
|
18
|
+
payload: str
|
|
19
|
+
intent: str
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class LinkButton(BaseButton):
|
|
23
|
+
type: Literal[ButtonType.LINK] = ButtonType.LINK
|
|
24
|
+
url: str
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class ChatButton(BaseButton):
|
|
28
|
+
type: Literal[ButtonType.CHAT] = ButtonType.CHAT
|
|
29
|
+
payload: str
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class GeoLocationButton(BaseButton):
|
|
33
|
+
type: Literal[ButtonType.REQUEST_GEO_LOCATION] = ButtonType.REQUEST_GEO_LOCATION
|
|
34
|
+
quick: bool | None = None
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class RequestContactButton(BaseButton):
|
|
38
|
+
type: Literal[ButtonType.REQUEST_CONTACT] = ButtonType.REQUEST_CONTACT
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class OpenAppButton(BaseButton):
|
|
42
|
+
type: Literal[ButtonType.OPEN_APP] = ButtonType.OPEN_APP
|
|
43
|
+
contact_id: int | None = None
|
|
44
|
+
payload: str | None = None
|
|
45
|
+
web_app: str | None = None
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class MessageButton(BaseButton):
|
|
49
|
+
type: Literal[ButtonType.MESSAGE] = ButtonType.MESSAGE
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class ClipboardButton(BaseButton):
|
|
53
|
+
type: Literal[ButtonType.CLIPBOARD] = ButtonType.CLIPBOARD
|
|
54
|
+
payload: str
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
Button: TypeAlias = Annotated[
|
|
58
|
+
CallbackButton
|
|
59
|
+
| ChatButton
|
|
60
|
+
| LinkButton
|
|
61
|
+
| RequestContactButton
|
|
62
|
+
| GeoLocationButton
|
|
63
|
+
| OpenAppButton
|
|
64
|
+
| MessageButton
|
|
65
|
+
| ClipboardButton,
|
|
66
|
+
Field(discriminator="type"),
|
|
67
|
+
]
|
|
68
|
+
ButtonRow: TypeAlias = list[Button]
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class InlineKeyboard(CamelModel):
|
|
72
|
+
"""Inline-клавиатура со списком строк кнопок в ``buttons``."""
|
|
73
|
+
|
|
74
|
+
buttons: list[ButtonRow]
|
|
75
|
+
|
|
8
76
|
|
|
9
77
|
class InlineKeyboardAttachment(CamelModel):
|
|
10
78
|
"""Вложение inline-клавиатуры.
|
|
@@ -12,8 +80,11 @@ class InlineKeyboardAttachment(CamelModel):
|
|
|
12
80
|
:ivar type: Тип вложения.
|
|
13
81
|
:vartype type: Literal[AttachmentType.INLINE_KEYBOARD]
|
|
14
82
|
:ivar keyboard: Данные inline-клавиатуры.
|
|
15
|
-
:vartype keyboard:
|
|
83
|
+
:vartype keyboard: InlineKeyboard
|
|
84
|
+
:ivar callback_id: ID клавиатуры для ``client.send_callback()``.
|
|
85
|
+
:vartype callback_id: str
|
|
16
86
|
"""
|
|
17
87
|
|
|
18
88
|
type: Literal[AttachmentType.INLINE_KEYBOARD] = Field(alias="_type")
|
|
19
|
-
keyboard:
|
|
89
|
+
keyboard: InlineKeyboard
|
|
90
|
+
callback_id: str
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from .base import CamelModel
|
|
2
|
+
from .chat import Chat
|
|
3
|
+
from .message import Message
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class CallbackResponse(CamelModel):
|
|
7
|
+
"""Результат нажатия inline-кнопки бота.
|
|
8
|
+
|
|
9
|
+
``success`` показывает результат операции. Необязательные ``message`` и
|
|
10
|
+
``chat`` содержат дополнительные данные ответа Max и поддерживают методы
|
|
11
|
+
действий, если ответ получен через клиент.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
success: bool
|
|
15
|
+
unread: int
|
|
16
|
+
mark: int
|
|
17
|
+
message: Message | None = None
|
|
18
|
+
chat: Chat | None = None
|
|
19
|
+
chat_access_token: str | None = None
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from .base import CamelModel
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class CommentsInfo(CamelModel):
|
|
5
|
+
"""Счетчики комментариев поста.
|
|
6
|
+
|
|
7
|
+
:ivar total_count: Общее количество комментариев.
|
|
8
|
+
:vartype total_count: int
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
total_count: int = 0
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class CommentsInfoUpdate(CamelModel):
|
|
15
|
+
"""Обновление счетчиков комментариев поста.
|
|
16
|
+
|
|
17
|
+
:ivar post_id: ID поста.
|
|
18
|
+
:vartype post_id: int
|
|
19
|
+
:ivar comments_info: Счетчики комментариев поста, если Max их прислал.
|
|
20
|
+
:vartype comments_info: CommentsInfo | None
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
post_id: int
|
|
24
|
+
comments_info: CommentsInfo | None
|
pymax/types/domain/message.py
CHANGED
|
@@ -50,6 +50,11 @@ SendAttachments: TypeAlias = Sequence[SendAttachment] | None
|
|
|
50
50
|
Link: TypeAlias = Annotated["ReplyLink | ForwardLink", Field(discriminator="type")]
|
|
51
51
|
|
|
52
52
|
|
|
53
|
+
class MessageLink(CamelModel):
|
|
54
|
+
message: Message
|
|
55
|
+
chat_id: int
|
|
56
|
+
|
|
57
|
+
|
|
53
58
|
class ReactionCounter(CamelModel):
|
|
54
59
|
"""Счетчик одной реакции на сообщение.
|
|
55
60
|
|
|
@@ -98,7 +103,7 @@ class DelayedAttributes(CamelModel):
|
|
|
98
103
|
notify_opponents: bool
|
|
99
104
|
|
|
100
105
|
|
|
101
|
-
class ReplyLink(
|
|
106
|
+
class ReplyLink(MessageLink):
|
|
102
107
|
"""Источник сообщения, отправленного как reply.
|
|
103
108
|
|
|
104
109
|
:ivar message: Сообщение, на которое ответили.
|
|
@@ -112,7 +117,7 @@ class ReplyLink(CamelModel):
|
|
|
112
117
|
chat_id: int
|
|
113
118
|
|
|
114
119
|
|
|
115
|
-
class ForwardLink(
|
|
120
|
+
class ForwardLink(MessageLink):
|
|
116
121
|
"""Источник пересланного сообщения.
|
|
117
122
|
|
|
118
123
|
Помимо исходного сообщения содержит доступные в payload-е имя, ссылку,
|
|
@@ -134,24 +139,6 @@ class ForwardLink(CamelModel):
|
|
|
134
139
|
chat_icon_url: str | None = None
|
|
135
140
|
|
|
136
141
|
|
|
137
|
-
"""
|
|
138
|
-
"link": {
|
|
139
|
-
"type": "FORWARD",
|
|
140
|
-
"message": {
|
|
141
|
-
"id": "116641336752745888",
|
|
142
|
-
"time": 1779805553478,
|
|
143
|
-
"type": "CHANNEL",
|
|
144
|
-
"text": "Добро пожаловать на наше шоу!",
|
|
145
|
-
"attaches": []
|
|
146
|
-
},
|
|
147
|
-
"chatId": -75183661482741,
|
|
148
|
-
"chatName": "Max Bounty Test",
|
|
149
|
-
"chatLink": "https://max.ru/join/htlT5aTtlpAgcno_hc93lLM44Dxe45HvOEBqx22lKms",
|
|
150
|
-
"chatAccessType": "PRIVATE"
|
|
151
|
-
}
|
|
152
|
-
"""
|
|
153
|
-
|
|
154
|
-
|
|
155
142
|
class Message(CamelModel):
|
|
156
143
|
"""Сообщение Max с методами для действий над ним.
|
|
157
144
|
|
|
@@ -181,6 +168,8 @@ class Message(CamelModel):
|
|
|
181
168
|
:vartype id: int
|
|
182
169
|
:ivar chat_id: ID чата, если он есть в данных сообщения.
|
|
183
170
|
:vartype chat_id: int | None
|
|
171
|
+
:ivar post_id: ID поста канала, если сообщение является комментарием.
|
|
172
|
+
:vartype post_id: int | None
|
|
184
173
|
:ivar sender: ID отправителя.
|
|
185
174
|
:vartype sender: int | None
|
|
186
175
|
:ivar text: Текст сообщения.
|
|
@@ -222,6 +211,7 @@ class Message(CamelModel):
|
|
|
222
211
|
|
|
223
212
|
id: int
|
|
224
213
|
chat_id: int | None = None
|
|
214
|
+
post_id: int | None = None
|
|
225
215
|
sender: int | None = None
|
|
226
216
|
text: str = ""
|
|
227
217
|
time: int
|
|
@@ -261,7 +251,7 @@ class Message(CamelModel):
|
|
|
261
251
|
notify: bool = True,
|
|
262
252
|
send_at: DateTimeUnion | None = None,
|
|
263
253
|
) -> Message:
|
|
264
|
-
"""Отправляет ответ
|
|
254
|
+
"""Отправляет ответ в тот же чат или ветку комментариев.
|
|
265
255
|
|
|
266
256
|
:param text: Текст сообщения.
|
|
267
257
|
:type text: str | None
|
|
@@ -276,9 +266,29 @@ class Message(CamelModel):
|
|
|
276
266
|
:rtype: Message
|
|
277
267
|
:raises RuntimeError: Если сообщение не привязано к сервису или не
|
|
278
268
|
содержит ``chat_id``.
|
|
269
|
+
:raises ValueError: Если для комментария задана отложенная отправка.
|
|
279
270
|
"""
|
|
280
271
|
actions, chat_id = self._bound()
|
|
281
272
|
|
|
273
|
+
if self.post_id is not None:
|
|
274
|
+
if send_at:
|
|
275
|
+
raise ValueError("Scheduled comments are not supported")
|
|
276
|
+
|
|
277
|
+
message = await actions.send_comment(
|
|
278
|
+
chat_id=chat_id,
|
|
279
|
+
post_id=self.post_id,
|
|
280
|
+
text=text,
|
|
281
|
+
reply_to=self.id,
|
|
282
|
+
attachments=attachments,
|
|
283
|
+
notify=notify,
|
|
284
|
+
)
|
|
285
|
+
if message.chat_id is None:
|
|
286
|
+
message.chat_id = chat_id
|
|
287
|
+
if message.post_id is None:
|
|
288
|
+
message.post_id = self.post_id
|
|
289
|
+
|
|
290
|
+
return message
|
|
291
|
+
|
|
282
292
|
return await actions.send_message(
|
|
283
293
|
chat_id=chat_id,
|
|
284
294
|
text=text,
|
|
@@ -297,7 +307,7 @@ class Message(CamelModel):
|
|
|
297
307
|
notify: bool = True,
|
|
298
308
|
send_at: DateTimeUnion | None = None,
|
|
299
309
|
) -> Message:
|
|
300
|
-
"""Отправляет сообщение в тот же
|
|
310
|
+
"""Отправляет сообщение в тот же чат или ветку комментариев.
|
|
301
311
|
|
|
302
312
|
:param text: Текст сообщения.
|
|
303
313
|
:type text: str | None
|
|
@@ -314,9 +324,29 @@ class Message(CamelModel):
|
|
|
314
324
|
:rtype: Message
|
|
315
325
|
:raises RuntimeError: Если сообщение не привязано к сервису или не
|
|
316
326
|
содержит ``chat_id``.
|
|
327
|
+
:raises ValueError: Если для комментария задана отложенная отправка.
|
|
317
328
|
"""
|
|
318
329
|
actions, chat_id = self._bound()
|
|
319
330
|
|
|
331
|
+
if self.post_id is not None:
|
|
332
|
+
if send_at:
|
|
333
|
+
raise ValueError("Scheduled comments are not supported")
|
|
334
|
+
|
|
335
|
+
message = await actions.send_comment(
|
|
336
|
+
chat_id=chat_id,
|
|
337
|
+
post_id=self.post_id,
|
|
338
|
+
text=text,
|
|
339
|
+
reply_to=reply_to,
|
|
340
|
+
attachments=attachments,
|
|
341
|
+
notify=notify,
|
|
342
|
+
)
|
|
343
|
+
if message.chat_id is None:
|
|
344
|
+
message.chat_id = chat_id
|
|
345
|
+
if message.post_id is None:
|
|
346
|
+
message.post_id = self.post_id
|
|
347
|
+
|
|
348
|
+
return message
|
|
349
|
+
|
|
320
350
|
return await actions.send_message(
|
|
321
351
|
chat_id=chat_id,
|
|
322
352
|
text=text,
|
|
@@ -388,6 +418,21 @@ class Message(CamelModel):
|
|
|
388
418
|
"""
|
|
389
419
|
actions, chat_id = self._bound()
|
|
390
420
|
|
|
421
|
+
if self.post_id is not None:
|
|
422
|
+
message = await actions.edit_comment(
|
|
423
|
+
chat_id=chat_id,
|
|
424
|
+
post_id=self.post_id,
|
|
425
|
+
message_id=self.id,
|
|
426
|
+
text=text,
|
|
427
|
+
attachments=attachments,
|
|
428
|
+
)
|
|
429
|
+
if message.chat_id is None:
|
|
430
|
+
message.chat_id = chat_id
|
|
431
|
+
if message.post_id is None:
|
|
432
|
+
message.post_id = self.post_id
|
|
433
|
+
|
|
434
|
+
return message
|
|
435
|
+
|
|
391
436
|
return await actions.edit_message(
|
|
392
437
|
chat_id=chat_id,
|
|
393
438
|
message_id=self.id,
|
|
@@ -407,6 +452,14 @@ class Message(CamelModel):
|
|
|
407
452
|
"""
|
|
408
453
|
actions, chat_id = self._bound()
|
|
409
454
|
|
|
455
|
+
if self.post_id is not None:
|
|
456
|
+
return await actions.delete_comment(
|
|
457
|
+
chat_id=chat_id,
|
|
458
|
+
post_id=self.post_id,
|
|
459
|
+
message_ids=[self.id],
|
|
460
|
+
for_me=for_me,
|
|
461
|
+
)
|
|
462
|
+
|
|
410
463
|
return await actions.delete_message(
|
|
411
464
|
chat_id=chat_id,
|
|
412
465
|
message_ids=[self.id],
|
|
@@ -440,6 +493,14 @@ class Message(CamelModel):
|
|
|
440
493
|
"""
|
|
441
494
|
actions, chat_id = self._bound()
|
|
442
495
|
|
|
496
|
+
if self.post_id is not None:
|
|
497
|
+
return await actions.add_comment_reaction(
|
|
498
|
+
chat_id=chat_id,
|
|
499
|
+
post_id=self.post_id,
|
|
500
|
+
message_id=self.id,
|
|
501
|
+
reaction=reaction,
|
|
502
|
+
)
|
|
503
|
+
|
|
443
504
|
return await actions.add_reaction(
|
|
444
505
|
chat_id=chat_id,
|
|
445
506
|
message_id=self.id,
|
|
@@ -456,6 +517,13 @@ class Message(CamelModel):
|
|
|
456
517
|
"""
|
|
457
518
|
actions, chat_id = self._bound()
|
|
458
519
|
|
|
520
|
+
if self.post_id is not None:
|
|
521
|
+
return await actions.remove_comment_reaction(
|
|
522
|
+
chat_id=chat_id,
|
|
523
|
+
post_id=self.post_id,
|
|
524
|
+
message_id=self.id,
|
|
525
|
+
)
|
|
526
|
+
|
|
459
527
|
return await actions.remove_reaction(
|
|
460
528
|
chat_id=chat_id,
|
|
461
529
|
message_id=self.id,
|
|
@@ -507,6 +575,7 @@ class Message(CamelModel):
|
|
|
507
575
|
return {
|
|
508
576
|
**message,
|
|
509
577
|
"chatId": value.get("chatId"),
|
|
578
|
+
"postId": value.get("postId"),
|
|
510
579
|
"prevMessageId": value.get("prevMessageId"),
|
|
511
580
|
"ttl": value.get("ttl"),
|
|
512
581
|
"unread": value.get("unread"),
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
from typing import Annotated, Literal, TypeAlias
|
|
2
|
+
|
|
3
|
+
from pydantic import Field
|
|
4
|
+
|
|
5
|
+
from .attachments.enums import AttachmentType
|
|
6
|
+
from .base import CamelModel
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class StoryCoordinates(CamelModel):
|
|
10
|
+
"""Положение слоя сторис в долях ширины и высоты кадра."""
|
|
11
|
+
|
|
12
|
+
x: float
|
|
13
|
+
y: float
|
|
14
|
+
w: float
|
|
15
|
+
h: float
|
|
16
|
+
rotation: float = 0
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class StoryClickableLink(CamelModel):
|
|
20
|
+
"""Ссылка слоя сторис и результат ее проверки сервером."""
|
|
21
|
+
|
|
22
|
+
url: str
|
|
23
|
+
check_result: int | None = None
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class StoryLayer(CamelModel):
|
|
27
|
+
"""Слой сторис со ссылкой и координатами."""
|
|
28
|
+
|
|
29
|
+
type: int = 0
|
|
30
|
+
coordinates: StoryCoordinates
|
|
31
|
+
clickable_link: StoryClickableLink
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class StoryPhotoMedia(CamelModel):
|
|
35
|
+
"""Фото сторис, полученное от Max, без обязательного токена загрузки."""
|
|
36
|
+
|
|
37
|
+
base_url: str
|
|
38
|
+
height: int
|
|
39
|
+
width: int
|
|
40
|
+
photo_id: int
|
|
41
|
+
preview_data: bytes | None = None
|
|
42
|
+
type: Literal[AttachmentType.PHOTO] = Field(alias="_type")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class StoryVideoMedia(CamelModel):
|
|
46
|
+
"""Видео сторис, полученное от Max.
|
|
47
|
+
|
|
48
|
+
В отличие от ``VideoAttachment``, не требует токен загрузки и
|
|
49
|
+
``video_type``, которые используются при публикации.
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
height: int
|
|
53
|
+
width: int
|
|
54
|
+
video_id: int
|
|
55
|
+
duration: int | None = None
|
|
56
|
+
preview_data: bytes | None = None
|
|
57
|
+
type: Literal[AttachmentType.VIDEO] = Field(alias="_type")
|
|
58
|
+
thumbnail: str
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
StoryMedia: TypeAlias = Annotated[
|
|
62
|
+
StoryPhotoMedia | StoryVideoMedia,
|
|
63
|
+
Field(discriminator="type"),
|
|
64
|
+
]
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class Story(CamelModel):
|
|
68
|
+
"""Сторис, возвращенная после публикации или при чтении.
|
|
69
|
+
|
|
70
|
+
:ivar id: ID сторис.
|
|
71
|
+
:ivar cid: Идентификатор публикации, сформированный клиентом.
|
|
72
|
+
:ivar media: Фото или видео сторис.
|
|
73
|
+
:ivar layers: Слои поверх кадра.
|
|
74
|
+
:ivar settings: Настройки сторис в формате Max.
|
|
75
|
+
:ivar expiration: Время жизни сторис в миллисекундах.
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
id: int
|
|
79
|
+
cid: int | None = None
|
|
80
|
+
media: StoryMedia
|
|
81
|
+
layers: list[StoryLayer] = Field(default_factory=list)
|
|
82
|
+
settings: int
|
|
83
|
+
expiration: int
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class StoryOwner(CamelModel):
|
|
87
|
+
"""Владелец сторис.
|
|
88
|
+
|
|
89
|
+
:ivar owner_id: ID владельца, а не отдельной сторис.
|
|
90
|
+
:ivar type: Числовой тип владельца из Max. ``0`` — пользователь.
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
owner_id: int
|
|
94
|
+
type: int = 0
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class StoryPreview(CamelModel):
|
|
98
|
+
"""Сводная информация о сторис владельца в ленте и обновлениях.
|
|
99
|
+
|
|
100
|
+
ID отдельных сторис в превью нет. Для их получения передайте
|
|
101
|
+
``owner.owner_id`` и ``owner.type`` в ``client.get_stories()``.
|
|
102
|
+
|
|
103
|
+
:ivar owner: Владелец сторис.
|
|
104
|
+
:ivar total_count: Общее количество сторис владельца.
|
|
105
|
+
:ivar read_count: Количество просмотренных сторис владельца.
|
|
106
|
+
"""
|
|
107
|
+
|
|
108
|
+
owner: StoryOwner
|
|
109
|
+
total_count: int = 0
|
|
110
|
+
read_count: int = 0
|
|
111
|
+
last_story_expiration_time: int = 0
|
|
112
|
+
settings: int = 0
|
|
113
|
+
update_time: int = 0
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class StoryDetailedStats(CamelModel):
|
|
117
|
+
"""Запись подробной статистики просмотра или реакции на сторис.
|
|
118
|
+
|
|
119
|
+
Типизировано только ``user_id``. Дополнительные поля доступны в
|
|
120
|
+
``model_extra`` с исходными именами ответа Max, без преобразования
|
|
121
|
+
camelCase. Поле ``reaction``, если оно пришло, остается словарем.
|
|
122
|
+
|
|
123
|
+
:ivar user_id: ID пользователя.
|
|
124
|
+
"""
|
|
125
|
+
|
|
126
|
+
user_id: int
|
pymax/types/events/reaction.py
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from pydantic import model_validator
|
|
4
|
+
|
|
1
5
|
from pymax.types.domain.base import CamelModel
|
|
2
6
|
from pymax.types.domain.message import ReactionCounter
|
|
3
7
|
|
|
@@ -6,16 +10,36 @@ class ReactionUpdateEvent(CamelModel):
|
|
|
6
10
|
"""Событие обновления реакций сообщения.
|
|
7
11
|
|
|
8
12
|
:ivar message_id: ID сообщения.
|
|
9
|
-
:vartype message_id:
|
|
13
|
+
:vartype message_id: int
|
|
10
14
|
:ivar chat_id: ID чата.
|
|
11
15
|
:vartype chat_id: int
|
|
12
|
-
:ivar counters: Счетчики реакций по
|
|
13
|
-
:vartype counters: list[ReactionCounter]
|
|
16
|
+
:ivar counters: Счетчики реакций по типам, если Max их прислал.
|
|
17
|
+
:vartype counters: list[ReactionCounter] | None
|
|
14
18
|
:ivar total_count: Общее количество реакций.
|
|
15
19
|
:vartype total_count: int
|
|
20
|
+
:ivar your_reaction: Реакция текущего пользователя, если она есть.
|
|
21
|
+
:vartype your_reaction: str | None
|
|
16
22
|
"""
|
|
17
23
|
|
|
18
|
-
message_id:
|
|
24
|
+
message_id: int
|
|
19
25
|
chat_id: int
|
|
20
26
|
counters: list[ReactionCounter] | None = None
|
|
21
27
|
total_count: int = 0
|
|
28
|
+
your_reaction: str | None = None
|
|
29
|
+
|
|
30
|
+
@model_validator(mode="before")
|
|
31
|
+
@classmethod
|
|
32
|
+
def _unwrap_reaction_info(cls, value: Any) -> Any:
|
|
33
|
+
if not isinstance(value, dict):
|
|
34
|
+
return value
|
|
35
|
+
|
|
36
|
+
reaction_info = value.get("reactionInfo")
|
|
37
|
+
if not isinstance(reaction_info, dict):
|
|
38
|
+
return value
|
|
39
|
+
|
|
40
|
+
data = value.copy()
|
|
41
|
+
for field in ("counters", "totalCount", "yourReaction"):
|
|
42
|
+
if field in reaction_info:
|
|
43
|
+
data[field] = reaction_info[field]
|
|
44
|
+
|
|
45
|
+
return data
|
pymax/versions/catalog.py
CHANGED
|
@@ -21,7 +21,7 @@ class VersionCatalog:
|
|
|
21
21
|
remote: Загружать ли удаленные версии при :meth:`load`.
|
|
22
22
|
"""
|
|
23
23
|
|
|
24
|
-
RECOMMENDED_APP_VERSION: str = "26.
|
|
24
|
+
RECOMMENDED_APP_VERSION: str = "26.37.1"
|
|
25
25
|
DEFAULT_REMOTE_URL: str = "https://hashes.pymax.org/versions.json"
|
|
26
26
|
|
|
27
27
|
def __init__(self, remote: bool = False) -> None:
|
|
File without changes
|