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
pymax/api/bots/payloads.py
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
from pymax.api.models import CamelModel
|
|
2
|
+
from pymax.types import ButtonType
|
|
2
3
|
|
|
3
4
|
|
|
4
5
|
class RequestInitDataPayload(CamelModel):
|
|
5
6
|
bot_id: int
|
|
6
7
|
chat_id: int | None = None
|
|
7
8
|
start_param: str | None = None
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class SendCallbackPayload(CamelModel):
|
|
12
|
+
callback_id: str
|
|
13
|
+
type: ButtonType
|
|
14
|
+
payload: str | None = None
|
|
15
|
+
timestamp: int
|
pymax/api/bots/service.py
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import time
|
|
3
4
|
from typing import TYPE_CHECKING
|
|
4
5
|
|
|
6
|
+
from pymax.api.binding import bind_api_model
|
|
5
7
|
from pymax.api.response import require_payload_model
|
|
6
8
|
from pymax.logging import get_logger
|
|
7
9
|
from pymax.protocol import Opcode
|
|
10
|
+
from pymax.types import ButtonType, CallbackResponse
|
|
8
11
|
from pymax.types.domain import InitData
|
|
9
12
|
|
|
10
|
-
from .payloads import RequestInitDataPayload
|
|
13
|
+
from .payloads import RequestInitDataPayload, SendCallbackPayload
|
|
11
14
|
|
|
12
15
|
if TYPE_CHECKING:
|
|
13
16
|
from pymax.app import App
|
|
@@ -29,3 +32,12 @@ class BotsService:
|
|
|
29
32
|
frame = RequestInitDataPayload(bot_id=bot_id, chat_id=chat_id, start_param=start_param)
|
|
30
33
|
response = await self.app.invoke(Opcode.WEB_APP_INIT_DATA, frame.to_payload())
|
|
31
34
|
return require_payload_model(response, InitData)
|
|
35
|
+
|
|
36
|
+
async def send_callback(
|
|
37
|
+
self, callback_id: str, type: ButtonType, payload: str | None = None
|
|
38
|
+
) -> CallbackResponse:
|
|
39
|
+
frame = SendCallbackPayload(
|
|
40
|
+
callback_id=callback_id, type=type, payload=payload, timestamp=int(time.time() * 1000)
|
|
41
|
+
)
|
|
42
|
+
response = await self.app.invoke(Opcode.MSG_SEND_CALLBACK, frame.to_payload())
|
|
43
|
+
return bind_api_model(self.app, require_payload_model(response, CallbackResponse))
|
pymax/api/chats/enums.py
CHANGED
|
@@ -11,6 +11,7 @@ class ChatMemberOperation(str, Enum):
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class ChatOption(str, Enum):
|
|
14
|
+
COMMENTS = "COMMENTS"
|
|
14
15
|
ONLY_OWNER_CAN_CHANGE_ICON_TITLE = "ONLY_OWNER_CAN_CHANGE_ICON_TITLE"
|
|
15
16
|
ALL_CAN_PIN_MESSAGE = "ALL_CAN_PIN_MESSAGE"
|
|
16
17
|
ONLY_ADMIN_CAN_ADD_MEMBER = "ONLY_ADMIN_CAN_ADD_MEMBER"
|
pymax/api/chats/payloads.py
CHANGED
|
@@ -137,3 +137,22 @@ class AddAdminPayload(CamelModel):
|
|
|
137
137
|
type: PermType = PermType.ADMIN
|
|
138
138
|
operation: str = "add"
|
|
139
139
|
permissions: int
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class ChannelCommentsOptions(CamelModel):
|
|
143
|
+
comments: bool = Field(serialization_alias=ChatOption.COMMENTS.value)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
class SetChannelCommentsPayload(CamelModel):
|
|
147
|
+
chat_id: int
|
|
148
|
+
options: ChannelCommentsOptions
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
class BlockCommentAuthorPayload(CamelModel):
|
|
152
|
+
chat_id: int
|
|
153
|
+
post_id: int
|
|
154
|
+
user_ids: list[int]
|
|
155
|
+
message_id: int
|
|
156
|
+
type: Literal["COMMENTS_BLACKLIST"] = "COMMENTS_BLACKLIST"
|
|
157
|
+
operation: ChatMemberOperation = ChatMemberOperation.ADD
|
|
158
|
+
clean_msg_period: int = 0
|
pymax/api/chats/service.py
CHANGED
|
@@ -22,9 +22,11 @@ from pymax.types.domain import Chat, Member, Message
|
|
|
22
22
|
from .enums import ChannelPermissions, ChatLinkPrefix, ChatMemberOperation, ChatPayloadKey
|
|
23
23
|
from .payloads import (
|
|
24
24
|
AddAdminPayload,
|
|
25
|
+
BlockCommentAuthorPayload,
|
|
25
26
|
ChangeGroupProfilePayload,
|
|
26
27
|
ChangeGroupSettingsOptions,
|
|
27
28
|
ChangeGroupSettingsPayload,
|
|
29
|
+
ChannelCommentsOptions,
|
|
28
30
|
CreateGroupAttach,
|
|
29
31
|
CreateGroupMessage,
|
|
30
32
|
CreateGroupPayload,
|
|
@@ -40,6 +42,7 @@ from .payloads import (
|
|
|
40
42
|
LinkInfoPayload,
|
|
41
43
|
RemoveUsersPayload,
|
|
42
44
|
ReworkInviteLinkPayload,
|
|
45
|
+
SetChannelCommentsPayload,
|
|
43
46
|
)
|
|
44
47
|
|
|
45
48
|
if TYPE_CHECKING:
|
|
@@ -422,3 +425,34 @@ class ChatService:
|
|
|
422
425
|
)
|
|
423
426
|
|
|
424
427
|
await self.app.invoke(Opcode.CHAT_MEMBERS_UPDATE, frame.to_payload())
|
|
428
|
+
|
|
429
|
+
async def set_channel_comments(self, chat_id: int, enabled: bool) -> None:
|
|
430
|
+
frame = SetChannelCommentsPayload(
|
|
431
|
+
chat_id=chat_id,
|
|
432
|
+
options=ChannelCommentsOptions(comments=enabled),
|
|
433
|
+
)
|
|
434
|
+
response = await self.app.invoke(Opcode.CHAT_UPDATE, frame.to_payload())
|
|
435
|
+
chat = parse_payload_item_model(response, ChatPayloadKey.CHAT, Chat)
|
|
436
|
+
if chat:
|
|
437
|
+
self._cache_chat(chat)
|
|
438
|
+
|
|
439
|
+
async def block_comment_author(
|
|
440
|
+
self,
|
|
441
|
+
chat_id: int,
|
|
442
|
+
post_id: int,
|
|
443
|
+
user_ids: list[int],
|
|
444
|
+
message_id: int,
|
|
445
|
+
clean_msg_period: int = 0,
|
|
446
|
+
) -> bool:
|
|
447
|
+
frame = BlockCommentAuthorPayload(
|
|
448
|
+
chat_id=chat_id,
|
|
449
|
+
post_id=post_id,
|
|
450
|
+
user_ids=user_ids,
|
|
451
|
+
message_id=message_id,
|
|
452
|
+
clean_msg_period=clean_msg_period,
|
|
453
|
+
)
|
|
454
|
+
response = await self.app.invoke(Opcode.CHAT_MEMBERS_UPDATE, frame.to_payload())
|
|
455
|
+
chat = parse_payload_item_model(response, ChatPayloadKey.CHAT, Chat)
|
|
456
|
+
if chat:
|
|
457
|
+
self._cache_chat(chat)
|
|
458
|
+
return True
|
pymax/api/facade.py
CHANGED
|
@@ -10,6 +10,7 @@ from .chats import ChatService
|
|
|
10
10
|
from .messages import MessageService
|
|
11
11
|
from .self import SelfService
|
|
12
12
|
from .session import SessionService
|
|
13
|
+
from .stories import StoriesService
|
|
13
14
|
from .uploads import UploadService
|
|
14
15
|
from .users import UserService
|
|
15
16
|
|
|
@@ -31,4 +32,5 @@ class ApiFacade:
|
|
|
31
32
|
self.session = SessionService(app)
|
|
32
33
|
self.auth = AuthService(app)
|
|
33
34
|
self.uploads = UploadService(app)
|
|
35
|
+
self.stories = StoriesService(app)
|
|
34
36
|
logger.debug("api facade initialized")
|
pymax/api/messages/enums.py
CHANGED
pymax/api/messages/payloads.py
CHANGED
|
@@ -18,6 +18,10 @@ class GetMessagesPayload(CamelModel):
|
|
|
18
18
|
message_ids: list[int]
|
|
19
19
|
|
|
20
20
|
|
|
21
|
+
class GetCommentsPayload(GetMessagesPayload):
|
|
22
|
+
post_id: int
|
|
23
|
+
|
|
24
|
+
|
|
21
25
|
class EditMessagePayload(CamelModel):
|
|
22
26
|
chat_id: int
|
|
23
27
|
message_id: int
|
|
@@ -28,6 +32,10 @@ class EditMessagePayload(CamelModel):
|
|
|
28
32
|
)
|
|
29
33
|
|
|
30
34
|
|
|
35
|
+
class EditCommentPayload(EditMessagePayload):
|
|
36
|
+
post_id: int
|
|
37
|
+
|
|
38
|
+
|
|
31
39
|
class ReplyLink(CamelModel):
|
|
32
40
|
type: str = "REPLY" # TODO: enum?
|
|
33
41
|
message_id: int
|
|
@@ -53,6 +61,10 @@ class SendMessagePayload(CamelModel):
|
|
|
53
61
|
notify: bool = False
|
|
54
62
|
|
|
55
63
|
|
|
64
|
+
class SendCommentPayload(SendMessagePayload):
|
|
65
|
+
post_id: int
|
|
66
|
+
|
|
67
|
+
|
|
56
68
|
class ForwardLink(CamelModel):
|
|
57
69
|
type: Literal["FORWARD"] = "FORWARD"
|
|
58
70
|
message_id: str
|
|
@@ -86,6 +98,10 @@ class ChatHistoryPayload(CamelModel):
|
|
|
86
98
|
interactive: bool = False
|
|
87
99
|
|
|
88
100
|
|
|
101
|
+
class CommentsHistoryPayload(ChatHistoryPayload):
|
|
102
|
+
post_id: int
|
|
103
|
+
|
|
104
|
+
|
|
89
105
|
class DeleteMessagePayload(CamelModel):
|
|
90
106
|
chat_id: int
|
|
91
107
|
message_ids: list[int]
|
|
@@ -134,7 +150,7 @@ class RemoveReactionPayload(CamelModel):
|
|
|
134
150
|
class ReadMessagesPayload(CamelModel):
|
|
135
151
|
type: ReadAction
|
|
136
152
|
chat_id: int
|
|
137
|
-
message_id:
|
|
153
|
+
message_id: int
|
|
138
154
|
mark: int
|
|
139
155
|
|
|
140
156
|
|
|
@@ -143,3 +159,33 @@ class VotePollPayload(CamelModel):
|
|
|
143
159
|
message_id: int
|
|
144
160
|
poll_id: int
|
|
145
161
|
answers_ids: list[int]
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
class DeleteCommentPayload(DeleteMessagePayload):
|
|
165
|
+
post_id: int
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
class AddCommentReactionPayload(AddReactionPayload):
|
|
169
|
+
post_id: int
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
class RemoveCommentReactionPayload(RemoveReactionPayload):
|
|
173
|
+
post_id: int
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
class SubscribeCommentsPayload(CamelModel):
|
|
177
|
+
chat_id: int
|
|
178
|
+
post_id: int
|
|
179
|
+
subscribe: bool = True
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
class GetCommentsInfoPayload(CamelModel):
|
|
183
|
+
chat_id: int
|
|
184
|
+
post_ids: list[int]
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
class DeleteUserCommentsPayload(CamelModel):
|
|
188
|
+
chat_id: int
|
|
189
|
+
post_id: int
|
|
190
|
+
user_id: int
|
|
191
|
+
message_id: int
|
pymax/api/messages/service.py
CHANGED
|
@@ -27,6 +27,7 @@ from pymax.formatting.markdown import Formatter
|
|
|
27
27
|
from pymax.logging import get_logger
|
|
28
28
|
from pymax.protocol import Opcode
|
|
29
29
|
from pymax.types.domain import (
|
|
30
|
+
CommentsInfoUpdate,
|
|
30
31
|
FileRequest,
|
|
31
32
|
Message,
|
|
32
33
|
Poll,
|
|
@@ -38,14 +39,21 @@ from pymax.types.domain import (
|
|
|
38
39
|
|
|
39
40
|
from .enums import ItemType, MessagePayloadKey, ReadAction
|
|
40
41
|
from .payloads import (
|
|
42
|
+
AddCommentReactionPayload,
|
|
41
43
|
AddReactionPayload,
|
|
42
44
|
ChatHistoryPayload,
|
|
45
|
+
CommentsHistoryPayload,
|
|
43
46
|
DelayedAttributes,
|
|
47
|
+
DeleteCommentPayload,
|
|
44
48
|
DeleteMessagePayload,
|
|
49
|
+
DeleteUserCommentsPayload,
|
|
50
|
+
EditCommentPayload,
|
|
45
51
|
EditMessagePayload,
|
|
46
52
|
ForwardLink,
|
|
47
53
|
ForwardMessagePayload,
|
|
48
54
|
ForwardMessagePayloadMessage,
|
|
55
|
+
GetCommentsInfoPayload,
|
|
56
|
+
GetCommentsPayload,
|
|
49
57
|
GetFilePayload,
|
|
50
58
|
GetMessagesPayload,
|
|
51
59
|
GetReactionsPayload,
|
|
@@ -53,10 +61,13 @@ from .payloads import (
|
|
|
53
61
|
PinMessagePayload,
|
|
54
62
|
ReactionInfoPayload,
|
|
55
63
|
ReadMessagesPayload,
|
|
64
|
+
RemoveCommentReactionPayload,
|
|
56
65
|
RemoveReactionPayload,
|
|
57
66
|
ReplyLink,
|
|
67
|
+
SendCommentPayload,
|
|
58
68
|
SendMessagePayload,
|
|
59
69
|
SendMessagePayloadMessage,
|
|
70
|
+
SubscribeCommentsPayload,
|
|
60
71
|
VotePollPayload,
|
|
61
72
|
)
|
|
62
73
|
|
|
@@ -85,6 +96,40 @@ class MessageService:
|
|
|
85
96
|
logger.debug("generated message cid=%s", e)
|
|
86
97
|
return e
|
|
87
98
|
|
|
99
|
+
async def _prepare_message(
|
|
100
|
+
self,
|
|
101
|
+
text: str | None,
|
|
102
|
+
reply_to: int | None,
|
|
103
|
+
attachments: SendAttachments,
|
|
104
|
+
*,
|
|
105
|
+
delayed_attributes: DelayedAttributes | None = None,
|
|
106
|
+
) -> tuple[
|
|
107
|
+
SendMessagePayloadMessage,
|
|
108
|
+
list[
|
|
109
|
+
AttachPhotoPayload | VideoAttachPayload | AttachFilePayload | VoiceAttachPayload | Poll
|
|
110
|
+
],
|
|
111
|
+
]:
|
|
112
|
+
if not text and not attachments:
|
|
113
|
+
raise ValueError("Either text or attachments must be provided")
|
|
114
|
+
|
|
115
|
+
if text:
|
|
116
|
+
clean_text, elements = Formatter.format_markdown(text)
|
|
117
|
+
else:
|
|
118
|
+
clean_text, elements = None, []
|
|
119
|
+
|
|
120
|
+
attaches = await self._upload_attachments(attachments)
|
|
121
|
+
|
|
122
|
+
message = SendMessagePayloadMessage(
|
|
123
|
+
text=clean_text,
|
|
124
|
+
cid=self._next_cid(),
|
|
125
|
+
elements=elements,
|
|
126
|
+
attaches=attaches,
|
|
127
|
+
link=ReplyLink(message_id=reply_to) if reply_to else None,
|
|
128
|
+
delayed_attributes=delayed_attributes,
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
return message, attaches
|
|
132
|
+
|
|
88
133
|
async def _upload_attachments(
|
|
89
134
|
self, attachments: SendAttachments
|
|
90
135
|
) -> list[
|
|
@@ -202,32 +247,21 @@ class MessageService:
|
|
|
202
247
|
) -> Message:
|
|
203
248
|
logger.info("sending message chat_id=%s text_len=%s", chat_id, len(text) if text else 0)
|
|
204
249
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
250
|
+
message, attaches = await self._prepare_message(
|
|
251
|
+
text,
|
|
252
|
+
reply_to,
|
|
253
|
+
attachments,
|
|
254
|
+
delayed_attributes=DelayedAttributes(
|
|
255
|
+
time_to_fire=self._convert_time(send_at),
|
|
256
|
+
notify_sender=notify,
|
|
257
|
+
)
|
|
258
|
+
if send_at
|
|
259
|
+
else None,
|
|
260
|
+
)
|
|
215
261
|
|
|
216
262
|
frame = SendMessagePayload(
|
|
217
263
|
chat_id=chat_id,
|
|
218
|
-
message=
|
|
219
|
-
text=clean_text,
|
|
220
|
-
cid=self._next_cid(),
|
|
221
|
-
elements=elements,
|
|
222
|
-
attaches=attaches,
|
|
223
|
-
link=ReplyLink(message_id=reply_to) if reply_to else None,
|
|
224
|
-
delayed_attributes=DelayedAttributes(
|
|
225
|
-
time_to_fire=self._convert_time(send_at),
|
|
226
|
-
notify_sender=notify,
|
|
227
|
-
)
|
|
228
|
-
if send_at
|
|
229
|
-
else None,
|
|
230
|
-
),
|
|
264
|
+
message=message,
|
|
231
265
|
notify=notify,
|
|
232
266
|
)
|
|
233
267
|
|
|
@@ -390,6 +424,33 @@ class MessageService:
|
|
|
390
424
|
)
|
|
391
425
|
return messages
|
|
392
426
|
|
|
427
|
+
async def fetch_comments(
|
|
428
|
+
self,
|
|
429
|
+
chat_id: int,
|
|
430
|
+
post_id: int,
|
|
431
|
+
from_: int | None = None,
|
|
432
|
+
backward: int = 30,
|
|
433
|
+
get_messages: bool = True,
|
|
434
|
+
forward: int = 0,
|
|
435
|
+
) -> list[Message]:
|
|
436
|
+
frame = CommentsHistoryPayload(
|
|
437
|
+
chat_id=chat_id,
|
|
438
|
+
forward=forward,
|
|
439
|
+
backward=backward,
|
|
440
|
+
post_id=post_id,
|
|
441
|
+
from_=-1 if from_ is None else from_,
|
|
442
|
+
get_messages=get_messages,
|
|
443
|
+
)
|
|
444
|
+
response = await self.app.invoke(
|
|
445
|
+
Opcode.CHAT_HISTORY,
|
|
446
|
+
payload=frame.to_payload(),
|
|
447
|
+
)
|
|
448
|
+
messages = bind_api_models(
|
|
449
|
+
self.app,
|
|
450
|
+
parse_payload_list(response, MessagePayloadKey.MESSAGES, Message),
|
|
451
|
+
)
|
|
452
|
+
return messages
|
|
453
|
+
|
|
393
454
|
async def delete_message(
|
|
394
455
|
self,
|
|
395
456
|
chat_id: int,
|
|
@@ -551,7 +612,7 @@ class MessageService:
|
|
|
551
612
|
|
|
552
613
|
return None
|
|
553
614
|
|
|
554
|
-
async def read_message(self, message_id: int
|
|
615
|
+
async def read_message(self, message_id: int, chat_id: int) -> ReadState:
|
|
555
616
|
logger.info(
|
|
556
617
|
"marking message as read chat_id=%s message_id=%s",
|
|
557
618
|
chat_id,
|
|
@@ -580,3 +641,226 @@ class MessageService:
|
|
|
580
641
|
response = await self.app.invoke(Opcode.SEND_VOTE, frame.to_payload())
|
|
581
642
|
|
|
582
643
|
return require_payload_item_model(response, "state", PollState)
|
|
644
|
+
|
|
645
|
+
async def send_comment(
|
|
646
|
+
self,
|
|
647
|
+
chat_id: int,
|
|
648
|
+
post_id: int,
|
|
649
|
+
text: str | None = None,
|
|
650
|
+
reply_to: int | None = None,
|
|
651
|
+
attachments: SendAttachments = None,
|
|
652
|
+
*,
|
|
653
|
+
notify: bool = True,
|
|
654
|
+
) -> Message:
|
|
655
|
+
message, attaches = await self._prepare_message(text, reply_to, attachments)
|
|
656
|
+
|
|
657
|
+
frame = SendCommentPayload(
|
|
658
|
+
chat_id=chat_id,
|
|
659
|
+
post_id=post_id,
|
|
660
|
+
notify=notify,
|
|
661
|
+
message=message,
|
|
662
|
+
)
|
|
663
|
+
|
|
664
|
+
try:
|
|
665
|
+
response = await self.app.invoke(Opcode.MSG_SEND, frame.to_payload())
|
|
666
|
+
except ApiError as e:
|
|
667
|
+
if e.error == "attachment.not.ready":
|
|
668
|
+
await self._process_attachment_error(attaches)
|
|
669
|
+
response = await self.app.invoke(Opcode.MSG_SEND, frame.to_payload())
|
|
670
|
+
else:
|
|
671
|
+
raise
|
|
672
|
+
|
|
673
|
+
return bind_api_model(
|
|
674
|
+
self.app,
|
|
675
|
+
require_payload_model(response, Message),
|
|
676
|
+
)
|
|
677
|
+
|
|
678
|
+
async def get_comments(
|
|
679
|
+
self,
|
|
680
|
+
chat_id: int,
|
|
681
|
+
post_id: int,
|
|
682
|
+
message_ids: list[int],
|
|
683
|
+
) -> list[Message]:
|
|
684
|
+
frame = GetCommentsPayload(
|
|
685
|
+
chat_id=chat_id,
|
|
686
|
+
post_id=post_id,
|
|
687
|
+
message_ids=message_ids,
|
|
688
|
+
)
|
|
689
|
+
|
|
690
|
+
response = await self.app.invoke(Opcode.MSG_GET, frame.to_payload())
|
|
691
|
+
messages = parse_payload_list(response, MessagePayloadKey.MESSAGES, Message)
|
|
692
|
+
for message in messages:
|
|
693
|
+
if message.chat_id is None:
|
|
694
|
+
message.chat_id = chat_id
|
|
695
|
+
|
|
696
|
+
return bind_api_models(self.app, messages)
|
|
697
|
+
|
|
698
|
+
async def get_comment(
|
|
699
|
+
self,
|
|
700
|
+
chat_id: int,
|
|
701
|
+
post_id: int,
|
|
702
|
+
message_id: int,
|
|
703
|
+
) -> Message | None:
|
|
704
|
+
messages = await self.get_comments(chat_id, post_id, [message_id])
|
|
705
|
+
return messages[0] if messages else None
|
|
706
|
+
|
|
707
|
+
async def edit_comment(
|
|
708
|
+
self,
|
|
709
|
+
chat_id: int,
|
|
710
|
+
post_id: int,
|
|
711
|
+
message_id: int,
|
|
712
|
+
text: str | None = None,
|
|
713
|
+
attachments: SendAttachments = None,
|
|
714
|
+
) -> Message:
|
|
715
|
+
if not text and not attachments:
|
|
716
|
+
logger.error("edit_comment failed: no text or attachments provided")
|
|
717
|
+
raise ValueError("Either text or attachments must be provided")
|
|
718
|
+
|
|
719
|
+
if text:
|
|
720
|
+
clean_text, elements = Formatter.format_markdown(text)
|
|
721
|
+
else:
|
|
722
|
+
clean_text, elements = None, []
|
|
723
|
+
|
|
724
|
+
attaches = await self._upload_attachments(attachments)
|
|
725
|
+
|
|
726
|
+
frame = EditCommentPayload(
|
|
727
|
+
chat_id=chat_id,
|
|
728
|
+
post_id=post_id,
|
|
729
|
+
message_id=message_id,
|
|
730
|
+
text=clean_text,
|
|
731
|
+
elements=elements,
|
|
732
|
+
attachments=attaches,
|
|
733
|
+
)
|
|
734
|
+
try:
|
|
735
|
+
response = await self.app.invoke(Opcode.MSG_EDIT, frame.to_payload())
|
|
736
|
+
except ApiError as e:
|
|
737
|
+
if e.error == "attachment.not.ready":
|
|
738
|
+
await self._process_attachment_error(attaches)
|
|
739
|
+
|
|
740
|
+
response = await self.app.invoke(Opcode.MSG_EDIT, frame.to_payload())
|
|
741
|
+
else:
|
|
742
|
+
raise
|
|
743
|
+
|
|
744
|
+
message = require_payload_item_model(
|
|
745
|
+
response,
|
|
746
|
+
MessagePayloadKey.MESSAGE,
|
|
747
|
+
Message,
|
|
748
|
+
)
|
|
749
|
+
if message.chat_id is None:
|
|
750
|
+
message.chat_id = chat_id
|
|
751
|
+
|
|
752
|
+
return bind_api_model(self.app, message)
|
|
753
|
+
|
|
754
|
+
async def delete_comment(
|
|
755
|
+
self,
|
|
756
|
+
chat_id: int,
|
|
757
|
+
post_id: int,
|
|
758
|
+
message_ids: list[int],
|
|
759
|
+
for_me: bool = False,
|
|
760
|
+
) -> bool:
|
|
761
|
+
logger.info(
|
|
762
|
+
"deleting messages chat_id=%s ids=%s for_me=%s",
|
|
763
|
+
chat_id,
|
|
764
|
+
message_ids,
|
|
765
|
+
for_me,
|
|
766
|
+
)
|
|
767
|
+
frame = DeleteCommentPayload(
|
|
768
|
+
chat_id=chat_id,
|
|
769
|
+
post_id=post_id,
|
|
770
|
+
message_ids=message_ids,
|
|
771
|
+
for_me=for_me,
|
|
772
|
+
)
|
|
773
|
+
|
|
774
|
+
await self.app.invoke(Opcode.MSG_DELETE, frame.to_payload())
|
|
775
|
+
logger.info("messages deleted chat_id=%s count=%s", chat_id, len(message_ids))
|
|
776
|
+
return True
|
|
777
|
+
|
|
778
|
+
async def add_comment_reaction(
|
|
779
|
+
self,
|
|
780
|
+
chat_id: int,
|
|
781
|
+
post_id: int,
|
|
782
|
+
message_id: int,
|
|
783
|
+
reaction: str,
|
|
784
|
+
) -> ReactionInfo | None:
|
|
785
|
+
logger.info(
|
|
786
|
+
"adding reaction chat_id=%s message_id=%s reaction=%s",
|
|
787
|
+
chat_id,
|
|
788
|
+
message_id,
|
|
789
|
+
reaction,
|
|
790
|
+
)
|
|
791
|
+
frame = AddCommentReactionPayload(
|
|
792
|
+
chat_id=chat_id,
|
|
793
|
+
post_id=post_id,
|
|
794
|
+
message_id=message_id,
|
|
795
|
+
reaction=ReactionInfoPayload(id=reaction),
|
|
796
|
+
)
|
|
797
|
+
|
|
798
|
+
response = await self.app.invoke(Opcode.MSG_REACTION, frame.to_payload())
|
|
799
|
+
reaction_info = payload_item(response, MessagePayloadKey.REACTION_INFO)
|
|
800
|
+
if reaction_info:
|
|
801
|
+
return ReactionInfo.model_validate(reaction_info)
|
|
802
|
+
|
|
803
|
+
return None
|
|
804
|
+
|
|
805
|
+
async def remove_comment_reaction(
|
|
806
|
+
self,
|
|
807
|
+
chat_id: int,
|
|
808
|
+
post_id: int,
|
|
809
|
+
message_id: int,
|
|
810
|
+
) -> ReactionInfo | None:
|
|
811
|
+
logger.info(
|
|
812
|
+
"removing reaction chat_id=%s message_id=%s",
|
|
813
|
+
chat_id,
|
|
814
|
+
message_id,
|
|
815
|
+
)
|
|
816
|
+
frame = RemoveCommentReactionPayload(
|
|
817
|
+
chat_id=chat_id, post_id=post_id, message_id=message_id
|
|
818
|
+
)
|
|
819
|
+
|
|
820
|
+
response = await self.app.invoke(
|
|
821
|
+
Opcode.MSG_CANCEL_REACTION,
|
|
822
|
+
frame.to_payload(),
|
|
823
|
+
)
|
|
824
|
+
reaction_info = payload_item(response, MessagePayloadKey.REACTION_INFO)
|
|
825
|
+
if reaction_info:
|
|
826
|
+
return ReactionInfo.model_validate(reaction_info)
|
|
827
|
+
|
|
828
|
+
return None
|
|
829
|
+
|
|
830
|
+
async def subscribe_comments(
|
|
831
|
+
self,
|
|
832
|
+
chat_id: int,
|
|
833
|
+
post_id: int,
|
|
834
|
+
subscribe: bool = True,
|
|
835
|
+
) -> None:
|
|
836
|
+
frame = SubscribeCommentsPayload(chat_id=chat_id, post_id=post_id, subscribe=subscribe)
|
|
837
|
+
await self.app.invoke(Opcode.CHAT_SUBSCRIBE, frame.to_payload())
|
|
838
|
+
|
|
839
|
+
async def get_comments_info(
|
|
840
|
+
self,
|
|
841
|
+
chat_id: int,
|
|
842
|
+
post_ids: list[int],
|
|
843
|
+
) -> list[CommentsInfoUpdate]:
|
|
844
|
+
frame = GetCommentsInfoPayload(chat_id=chat_id, post_ids=post_ids)
|
|
845
|
+
response = await self.app.invoke(Opcode.MSG_GET_COMMENTS_INFO, frame.to_payload())
|
|
846
|
+
return parse_payload_list(
|
|
847
|
+
response,
|
|
848
|
+
MessagePayloadKey.COMMENTS_INFO_UPDATES,
|
|
849
|
+
CommentsInfoUpdate,
|
|
850
|
+
)
|
|
851
|
+
|
|
852
|
+
async def delete_user_comments(
|
|
853
|
+
self,
|
|
854
|
+
chat_id: int,
|
|
855
|
+
post_id: int,
|
|
856
|
+
user_id: int,
|
|
857
|
+
message_id: int,
|
|
858
|
+
) -> bool:
|
|
859
|
+
frame = DeleteUserCommentsPayload(
|
|
860
|
+
chat_id=chat_id,
|
|
861
|
+
post_id=post_id,
|
|
862
|
+
user_id=user_id,
|
|
863
|
+
message_id=message_id,
|
|
864
|
+
)
|
|
865
|
+
await self.app.invoke(Opcode.MSG_DELETE_USER_COMMENTS, frame.to_payload())
|
|
866
|
+
return True
|
pymax/api/response.py
CHANGED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class StoryStatsFilter(int, Enum):
|
|
5
|
+
"""Фильтр подробной статистики: просмотры (``0``) или реакции (``1``)."""
|
|
6
|
+
|
|
7
|
+
VIEWS = 0
|
|
8
|
+
REACTIONS = 1
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class StoryPayloadKey(str, Enum):
|
|
12
|
+
STORIES = "stories"
|
|
13
|
+
PEER_STORIES = "peerStories"
|
|
14
|
+
STORY = "story"
|
|
15
|
+
STORIES_PREVIEWS = "storiesPreviews"
|
|
16
|
+
CURSOR = "cursor"
|
|
17
|
+
STORY_DETAILED_STATS = "storyDetailedStats"
|
|
18
|
+
MARKER = "marker"
|
|
19
|
+
SUCCESS = "success"
|