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
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import builtins
|
|
4
|
+
from typing import TYPE_CHECKING, Any, TypeVar
|
|
5
|
+
|
|
6
|
+
from pymax.filters.base import ClientT, Filter, PathResolver
|
|
7
|
+
from pymax.filters.refs import FieldRef, NumberFieldRef, TextFieldRef
|
|
8
|
+
|
|
9
|
+
from .message import MessageContainer
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
pass
|
|
13
|
+
|
|
14
|
+
EventT = TypeVar("EventT")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ChatContainer(Filter[EventT, ClientT], PathResolver):
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
expected_type: builtins.type[EventT],
|
|
21
|
+
path: list[str] | None = None,
|
|
22
|
+
) -> None:
|
|
23
|
+
self.expected_type: type[EventT] = expected_type
|
|
24
|
+
self.path = path or []
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def id(self) -> NumberFieldRef:
|
|
28
|
+
return NumberFieldRef(
|
|
29
|
+
path=[*self.path, "id"],
|
|
30
|
+
expected_type=self.expected_type,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def type(self) -> TextFieldRef:
|
|
35
|
+
return TextFieldRef(
|
|
36
|
+
path=[*self.path, "type"],
|
|
37
|
+
expected_type=self.expected_type,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def status(self) -> TextFieldRef:
|
|
42
|
+
return TextFieldRef(
|
|
43
|
+
path=[*self.path, "status"],
|
|
44
|
+
expected_type=self.expected_type,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
@property
|
|
48
|
+
def owner(self) -> NumberFieldRef:
|
|
49
|
+
return NumberFieldRef(
|
|
50
|
+
path=[*self.path, "owner"],
|
|
51
|
+
expected_type=self.expected_type,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def participants(self) -> FieldRef:
|
|
56
|
+
return FieldRef(
|
|
57
|
+
path=[*self.path, "participants"],
|
|
58
|
+
expected_type=self.expected_type,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def title(self) -> TextFieldRef:
|
|
63
|
+
return TextFieldRef(
|
|
64
|
+
path=[*self.path, "title"],
|
|
65
|
+
expected_type=self.expected_type,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
@property
|
|
69
|
+
def base_raw_icon_url(self) -> TextFieldRef:
|
|
70
|
+
return TextFieldRef(
|
|
71
|
+
path=[*self.path, "base_raw_icon_url"],
|
|
72
|
+
expected_type=self.expected_type,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def base_icon_url(self) -> TextFieldRef:
|
|
77
|
+
return TextFieldRef(
|
|
78
|
+
path=[*self.path, "base_icon_url"],
|
|
79
|
+
expected_type=self.expected_type,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
@property
|
|
83
|
+
def last_message(self) -> MessageContainer:
|
|
84
|
+
return MessageContainer(
|
|
85
|
+
path=[*self.path, "last_message"],
|
|
86
|
+
expected_type=self.expected_type,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
@property
|
|
90
|
+
def last_event_time(self) -> NumberFieldRef:
|
|
91
|
+
return NumberFieldRef(
|
|
92
|
+
path=[*self.path, "last_event_time"],
|
|
93
|
+
expected_type=self.expected_type,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def last_delayed_update_time(self) -> NumberFieldRef:
|
|
98
|
+
return NumberFieldRef(
|
|
99
|
+
path=[*self.path, "last_delayed_update_time"],
|
|
100
|
+
expected_type=self.expected_type,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
@property
|
|
104
|
+
def last_fire_delayed_error_time(self) -> NumberFieldRef:
|
|
105
|
+
return NumberFieldRef(
|
|
106
|
+
path=[*self.path, "last_fire_delayed_error_time"],
|
|
107
|
+
expected_type=self.expected_type,
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
@property
|
|
111
|
+
def created(self) -> NumberFieldRef:
|
|
112
|
+
return NumberFieldRef(
|
|
113
|
+
path=[*self.path, "created"],
|
|
114
|
+
expected_type=self.expected_type,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
@property
|
|
118
|
+
def new_messages(self) -> NumberFieldRef:
|
|
119
|
+
return NumberFieldRef(
|
|
120
|
+
path=[*self.path, "new_messages"],
|
|
121
|
+
expected_type=self.expected_type,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
@property
|
|
125
|
+
def link(self) -> TextFieldRef:
|
|
126
|
+
return TextFieldRef(
|
|
127
|
+
path=[*self.path, "link"],
|
|
128
|
+
expected_type=self.expected_type,
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
@property
|
|
132
|
+
def access(self) -> TextFieldRef:
|
|
133
|
+
return TextFieldRef(
|
|
134
|
+
path=[*self.path, "access"],
|
|
135
|
+
expected_type=self.expected_type,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
@property
|
|
139
|
+
def restrictions(self) -> NumberFieldRef:
|
|
140
|
+
return NumberFieldRef(
|
|
141
|
+
path=[*self.path, "restrictions"],
|
|
142
|
+
expected_type=self.expected_type,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
@property
|
|
146
|
+
def pinned_message(self) -> MessageContainer:
|
|
147
|
+
return MessageContainer(
|
|
148
|
+
path=[*self.path, "pinned_message"],
|
|
149
|
+
expected_type=self.expected_type,
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
@property
|
|
153
|
+
def participants_count(self) -> NumberFieldRef:
|
|
154
|
+
return NumberFieldRef(
|
|
155
|
+
path=[*self.path, "participants_count"],
|
|
156
|
+
expected_type=self.expected_type,
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
@property
|
|
160
|
+
def description(self) -> TextFieldRef:
|
|
161
|
+
return TextFieldRef(
|
|
162
|
+
path=[*self.path, "description"],
|
|
163
|
+
expected_type=self.expected_type,
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
@property
|
|
167
|
+
def options(self) -> FieldRef:
|
|
168
|
+
return FieldRef(
|
|
169
|
+
path=[*self.path, "options"],
|
|
170
|
+
expected_type=self.expected_type,
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
@property
|
|
174
|
+
def join_time(self) -> NumberFieldRef:
|
|
175
|
+
return NumberFieldRef(
|
|
176
|
+
path=[*self.path, "join_time"],
|
|
177
|
+
expected_type=self.expected_type,
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
@property
|
|
181
|
+
def invited_by(self) -> NumberFieldRef:
|
|
182
|
+
return NumberFieldRef(
|
|
183
|
+
path=[*self.path, "invited_by"],
|
|
184
|
+
expected_type=self.expected_type,
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
@property
|
|
188
|
+
def modified(self) -> NumberFieldRef:
|
|
189
|
+
return NumberFieldRef(
|
|
190
|
+
path=[*self.path, "modified"],
|
|
191
|
+
expected_type=self.expected_type,
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
@property
|
|
195
|
+
def messages_count(self) -> NumberFieldRef:
|
|
196
|
+
return NumberFieldRef(
|
|
197
|
+
path=[*self.path, "messages_count"],
|
|
198
|
+
expected_type=self.expected_type,
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
@property
|
|
202
|
+
def has_bots(self) -> FieldRef:
|
|
203
|
+
return FieldRef(
|
|
204
|
+
path=[*self.path, "has_bots"],
|
|
205
|
+
expected_type=self.expected_type,
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
@property
|
|
209
|
+
def prev_message_id(self) -> NumberFieldRef:
|
|
210
|
+
return NumberFieldRef(
|
|
211
|
+
path=[*self.path, "prev_message_id"],
|
|
212
|
+
expected_type=self.expected_type,
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
@property
|
|
216
|
+
def admin_participants(self) -> FieldRef:
|
|
217
|
+
return FieldRef(
|
|
218
|
+
path=[*self.path, "admin_participants"],
|
|
219
|
+
expected_type=self.expected_type,
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
@property
|
|
223
|
+
def admins(self) -> FieldRef:
|
|
224
|
+
return FieldRef(
|
|
225
|
+
path=[*self.path, "admins"],
|
|
226
|
+
expected_type=self.expected_type,
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
@property
|
|
230
|
+
def cid(self) -> NumberFieldRef:
|
|
231
|
+
return NumberFieldRef(
|
|
232
|
+
path=[*self.path, "cid"],
|
|
233
|
+
expected_type=self.expected_type,
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
def __call__(self, event: Any, client: ClientT) -> bool:
|
|
237
|
+
if not isinstance(event, self.expected_type):
|
|
238
|
+
return False
|
|
239
|
+
|
|
240
|
+
return bool(self.resolve_path(event))
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import builtins
|
|
4
|
+
from typing import TYPE_CHECKING, Any, TypeVar
|
|
5
|
+
|
|
6
|
+
from pymax.filters.base import ClientT, Filter, PathResolver
|
|
7
|
+
from pymax.filters.refs import FieldRef, NumberFieldRef, TextFieldRef
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
pass
|
|
11
|
+
|
|
12
|
+
EventT = TypeVar("EventT")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class MessageContainer(Filter[EventT, ClientT], PathResolver):
|
|
16
|
+
def __init__(
|
|
17
|
+
self,
|
|
18
|
+
expected_type: builtins.type[EventT],
|
|
19
|
+
path: list[str] | None = None,
|
|
20
|
+
) -> None:
|
|
21
|
+
self.expected_type: type[EventT] = expected_type
|
|
22
|
+
self.path = path or []
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def text(self) -> TextFieldRef:
|
|
26
|
+
return TextFieldRef(
|
|
27
|
+
path=[*self.path, "text"],
|
|
28
|
+
expected_type=self.expected_type,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
def id(self) -> NumberFieldRef:
|
|
33
|
+
return NumberFieldRef(
|
|
34
|
+
path=[*self.path, "id"],
|
|
35
|
+
expected_type=self.expected_type,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
def chat_id(self) -> NumberFieldRef:
|
|
40
|
+
return NumberFieldRef(
|
|
41
|
+
path=[*self.path, "chat_id"],
|
|
42
|
+
expected_type=self.expected_type,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def sender(self) -> NumberFieldRef:
|
|
47
|
+
return NumberFieldRef(
|
|
48
|
+
path=[*self.path, "sender"],
|
|
49
|
+
expected_type=self.expected_type,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
@property
|
|
53
|
+
def time(self) -> NumberFieldRef:
|
|
54
|
+
return NumberFieldRef(
|
|
55
|
+
path=[*self.path, "time"],
|
|
56
|
+
expected_type=self.expected_type,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def type(self) -> TextFieldRef:
|
|
61
|
+
return TextFieldRef(
|
|
62
|
+
path=[*self.path, "type"],
|
|
63
|
+
expected_type=self.expected_type,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def cid(self) -> NumberFieldRef:
|
|
68
|
+
return NumberFieldRef(
|
|
69
|
+
path=[*self.path, "cid"],
|
|
70
|
+
expected_type=self.expected_type,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
@property
|
|
74
|
+
def attaches(self) -> FieldRef:
|
|
75
|
+
return FieldRef(
|
|
76
|
+
path=[*self.path, "attaches"],
|
|
77
|
+
expected_type=self.expected_type,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
@property
|
|
81
|
+
def stats(self) -> FieldRef:
|
|
82
|
+
return FieldRef(
|
|
83
|
+
path=[*self.path, "stats"],
|
|
84
|
+
expected_type=self.expected_type,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
@property
|
|
88
|
+
def status(self) -> TextFieldRef:
|
|
89
|
+
return TextFieldRef(
|
|
90
|
+
path=[*self.path, "status"],
|
|
91
|
+
expected_type=self.expected_type,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
@property
|
|
95
|
+
def reaction_info(self) -> FieldRef:
|
|
96
|
+
return FieldRef(
|
|
97
|
+
path=[*self.path, "reaction_info"],
|
|
98
|
+
expected_type=self.expected_type,
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
@property
|
|
102
|
+
def options(self) -> FieldRef:
|
|
103
|
+
return FieldRef(
|
|
104
|
+
path=[*self.path, "options"],
|
|
105
|
+
expected_type=self.expected_type,
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
@property
|
|
109
|
+
def prev_message_id(self) -> NumberFieldRef:
|
|
110
|
+
return NumberFieldRef(
|
|
111
|
+
path=[*self.path, "prev_message_id"],
|
|
112
|
+
expected_type=self.expected_type,
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
@property
|
|
116
|
+
def ttl(self) -> FieldRef:
|
|
117
|
+
return FieldRef(
|
|
118
|
+
path=[*self.path, "ttl"],
|
|
119
|
+
expected_type=self.expected_type,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
@property
|
|
123
|
+
def unread(self) -> NumberFieldRef:
|
|
124
|
+
return NumberFieldRef(
|
|
125
|
+
path=[*self.path, "unread"],
|
|
126
|
+
expected_type=self.expected_type,
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
@property
|
|
130
|
+
def mark(self) -> NumberFieldRef:
|
|
131
|
+
return NumberFieldRef(
|
|
132
|
+
path=[*self.path, "mark"],
|
|
133
|
+
expected_type=self.expected_type,
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
@property
|
|
137
|
+
def elements(self) -> FieldRef:
|
|
138
|
+
return FieldRef(
|
|
139
|
+
path=[*self.path, "elements"],
|
|
140
|
+
expected_type=self.expected_type,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
def __call__(self, event: Any, client: ClientT) -> bool:
|
|
144
|
+
if not isinstance(event, self.expected_type):
|
|
145
|
+
return False
|
|
146
|
+
|
|
147
|
+
return bool(self.resolve_path(event))
|
pymax/filters/filters.py
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, TypeVar
|
|
4
|
+
|
|
5
|
+
from .base import ClientT, Filter, PathResolver
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
pass
|
|
9
|
+
|
|
10
|
+
EventT = TypeVar("EventT")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ContainsFilter(Filter[EventT, ClientT], PathResolver):
|
|
14
|
+
def __init__(
|
|
15
|
+
self,
|
|
16
|
+
path: list[str],
|
|
17
|
+
expected_type: type[EventT],
|
|
18
|
+
text: str,
|
|
19
|
+
ignore_case: bool,
|
|
20
|
+
) -> None:
|
|
21
|
+
self.path = path
|
|
22
|
+
self.expected_type = expected_type
|
|
23
|
+
self.text = text
|
|
24
|
+
self.ignore_case = ignore_case
|
|
25
|
+
|
|
26
|
+
def __call__(self, event: EventT, _: ClientT) -> bool:
|
|
27
|
+
if not isinstance(event, self.expected_type):
|
|
28
|
+
return False
|
|
29
|
+
|
|
30
|
+
value = self.resolve_path(event)
|
|
31
|
+
|
|
32
|
+
if not isinstance(value, str):
|
|
33
|
+
return False
|
|
34
|
+
|
|
35
|
+
if self.ignore_case:
|
|
36
|
+
return self.text.casefold() in value.casefold()
|
|
37
|
+
|
|
38
|
+
return self.text in value
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class StartsWithFilter(Filter[EventT, ClientT], PathResolver):
|
|
42
|
+
def __init__(
|
|
43
|
+
self,
|
|
44
|
+
path: list[str],
|
|
45
|
+
expected_type: type[EventT],
|
|
46
|
+
text: str,
|
|
47
|
+
ignore_case: bool,
|
|
48
|
+
) -> None:
|
|
49
|
+
self.path = path
|
|
50
|
+
self.expected_type = expected_type
|
|
51
|
+
self.text = text
|
|
52
|
+
self.ignore_case = ignore_case
|
|
53
|
+
|
|
54
|
+
def __call__(self, event: EventT, _: ClientT) -> bool:
|
|
55
|
+
if not isinstance(event, self.expected_type):
|
|
56
|
+
return False
|
|
57
|
+
|
|
58
|
+
value = self.resolve_path(event)
|
|
59
|
+
|
|
60
|
+
if not isinstance(value, str):
|
|
61
|
+
return False
|
|
62
|
+
|
|
63
|
+
if self.ignore_case:
|
|
64
|
+
return value.casefold().startswith(self.text.casefold())
|
|
65
|
+
|
|
66
|
+
return value.startswith(self.text)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class EndsWithFilter(Filter[EventT, ClientT], PathResolver):
|
|
70
|
+
def __init__(
|
|
71
|
+
self,
|
|
72
|
+
path: list[str],
|
|
73
|
+
expected_type: type[EventT],
|
|
74
|
+
text: str,
|
|
75
|
+
ignore_case: bool,
|
|
76
|
+
) -> None:
|
|
77
|
+
self.path = path
|
|
78
|
+
self.expected_type = expected_type
|
|
79
|
+
self.text = text
|
|
80
|
+
self.ignore_case = ignore_case
|
|
81
|
+
|
|
82
|
+
def __call__(self, event: EventT, _: ClientT) -> bool:
|
|
83
|
+
if not isinstance(event, self.expected_type):
|
|
84
|
+
return False
|
|
85
|
+
|
|
86
|
+
value = self.resolve_path(event)
|
|
87
|
+
|
|
88
|
+
if not isinstance(value, str):
|
|
89
|
+
return False
|
|
90
|
+
|
|
91
|
+
if self.ignore_case:
|
|
92
|
+
return value.casefold().endswith(self.text.casefold())
|
|
93
|
+
|
|
94
|
+
return value.endswith(self.text)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class LessThanFilter(Filter[EventT, ClientT], PathResolver):
|
|
98
|
+
def __init__(
|
|
99
|
+
self,
|
|
100
|
+
path: list[str],
|
|
101
|
+
expected_type: type[EventT],
|
|
102
|
+
expected: float,
|
|
103
|
+
) -> None:
|
|
104
|
+
self.path = path
|
|
105
|
+
self.expected_type = expected_type
|
|
106
|
+
self.expected = expected
|
|
107
|
+
|
|
108
|
+
def __call__(self, event: EventT, _: ClientT) -> bool:
|
|
109
|
+
if not isinstance(event, self.expected_type):
|
|
110
|
+
return False
|
|
111
|
+
|
|
112
|
+
value = self.resolve_path(event)
|
|
113
|
+
|
|
114
|
+
if not isinstance(value, int | float) or isinstance(value, bool):
|
|
115
|
+
return False
|
|
116
|
+
|
|
117
|
+
return value < self.expected
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class LessThanOrEqualFilter(Filter[EventT, ClientT], PathResolver):
|
|
121
|
+
def __init__(
|
|
122
|
+
self,
|
|
123
|
+
path: list[str],
|
|
124
|
+
expected_type: type[EventT],
|
|
125
|
+
expected: float,
|
|
126
|
+
) -> None:
|
|
127
|
+
self.path = path
|
|
128
|
+
self.expected_type = expected_type
|
|
129
|
+
self.expected = expected
|
|
130
|
+
|
|
131
|
+
def __call__(self, event: EventT, _: ClientT) -> bool:
|
|
132
|
+
if not isinstance(event, self.expected_type):
|
|
133
|
+
return False
|
|
134
|
+
|
|
135
|
+
value = self.resolve_path(event)
|
|
136
|
+
|
|
137
|
+
if not isinstance(value, int | float) or isinstance(value, bool):
|
|
138
|
+
return False
|
|
139
|
+
|
|
140
|
+
return value <= self.expected
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class GreaterThanFilter(Filter[EventT, ClientT], PathResolver):
|
|
144
|
+
def __init__(
|
|
145
|
+
self,
|
|
146
|
+
path: list[str],
|
|
147
|
+
expected_type: type[EventT],
|
|
148
|
+
expected: float,
|
|
149
|
+
) -> None:
|
|
150
|
+
self.path = path
|
|
151
|
+
self.expected_type = expected_type
|
|
152
|
+
self.expected = expected
|
|
153
|
+
|
|
154
|
+
def __call__(self, event: EventT, _: ClientT) -> bool:
|
|
155
|
+
if not isinstance(event, self.expected_type):
|
|
156
|
+
return False
|
|
157
|
+
|
|
158
|
+
value = self.resolve_path(event)
|
|
159
|
+
|
|
160
|
+
if not isinstance(value, int | float) or isinstance(value, bool):
|
|
161
|
+
return False
|
|
162
|
+
|
|
163
|
+
return value > self.expected
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
class GreaterThanOrEqualFilter(Filter[EventT, ClientT], PathResolver):
|
|
167
|
+
def __init__(
|
|
168
|
+
self,
|
|
169
|
+
path: list[str],
|
|
170
|
+
expected_type: type[EventT],
|
|
171
|
+
expected: float,
|
|
172
|
+
) -> None:
|
|
173
|
+
self.path = path
|
|
174
|
+
self.expected_type = expected_type
|
|
175
|
+
self.expected = expected
|
|
176
|
+
|
|
177
|
+
def __call__(self, event: EventT, _: ClientT) -> bool:
|
|
178
|
+
if not isinstance(event, self.expected_type):
|
|
179
|
+
return False
|
|
180
|
+
|
|
181
|
+
value = self.resolve_path(event)
|
|
182
|
+
|
|
183
|
+
if not isinstance(value, int | float) or isinstance(value, bool):
|
|
184
|
+
return False
|
|
185
|
+
|
|
186
|
+
return value >= self.expected
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
class BetweenFilter(Filter[EventT, ClientT], PathResolver):
|
|
190
|
+
def __init__(
|
|
191
|
+
self,
|
|
192
|
+
path: list[str],
|
|
193
|
+
expected_type: type[EventT],
|
|
194
|
+
minimum: float,
|
|
195
|
+
maximum: float,
|
|
196
|
+
) -> None:
|
|
197
|
+
self.path = path
|
|
198
|
+
self.expected_type = expected_type
|
|
199
|
+
self.minimum = minimum
|
|
200
|
+
self.maximum = maximum
|
|
201
|
+
|
|
202
|
+
def __call__(self, event: EventT, _: ClientT) -> bool:
|
|
203
|
+
if not isinstance(event, self.expected_type):
|
|
204
|
+
return False
|
|
205
|
+
|
|
206
|
+
value = self.resolve_path(event)
|
|
207
|
+
|
|
208
|
+
if not isinstance(value, int | float) or isinstance(value, bool):
|
|
209
|
+
return False
|
|
210
|
+
|
|
211
|
+
return self.minimum <= value <= self.maximum
|
pymax/filters/magic.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from pymax.types import Chat, Message
|
|
2
|
+
|
|
3
|
+
from .containers import ChatContainer, MessageContainer
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class MagicFilter:
|
|
7
|
+
@property
|
|
8
|
+
def message(self) -> MessageContainer:
|
|
9
|
+
return MessageContainer(Message)
|
|
10
|
+
|
|
11
|
+
@property
|
|
12
|
+
def chat(self) -> ChatContainer:
|
|
13
|
+
return ChatContainer(Chat)
|
pymax/filters/presets.py
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from pymax.types import LinkType, Message
|
|
6
|
+
|
|
7
|
+
from .base import Filter
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from pymax.base import BaseClient
|
|
11
|
+
from pymax.types.domain.message import Attachment
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Command(Filter[Message, "BaseClient"]):
|
|
15
|
+
"""Проверяет команду в первом слове текста сообщения.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
command: Имя команды, например ``"start"`` для ``/start``.
|
|
19
|
+
prefix: Префикс команды или кортеж допустимых префиксов.
|
|
20
|
+
|
|
21
|
+
Аргументы после команды не влияют на совпадение.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def __init__(self, command: str, prefix: str | tuple[str, ...] = "/") -> None:
|
|
25
|
+
self.prefixes = (prefix,) if isinstance(prefix, str) else prefix
|
|
26
|
+
|
|
27
|
+
for prefix_ in self.prefixes:
|
|
28
|
+
self.command = command.removeprefix(prefix_)
|
|
29
|
+
|
|
30
|
+
def __call__(self, message: Message, _: BaseClient) -> bool:
|
|
31
|
+
if not message.text:
|
|
32
|
+
return False
|
|
33
|
+
|
|
34
|
+
parts = message.text.split(maxsplit=1)
|
|
35
|
+
if not parts:
|
|
36
|
+
return False
|
|
37
|
+
|
|
38
|
+
dirty_command = parts[0]
|
|
39
|
+
|
|
40
|
+
for prefix in self.prefixes:
|
|
41
|
+
if message.text.startswith(prefix) and dirty_command[len(prefix) :] == self.command:
|
|
42
|
+
return True
|
|
43
|
+
|
|
44
|
+
return False
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class Reply(Filter[Message, "BaseClient"]):
|
|
48
|
+
"""Проверяет, является ли сообщение ответом на другое сообщение."""
|
|
49
|
+
|
|
50
|
+
def __call__(self, message: Message, _: BaseClient) -> bool:
|
|
51
|
+
if not message.link:
|
|
52
|
+
return False
|
|
53
|
+
|
|
54
|
+
return message.link.type == LinkType.REPLY
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class Forwarded(Filter[Message, "BaseClient"]):
|
|
58
|
+
"""Проверяет, является ли сообщение пересланным."""
|
|
59
|
+
|
|
60
|
+
def __call__(self, message: Message, _: BaseClient) -> bool:
|
|
61
|
+
if not message.link:
|
|
62
|
+
return False
|
|
63
|
+
|
|
64
|
+
return message.link.type == LinkType.FORWARD
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class FromMe(Filter[Message, "BaseClient"]):
|
|
68
|
+
"""Проверяет, отправлено ли сообщение текущим пользователем клиента."""
|
|
69
|
+
|
|
70
|
+
def __call__(self, message: Message, client: BaseClient) -> bool:
|
|
71
|
+
if not message.sender or not client.me:
|
|
72
|
+
return False
|
|
73
|
+
|
|
74
|
+
return message.sender == client.me.contact.id
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class HasAttachment(Filter[Message, "BaseClient"]):
|
|
78
|
+
"""Проверяет наличие хотя бы одного вложения указанных типов.
|
|
79
|
+
|
|
80
|
+
Передавайте классы входящих вложений из ``pymax.types``, например
|
|
81
|
+
``PhotoAttachment``, а не классы файлов для отправки.
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
def __init__(self, *attachment_types: type[Attachment]) -> None:
|
|
85
|
+
self.expected_types = attachment_types
|
|
86
|
+
|
|
87
|
+
def __call__(self, message: Message, _: BaseClient) -> bool:
|
|
88
|
+
return any(isinstance(attach, self.expected_types) for attach in message.attaches)
|