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/dispatch/router.py
CHANGED
|
@@ -4,17 +4,28 @@ from collections import defaultdict
|
|
|
4
4
|
from collections.abc import Awaitable, Callable
|
|
5
5
|
from dataclasses import dataclass
|
|
6
6
|
from enum import Enum
|
|
7
|
-
from typing import
|
|
8
|
-
|
|
7
|
+
from typing import (
|
|
8
|
+
TYPE_CHECKING,
|
|
9
|
+
Any,
|
|
10
|
+
Concatenate,
|
|
11
|
+
Generic,
|
|
12
|
+
ParamSpec,
|
|
13
|
+
Protocol,
|
|
14
|
+
TypeAlias,
|
|
15
|
+
TypeVar,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
from pymax.filters import Filter
|
|
9
19
|
from pymax.types import MessageDeleteEvent
|
|
10
20
|
|
|
11
21
|
from .enums import EventType
|
|
12
22
|
|
|
13
23
|
if TYPE_CHECKING:
|
|
14
|
-
from pymax import Client
|
|
15
24
|
from pymax.base import BaseClient
|
|
25
|
+
from pymax.client import Client
|
|
26
|
+
from pymax.dispatch.middleware import Middleware
|
|
16
27
|
from pymax.protocol import InboundFrame
|
|
17
|
-
from pymax.types import Chat
|
|
28
|
+
from pymax.types import Chat, StoryPreview
|
|
18
29
|
from pymax.types.domain import Message
|
|
19
30
|
from pymax.types.events import (
|
|
20
31
|
MessageReadEvent,
|
|
@@ -33,19 +44,33 @@ class ErrorScope(str, Enum):
|
|
|
33
44
|
|
|
34
45
|
_EventT = TypeVar("_EventT")
|
|
35
46
|
ClientT = TypeVar("ClientT", bound="BaseClient")
|
|
47
|
+
_P = ParamSpec("_P")
|
|
36
48
|
|
|
37
49
|
HandlerCallback: TypeAlias = Callable[
|
|
38
50
|
[_EventT, ClientT],
|
|
39
51
|
Awaitable[Any] | Any,
|
|
40
52
|
]
|
|
41
|
-
HandlerDecorator: TypeAlias = Callable[
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
]
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
53
|
+
# HandlerDecorator: TypeAlias = Callable[
|
|
54
|
+
# [HandlerCallback[_EventT, ClientT]],
|
|
55
|
+
# HandlerCallback[_EventT, ClientT],
|
|
56
|
+
# ]
|
|
57
|
+
|
|
58
|
+
HandlerResult: TypeAlias = Awaitable[Any] | Any
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class HandlerDecorator(Protocol, Generic[_EventT, ClientT]):
|
|
62
|
+
def __call__(
|
|
63
|
+
self,
|
|
64
|
+
handler: Callable[
|
|
65
|
+
Concatenate[_EventT, ClientT, _P],
|
|
66
|
+
HandlerResult,
|
|
67
|
+
],
|
|
68
|
+
) -> Callable[
|
|
69
|
+
Concatenate[_EventT, ClientT, _P],
|
|
70
|
+
HandlerResult,
|
|
71
|
+
]: ...
|
|
72
|
+
|
|
73
|
+
|
|
49
74
|
StartCallback: TypeAlias = Callable[
|
|
50
75
|
[ClientT],
|
|
51
76
|
Awaitable[Any] | Any,
|
|
@@ -91,7 +116,7 @@ DisconnectDecorator: TypeAlias = Callable[
|
|
|
91
116
|
@dataclass(slots=True)
|
|
92
117
|
class HandlerEntry(Generic[_EventT, ClientT]):
|
|
93
118
|
callback: HandlerCallback[_EventT, ClientT]
|
|
94
|
-
filters: tuple[
|
|
119
|
+
filters: tuple[Filter, ...] = ()
|
|
95
120
|
|
|
96
121
|
|
|
97
122
|
@dataclass(slots=True)
|
|
@@ -107,20 +132,20 @@ class Router(Generic[ClientT]):
|
|
|
107
132
|
"""Контейнер обработчиков событий PyMax.
|
|
108
133
|
|
|
109
134
|
Роутер хранит обработчики и фильтры. Когда приходит событие, dispatcher
|
|
110
|
-
|
|
111
|
-
|
|
135
|
+
проверяет обработчики в порядке регистрации и вызывает первый подходящий
|
|
136
|
+
в каждом роутере, затем обходит его дочерние роутеры. Первые два аргумента
|
|
137
|
+
обработчика — ``event`` и ``client``; дополнительные обязательные параметры
|
|
138
|
+
разрешаются по аннотациям типов через DI.
|
|
112
139
|
|
|
113
140
|
Example:
|
|
114
141
|
.. code-block:: python
|
|
115
142
|
|
|
116
143
|
from pymax import Client, Message, Router
|
|
144
|
+
from pymax.filters import Command
|
|
117
145
|
|
|
118
146
|
router = Router[Client]()
|
|
119
147
|
|
|
120
|
-
|
|
121
|
-
return message.text == "/start"
|
|
122
|
-
|
|
123
|
-
@router.on_message(is_start)
|
|
148
|
+
@router.on_message(Command("start"))
|
|
124
149
|
async def start(message: Message, client: Client) -> None:
|
|
125
150
|
await message.answer("Привет")
|
|
126
151
|
|
|
@@ -135,6 +160,7 @@ class Router(Generic[ClientT]):
|
|
|
135
160
|
] = defaultdict(list)
|
|
136
161
|
|
|
137
162
|
self.children: list[Router[ClientT]] = []
|
|
163
|
+
self.middlewares: list[Middleware[ClientT]] = []
|
|
138
164
|
self.on_start_handlers: list[StartCallback[ClientT]] = []
|
|
139
165
|
self.error_handlers: list[ErrorEntry[ClientT]] = []
|
|
140
166
|
self.disconnect_handlers: list[DisconnectCallback] = []
|
|
@@ -147,6 +173,8 @@ class Router(Generic[ClientT]):
|
|
|
147
173
|
|
|
148
174
|
``GLOBAL``-handler видит ошибки всего дерева подключенных router-ов.
|
|
149
175
|
``LOCAL``-handler видит только ошибки своего router-а.
|
|
176
|
+
Callback получает ``handler(exception, context)``. При обработке
|
|
177
|
+
событий сюда передаются ошибки фильтров, middleware и handler-ов.
|
|
150
178
|
"""
|
|
151
179
|
scope = ErrorScope(scope)
|
|
152
180
|
|
|
@@ -173,13 +201,15 @@ class Router(Generic[ClientT]):
|
|
|
173
201
|
self,
|
|
174
202
|
event: EventType,
|
|
175
203
|
/,
|
|
176
|
-
*filters:
|
|
204
|
+
*filters: Filter[_EventT, ClientT],
|
|
177
205
|
) -> HandlerDecorator[_EventT, ClientT]:
|
|
178
206
|
"""Регистрирует обработчик события по ``EventType``.
|
|
179
207
|
|
|
180
208
|
Args:
|
|
181
209
|
event: Тип события.
|
|
182
|
-
*filters:
|
|
210
|
+
*filters: Экземпляры ``Filter``, вызываемые как
|
|
211
|
+
``filter(event, client)``. Все фильтры должны вернуть ``True``;
|
|
212
|
+
async-результаты ожидаются автоматически.
|
|
183
213
|
|
|
184
214
|
Returns:
|
|
185
215
|
Декоратор для ``handler(event, client)``.
|
|
@@ -187,7 +217,7 @@ class Router(Generic[ClientT]):
|
|
|
187
217
|
Example:
|
|
188
218
|
.. code-block:: python
|
|
189
219
|
|
|
190
|
-
from pymax import Client
|
|
220
|
+
from pymax import Client, EventType
|
|
191
221
|
from pymax.protocol import InboundFrame
|
|
192
222
|
|
|
193
223
|
@router.on(EventType.RAW)
|
|
@@ -196,8 +226,14 @@ class Router(Generic[ClientT]):
|
|
|
196
226
|
"""
|
|
197
227
|
|
|
198
228
|
def decorator(
|
|
199
|
-
handler:
|
|
200
|
-
|
|
229
|
+
handler: Callable[
|
|
230
|
+
Concatenate[_EventT, ClientT, _P],
|
|
231
|
+
HandlerResult,
|
|
232
|
+
],
|
|
233
|
+
) -> Callable[
|
|
234
|
+
Concatenate[_EventT, ClientT, _P],
|
|
235
|
+
HandlerResult,
|
|
236
|
+
]:
|
|
201
237
|
self.handlers[event].append(
|
|
202
238
|
HandlerEntry(
|
|
203
239
|
callback=handler,
|
|
@@ -234,7 +270,7 @@ class Router(Generic[ClientT]):
|
|
|
234
270
|
|
|
235
271
|
def on_message(
|
|
236
272
|
self,
|
|
237
|
-
*filters:
|
|
273
|
+
*filters: Filter[Message, ClientT],
|
|
238
274
|
) -> HandlerDecorator[Message, ClientT]:
|
|
239
275
|
"""Регистрирует обработчик новых сообщений.
|
|
240
276
|
|
|
@@ -248,7 +284,7 @@ class Router(Generic[ClientT]):
|
|
|
248
284
|
|
|
249
285
|
def on_message_edit(
|
|
250
286
|
self,
|
|
251
|
-
*filters:
|
|
287
|
+
*filters: Filter[Message, ClientT],
|
|
252
288
|
) -> HandlerDecorator[Message, ClientT]:
|
|
253
289
|
"""Регистрирует обработчик редактирования сообщений.
|
|
254
290
|
|
|
@@ -262,7 +298,7 @@ class Router(Generic[ClientT]):
|
|
|
262
298
|
|
|
263
299
|
def on_message_delete(
|
|
264
300
|
self,
|
|
265
|
-
*filters:
|
|
301
|
+
*filters: Filter[MessageDeleteEvent, ClientT],
|
|
266
302
|
) -> HandlerDecorator[MessageDeleteEvent, ClientT]:
|
|
267
303
|
"""Регистрирует обработчик удаления сообщений.
|
|
268
304
|
|
|
@@ -276,35 +312,38 @@ class Router(Generic[ClientT]):
|
|
|
276
312
|
|
|
277
313
|
def on_message_read(
|
|
278
314
|
self,
|
|
279
|
-
*filters:
|
|
315
|
+
*filters: Filter[MessageReadEvent, ClientT],
|
|
280
316
|
) -> HandlerDecorator[MessageReadEvent, ClientT]:
|
|
281
317
|
"""Регистрирует обработчик изменения отметки прочтения."""
|
|
282
318
|
return self.on(EventType.MESSAGE_READ, *filters)
|
|
283
319
|
|
|
284
320
|
def on_typing(
|
|
285
321
|
self,
|
|
286
|
-
*filters:
|
|
322
|
+
*filters: Filter[TypingEvent, ClientT],
|
|
287
323
|
) -> HandlerDecorator[TypingEvent, ClientT]:
|
|
288
324
|
"""Регистрирует обработчик набора текста."""
|
|
289
325
|
return self.on(EventType.TYPING, *filters)
|
|
290
326
|
|
|
291
327
|
def on_presence(
|
|
292
328
|
self,
|
|
293
|
-
*filters:
|
|
329
|
+
*filters: Filter[PresenceEvent, ClientT],
|
|
294
330
|
) -> HandlerDecorator[PresenceEvent, ClientT]:
|
|
295
331
|
"""Регистрирует обработчик изменения присутствия пользователя."""
|
|
296
332
|
return self.on(EventType.PRESENCE, *filters)
|
|
297
333
|
|
|
298
334
|
def on_reaction_update(
|
|
299
335
|
self,
|
|
300
|
-
*filters:
|
|
336
|
+
*filters: Filter[ReactionUpdateEvent, ClientT],
|
|
301
337
|
) -> HandlerDecorator[ReactionUpdateEvent, ClientT]:
|
|
302
|
-
"""Регистрирует обработчик
|
|
338
|
+
"""Регистрирует обработчик ``ReactionUpdateEvent``.
|
|
339
|
+
|
|
340
|
+
Получает обновления счетчиков реакций и реакции текущего пользователя.
|
|
341
|
+
"""
|
|
303
342
|
return self.on(EventType.REACTION_UPDATE, *filters)
|
|
304
343
|
|
|
305
344
|
def on_chat_update(
|
|
306
345
|
self,
|
|
307
|
-
*filters:
|
|
346
|
+
*filters: Filter[Chat, ClientT],
|
|
308
347
|
) -> HandlerDecorator[Chat, ClientT]:
|
|
309
348
|
"""Регистрирует обработчик обновления чата.
|
|
310
349
|
|
|
@@ -316,12 +355,25 @@ class Router(Generic[ClientT]):
|
|
|
316
355
|
"""
|
|
317
356
|
return self.on(EventType.CHAT_UPDATE, *filters)
|
|
318
357
|
|
|
358
|
+
def on_story_update(
|
|
359
|
+
self,
|
|
360
|
+
*filters: Filter[StoryPreview, ClientT],
|
|
361
|
+
) -> HandlerDecorator[StoryPreview, ClientT]:
|
|
362
|
+
"""Регистрирует обработчик обновления ``StoryPreview`` владельца.
|
|
363
|
+
|
|
364
|
+
Первый аргумент handler-а содержит сводное превью, а не ``Story``.
|
|
365
|
+
"""
|
|
366
|
+
return self.on(EventType.STORY_UPDATE, *filters)
|
|
367
|
+
|
|
319
368
|
def on_raw(
|
|
320
369
|
self,
|
|
321
|
-
*filters:
|
|
370
|
+
*filters: Filter[InboundFrame, ClientT],
|
|
322
371
|
) -> HandlerDecorator[InboundFrame, ClientT]:
|
|
323
372
|
"""Регистрирует обработчик исходных frame-ов.
|
|
324
373
|
|
|
374
|
+
Вызывается после типизированной обработки, даже если тип события
|
|
375
|
+
неизвестен или его обработка завершилась ошибкой.
|
|
376
|
+
|
|
325
377
|
Args:
|
|
326
378
|
*filters: Фильтры для ``InboundFrame``.
|
|
327
379
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from .base import Filter
|
|
2
|
+
from .magic import MagicFilter
|
|
3
|
+
from .presets import Command, Forwarded, FromMe, HasAttachment, Reply
|
|
4
|
+
|
|
5
|
+
F = MagicFilter()
|
|
6
|
+
|
|
7
|
+
__all__ = (
|
|
8
|
+
"Command",
|
|
9
|
+
"Filter",
|
|
10
|
+
"Forwarded",
|
|
11
|
+
"FromMe",
|
|
12
|
+
"HasAttachment",
|
|
13
|
+
"MagicFilter",
|
|
14
|
+
"Reply",
|
|
15
|
+
)
|
pymax/filters/base.py
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from inspect import isawaitable
|
|
5
|
+
from typing import TYPE_CHECKING, Any, Generic, TypeVar
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from collections.abc import Awaitable
|
|
9
|
+
|
|
10
|
+
from pymax.base import BaseClient
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
MISSING = object()
|
|
14
|
+
|
|
15
|
+
EventT = TypeVar("EventT")
|
|
16
|
+
ClientT = TypeVar("ClientT", bound="BaseClient")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Filter(ABC, Generic[EventT, ClientT]):
|
|
20
|
+
"""Базовый класс фильтра событий.
|
|
21
|
+
|
|
22
|
+
Реализуйте ``__call__(event, client)`` с результатом ``bool`` или
|
|
23
|
+
``Awaitable[bool]`` и передайте экземпляр в декоратор роутера. Фильтры
|
|
24
|
+
объединяются через ``&``, ``|`` и ``~``; составной фильтр при ручном вызове
|
|
25
|
+
нужно ожидать через ``await``.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
@abstractmethod
|
|
29
|
+
def __call__(
|
|
30
|
+
self,
|
|
31
|
+
event: EventT,
|
|
32
|
+
client: ClientT,
|
|
33
|
+
/,
|
|
34
|
+
) -> bool | Awaitable[bool]: ...
|
|
35
|
+
def __and__(self, other: Filter[EventT, ClientT]) -> AndFilter:
|
|
36
|
+
return AndFilter(self, other)
|
|
37
|
+
|
|
38
|
+
def __or__(self, value: Filter[EventT, ClientT]) -> OrFilter:
|
|
39
|
+
return OrFilter(self, value)
|
|
40
|
+
|
|
41
|
+
def __invert__(self) -> NotFilter:
|
|
42
|
+
return NotFilter(self)
|
|
43
|
+
|
|
44
|
+
async def resolve_filter_result(
|
|
45
|
+
self,
|
|
46
|
+
result: bool | Awaitable[bool],
|
|
47
|
+
) -> bool:
|
|
48
|
+
if isawaitable(result):
|
|
49
|
+
return await result
|
|
50
|
+
|
|
51
|
+
return result
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class PathResolver:
|
|
55
|
+
def __init__(self, path: list[str]) -> None:
|
|
56
|
+
self.path = path
|
|
57
|
+
|
|
58
|
+
def resolve_path(self, event: Any) -> Any | None:
|
|
59
|
+
value = event
|
|
60
|
+
|
|
61
|
+
for attr in self.path:
|
|
62
|
+
value = getattr(value, attr, MISSING)
|
|
63
|
+
|
|
64
|
+
if value is MISSING:
|
|
65
|
+
return MISSING
|
|
66
|
+
|
|
67
|
+
return value
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class IsNoneFilter(Filter[EventT, ClientT], PathResolver):
|
|
71
|
+
def __init__(
|
|
72
|
+
self,
|
|
73
|
+
path: list[str],
|
|
74
|
+
expected_type: type[EventT],
|
|
75
|
+
) -> None:
|
|
76
|
+
super().__init__(path)
|
|
77
|
+
self.expected_type = expected_type
|
|
78
|
+
self.path = path
|
|
79
|
+
|
|
80
|
+
def __call__(self, event: EventT, _: ClientT) -> bool:
|
|
81
|
+
if not isinstance(event, self.expected_type):
|
|
82
|
+
return False
|
|
83
|
+
|
|
84
|
+
value = self.resolve_path(event)
|
|
85
|
+
|
|
86
|
+
if value is MISSING:
|
|
87
|
+
return False
|
|
88
|
+
|
|
89
|
+
return value is None
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class IsEmptyFilter(Filter[EventT, ClientT], PathResolver):
|
|
93
|
+
def __init__(
|
|
94
|
+
self,
|
|
95
|
+
path: list[str],
|
|
96
|
+
expected_type: type[EventT],
|
|
97
|
+
) -> None:
|
|
98
|
+
super().__init__(path)
|
|
99
|
+
self.expected_type = expected_type
|
|
100
|
+
self.path = path
|
|
101
|
+
|
|
102
|
+
def __call__(self, event: EventT, _: ClientT) -> bool:
|
|
103
|
+
if not isinstance(event, self.expected_type):
|
|
104
|
+
return False
|
|
105
|
+
|
|
106
|
+
value = self.resolve_path(event)
|
|
107
|
+
|
|
108
|
+
return value == []
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class FieldRef(Filter[EventT, ClientT], PathResolver):
|
|
112
|
+
def __init__(self, path: list[str], expected_type: type[EventT]) -> None:
|
|
113
|
+
super().__init__(path)
|
|
114
|
+
self.expected_type: type[EventT] = expected_type
|
|
115
|
+
self.path = path
|
|
116
|
+
|
|
117
|
+
def __eq__(self, value: object) -> EqFilter: # pyright: ignore[reportIncompatibleMethodOverride] # ty: ignore[invalid-method-override]
|
|
118
|
+
return EqFilter(self.path, self.expected_type, value)
|
|
119
|
+
|
|
120
|
+
def __ne__(self, value: object) -> NeFilter: # pyright: ignore[reportIncompatibleMethodOverride] # ty: ignore[invalid-method-override]
|
|
121
|
+
return NeFilter(self.path, self.expected_type, value)
|
|
122
|
+
|
|
123
|
+
def is_none(self) -> IsNoneFilter:
|
|
124
|
+
return IsNoneFilter(self.path, expected_type=self.expected_type)
|
|
125
|
+
|
|
126
|
+
def is_empty(self) -> IsEmptyFilter:
|
|
127
|
+
return IsEmptyFilter(self.path, expected_type=self.expected_type)
|
|
128
|
+
|
|
129
|
+
def __call__(self, event: EventT, _: ClientT, /) -> bool:
|
|
130
|
+
if not isinstance(event, self.expected_type):
|
|
131
|
+
return False
|
|
132
|
+
|
|
133
|
+
value = self.resolve_path(event)
|
|
134
|
+
|
|
135
|
+
if value is MISSING:
|
|
136
|
+
return False
|
|
137
|
+
|
|
138
|
+
return bool(value)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
class BinaryFilter(Filter[EventT, ClientT]):
|
|
142
|
+
def __init__(self, left: Filter[EventT, ClientT], right: Filter[EventT, ClientT]) -> None:
|
|
143
|
+
self.left = left
|
|
144
|
+
self.right = right
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class NotFilter(Filter[EventT, ClientT]):
|
|
148
|
+
def __init__(self, filter_instance: Filter[EventT, ClientT]) -> None:
|
|
149
|
+
self.filter = filter_instance
|
|
150
|
+
|
|
151
|
+
async def __call__(self, event: EventT, client: ClientT) -> bool:
|
|
152
|
+
return not await self.resolve_filter_result(self.filter(event, client))
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
class NeFilter(Filter[EventT, ClientT], PathResolver):
|
|
156
|
+
def __init__(
|
|
157
|
+
self,
|
|
158
|
+
path: list[str],
|
|
159
|
+
expected_type: type[EventT],
|
|
160
|
+
expected: object,
|
|
161
|
+
) -> None:
|
|
162
|
+
self.path: list[str] = path
|
|
163
|
+
self.expected_type = expected_type
|
|
164
|
+
self.expected: object = expected
|
|
165
|
+
|
|
166
|
+
def __call__(self, event: EventT, _: ClientT) -> bool:
|
|
167
|
+
if not isinstance(event, self.expected_type):
|
|
168
|
+
return False
|
|
169
|
+
|
|
170
|
+
value = self.resolve_path(event)
|
|
171
|
+
|
|
172
|
+
if value is MISSING:
|
|
173
|
+
return False
|
|
174
|
+
|
|
175
|
+
return value != self.expected
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
class AndFilter(BinaryFilter[EventT, ClientT]):
|
|
179
|
+
async def __call__(self, event: EventT, client: ClientT) -> bool:
|
|
180
|
+
left = await self.resolve_filter_result(self.left(event, client))
|
|
181
|
+
|
|
182
|
+
if not left:
|
|
183
|
+
return False
|
|
184
|
+
|
|
185
|
+
return await self.resolve_filter_result(self.right(event, client))
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
class OrFilter(BinaryFilter[EventT, ClientT]):
|
|
189
|
+
async def __call__(self, event: EventT, client: ClientT) -> bool:
|
|
190
|
+
left = await self.resolve_filter_result(self.left(event, client))
|
|
191
|
+
|
|
192
|
+
if left:
|
|
193
|
+
return True
|
|
194
|
+
|
|
195
|
+
return await self.resolve_filter_result(self.right(event, client))
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
class EqFilter(Filter[EventT, ClientT], PathResolver):
|
|
199
|
+
def __init__(
|
|
200
|
+
self,
|
|
201
|
+
path: list[str],
|
|
202
|
+
expected_type: type[EventT],
|
|
203
|
+
expected: object,
|
|
204
|
+
) -> None:
|
|
205
|
+
self.path: list[str] = path
|
|
206
|
+
self.expected_type = expected_type
|
|
207
|
+
self.expected: object = expected
|
|
208
|
+
|
|
209
|
+
def __call__(self, event: EventT, _: ClientT) -> bool:
|
|
210
|
+
if not isinstance(event, self.expected_type):
|
|
211
|
+
return False
|
|
212
|
+
|
|
213
|
+
value = self.resolve_path(event)
|
|
214
|
+
|
|
215
|
+
if value is MISSING:
|
|
216
|
+
return False
|
|
217
|
+
|
|
218
|
+
return value == self.expected
|