telegrinder 0.4.1__py3-none-any.whl → 0.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.
Potentially problematic release.
This version of telegrinder might be problematic. Click here for more details.
- telegrinder/__init__.py +38 -56
- telegrinder/__meta__.py +1 -0
- telegrinder/api/__init__.py +6 -4
- telegrinder/api/api.py +100 -26
- telegrinder/api/error.py +42 -8
- telegrinder/api/response.py +4 -1
- telegrinder/api/token.py +2 -2
- telegrinder/bot/__init__.py +9 -25
- telegrinder/bot/bot.py +31 -25
- telegrinder/bot/cute_types/__init__.py +0 -0
- telegrinder/bot/cute_types/base.py +103 -61
- telegrinder/bot/cute_types/callback_query.py +447 -400
- telegrinder/bot/cute_types/chat_join_request.py +59 -62
- telegrinder/bot/cute_types/chat_member_updated.py +154 -157
- telegrinder/bot/cute_types/inline_query.py +41 -44
- telegrinder/bot/cute_types/message.py +2621 -2590
- telegrinder/bot/cute_types/pre_checkout_query.py +38 -42
- telegrinder/bot/cute_types/update.py +1 -8
- telegrinder/bot/cute_types/utils.py +1 -1
- telegrinder/bot/dispatch/__init__.py +10 -15
- telegrinder/bot/dispatch/abc.py +12 -11
- telegrinder/bot/dispatch/action.py +104 -0
- telegrinder/bot/dispatch/context.py +32 -26
- telegrinder/bot/dispatch/dispatch.py +61 -134
- telegrinder/bot/dispatch/handler/__init__.py +2 -0
- telegrinder/bot/dispatch/handler/abc.py +10 -8
- telegrinder/bot/dispatch/handler/audio_reply.py +2 -3
- telegrinder/bot/dispatch/handler/base.py +10 -33
- telegrinder/bot/dispatch/handler/document_reply.py +2 -3
- telegrinder/bot/dispatch/handler/func.py +55 -87
- telegrinder/bot/dispatch/handler/media_group_reply.py +2 -3
- telegrinder/bot/dispatch/handler/message_reply.py +2 -3
- telegrinder/bot/dispatch/handler/photo_reply.py +2 -3
- telegrinder/bot/dispatch/handler/sticker_reply.py +2 -3
- telegrinder/bot/dispatch/handler/video_reply.py +2 -3
- telegrinder/bot/dispatch/middleware/__init__.py +0 -0
- telegrinder/bot/dispatch/middleware/abc.py +79 -55
- telegrinder/bot/dispatch/middleware/global_middleware.py +18 -33
- telegrinder/bot/dispatch/process.py +84 -105
- telegrinder/bot/dispatch/return_manager/__init__.py +0 -0
- telegrinder/bot/dispatch/return_manager/abc.py +102 -65
- telegrinder/bot/dispatch/return_manager/callback_query.py +4 -5
- telegrinder/bot/dispatch/return_manager/inline_query.py +3 -4
- telegrinder/bot/dispatch/return_manager/message.py +8 -10
- telegrinder/bot/dispatch/return_manager/pre_checkout_query.py +4 -5
- telegrinder/bot/dispatch/view/__init__.py +4 -4
- telegrinder/bot/dispatch/view/abc.py +6 -16
- telegrinder/bot/dispatch/view/base.py +54 -178
- telegrinder/bot/dispatch/view/box.py +19 -18
- telegrinder/bot/dispatch/view/callback_query.py +4 -8
- telegrinder/bot/dispatch/view/chat_join_request.py +5 -6
- telegrinder/bot/dispatch/view/chat_member.py +5 -25
- telegrinder/bot/dispatch/view/error.py +9 -0
- telegrinder/bot/dispatch/view/inline_query.py +4 -8
- telegrinder/bot/dispatch/view/message.py +5 -25
- telegrinder/bot/dispatch/view/pre_checkout_query.py +4 -8
- telegrinder/bot/dispatch/view/raw.py +3 -109
- telegrinder/bot/dispatch/waiter_machine/__init__.py +2 -5
- telegrinder/bot/dispatch/waiter_machine/actions.py +6 -4
- telegrinder/bot/dispatch/waiter_machine/hasher/__init__.py +1 -3
- telegrinder/bot/dispatch/waiter_machine/hasher/callback.py +1 -1
- telegrinder/bot/dispatch/waiter_machine/hasher/hasher.py +11 -7
- telegrinder/bot/dispatch/waiter_machine/hasher/message.py +0 -0
- telegrinder/bot/dispatch/waiter_machine/machine.py +43 -60
- telegrinder/bot/dispatch/waiter_machine/middleware.py +19 -23
- telegrinder/bot/dispatch/waiter_machine/short_state.py +6 -5
- telegrinder/bot/polling/__init__.py +0 -0
- telegrinder/bot/polling/abc.py +0 -0
- telegrinder/bot/polling/polling.py +209 -88
- telegrinder/bot/rules/__init__.py +3 -16
- telegrinder/bot/rules/abc.py +42 -122
- telegrinder/bot/rules/callback_data.py +29 -49
- telegrinder/bot/rules/chat_join.py +5 -23
- telegrinder/bot/rules/command.py +8 -4
- telegrinder/bot/rules/enum_text.py +3 -4
- telegrinder/bot/rules/func.py +7 -14
- telegrinder/bot/rules/fuzzy.py +3 -4
- telegrinder/bot/rules/inline.py +8 -20
- telegrinder/bot/rules/integer.py +2 -3
- telegrinder/bot/rules/is_from.py +12 -11
- telegrinder/bot/rules/logic.py +11 -5
- telegrinder/bot/rules/markup.py +22 -14
- telegrinder/bot/rules/mention.py +8 -7
- telegrinder/bot/rules/message_entities.py +8 -4
- telegrinder/bot/rules/node.py +23 -12
- telegrinder/bot/rules/payload.py +5 -4
- telegrinder/bot/rules/payment_invoice.py +6 -21
- telegrinder/bot/rules/regex.py +2 -4
- telegrinder/bot/rules/rule_enum.py +8 -7
- telegrinder/bot/rules/start.py +5 -6
- telegrinder/bot/rules/state.py +1 -1
- telegrinder/bot/rules/text.py +4 -15
- telegrinder/bot/rules/update.py +3 -4
- telegrinder/bot/scenario/__init__.py +0 -0
- telegrinder/bot/scenario/abc.py +6 -5
- telegrinder/bot/scenario/checkbox.py +1 -1
- telegrinder/bot/scenario/choice.py +30 -39
- telegrinder/client/__init__.py +3 -5
- telegrinder/client/abc.py +11 -6
- telegrinder/client/aiohttp.py +141 -27
- telegrinder/client/form_data.py +1 -1
- telegrinder/model.py +61 -89
- telegrinder/modules.py +325 -102
- telegrinder/msgspec_utils/__init__.py +40 -0
- telegrinder/msgspec_utils/abc.py +18 -0
- telegrinder/msgspec_utils/custom_types/__init__.py +6 -0
- telegrinder/msgspec_utils/custom_types/datetime.py +24 -0
- telegrinder/msgspec_utils/custom_types/enum_meta.py +43 -0
- telegrinder/msgspec_utils/custom_types/literal.py +25 -0
- telegrinder/msgspec_utils/custom_types/option.py +17 -0
- telegrinder/msgspec_utils/decoder.py +389 -0
- telegrinder/msgspec_utils/encoder.py +206 -0
- telegrinder/{msgspec_json.py → msgspec_utils/json.py} +6 -5
- telegrinder/msgspec_utils/tools.py +75 -0
- telegrinder/node/__init__.py +24 -7
- telegrinder/node/attachment.py +1 -0
- telegrinder/node/base.py +154 -72
- telegrinder/node/callback_query.py +5 -5
- telegrinder/node/collection.py +39 -0
- telegrinder/node/command.py +1 -2
- telegrinder/node/composer.py +121 -72
- telegrinder/node/container.py +11 -8
- telegrinder/node/context.py +48 -0
- telegrinder/node/either.py +27 -40
- telegrinder/node/error.py +41 -0
- telegrinder/node/event.py +37 -11
- telegrinder/node/exceptions.py +7 -0
- telegrinder/node/file.py +0 -0
- telegrinder/node/i18n.py +108 -0
- telegrinder/node/me.py +3 -2
- telegrinder/node/payload.py +1 -1
- telegrinder/node/polymorphic.py +63 -28
- telegrinder/node/reply_message.py +12 -0
- telegrinder/node/rule.py +6 -13
- telegrinder/node/scope.py +14 -5
- telegrinder/node/session.py +53 -0
- telegrinder/node/source.py +41 -9
- telegrinder/node/text.py +1 -2
- telegrinder/node/tools/__init__.py +0 -0
- telegrinder/node/tools/generator.py +3 -5
- telegrinder/node/utility.py +16 -0
- telegrinder/py.typed +0 -0
- telegrinder/rules.py +0 -0
- telegrinder/tools/__init__.py +48 -88
- telegrinder/tools/aio.py +103 -0
- telegrinder/tools/callback_data_serialization/__init__.py +5 -0
- telegrinder/tools/{callback_data_serilization → callback_data_serialization}/abc.py +0 -0
- telegrinder/tools/{callback_data_serilization → callback_data_serialization}/json_ser.py +2 -3
- telegrinder/tools/{callback_data_serilization → callback_data_serialization}/msgpack_ser.py +45 -27
- telegrinder/tools/final.py +21 -0
- telegrinder/tools/formatting/__init__.py +2 -18
- telegrinder/tools/formatting/deep_links/__init__.py +39 -0
- telegrinder/tools/formatting/{deep_links.py → deep_links/links.py} +12 -85
- telegrinder/tools/formatting/deep_links/parsing.py +90 -0
- telegrinder/tools/formatting/deep_links/validators.py +8 -0
- telegrinder/tools/formatting/html_formatter.py +18 -45
- telegrinder/tools/fullname.py +83 -0
- telegrinder/tools/global_context/__init__.py +4 -3
- telegrinder/tools/global_context/abc.py +17 -14
- telegrinder/tools/global_context/builtin_context.py +39 -0
- telegrinder/tools/global_context/global_context.py +138 -39
- telegrinder/tools/input_file_directory.py +0 -0
- telegrinder/tools/keyboard/__init__.py +39 -0
- telegrinder/tools/keyboard/abc.py +159 -0
- telegrinder/tools/keyboard/base.py +77 -0
- telegrinder/tools/keyboard/buttons/__init__.py +14 -0
- telegrinder/tools/keyboard/buttons/base.py +18 -0
- telegrinder/tools/{buttons.py → keyboard/buttons/buttons.py} +71 -23
- telegrinder/tools/keyboard/buttons/static_buttons.py +56 -0
- telegrinder/tools/keyboard/buttons/tools.py +18 -0
- telegrinder/tools/keyboard/data.py +20 -0
- telegrinder/tools/keyboard/keyboard.py +131 -0
- telegrinder/tools/keyboard/static_keyboard.py +83 -0
- telegrinder/tools/lifespan.py +87 -51
- telegrinder/tools/limited_dict.py +4 -1
- telegrinder/tools/loop_wrapper.py +332 -0
- telegrinder/tools/magic/__init__.py +32 -0
- telegrinder/tools/magic/annotations.py +165 -0
- telegrinder/tools/magic/dictionary.py +20 -0
- telegrinder/tools/magic/function.py +246 -0
- telegrinder/tools/magic/shortcut.py +111 -0
- telegrinder/tools/parse_mode.py +9 -3
- telegrinder/tools/singleton/__init__.py +4 -0
- telegrinder/tools/singleton/abc.py +14 -0
- telegrinder/tools/singleton/singleton.py +18 -0
- telegrinder/tools/state_storage/__init__.py +0 -0
- telegrinder/tools/state_storage/abc.py +6 -1
- telegrinder/tools/state_storage/memory.py +1 -1
- telegrinder/tools/strings.py +0 -0
- telegrinder/types/__init__.py +307 -268
- telegrinder/types/enums.py +64 -37
- telegrinder/types/input_file.py +3 -3
- telegrinder/types/methods.py +5699 -5055
- telegrinder/types/methods_utils.py +62 -0
- telegrinder/types/objects.py +7846 -7058
- telegrinder/verification_utils.py +3 -1
- telegrinder-0.5.0.dist-info/METADATA +162 -0
- telegrinder-0.5.0.dist-info/RECORD +200 -0
- {telegrinder-0.4.1.dist-info → telegrinder-0.5.0.dist-info}/WHEEL +1 -1
- {telegrinder-0.4.1.dist-info → telegrinder-0.5.0.dist-info/licenses}/LICENSE +2 -2
- telegrinder/bot/dispatch/waiter_machine/hasher/state.py +0 -20
- telegrinder/bot/rules/id.py +0 -24
- telegrinder/bot/rules/message.py +0 -15
- telegrinder/client/sonic.py +0 -212
- telegrinder/msgspec_utils.py +0 -478
- telegrinder/tools/adapter/__init__.py +0 -19
- telegrinder/tools/adapter/abc.py +0 -49
- telegrinder/tools/adapter/dataclass.py +0 -56
- telegrinder/tools/adapter/errors.py +0 -5
- telegrinder/tools/adapter/event.py +0 -63
- telegrinder/tools/adapter/node.py +0 -46
- telegrinder/tools/adapter/raw_event.py +0 -27
- telegrinder/tools/adapter/raw_update.py +0 -30
- telegrinder/tools/callback_data_serilization/__init__.py +0 -5
- telegrinder/tools/error_handler/__init__.py +0 -10
- telegrinder/tools/error_handler/abc.py +0 -30
- telegrinder/tools/error_handler/error.py +0 -9
- telegrinder/tools/error_handler/error_handler.py +0 -179
- telegrinder/tools/formatting/spec_html_formats.py +0 -75
- telegrinder/tools/functional.py +0 -8
- telegrinder/tools/global_context/telegrinder_ctx.py +0 -27
- telegrinder/tools/i18n/__init__.py +0 -12
- telegrinder/tools/i18n/abc.py +0 -32
- telegrinder/tools/i18n/middleware/__init__.py +0 -3
- telegrinder/tools/i18n/middleware/abc.py +0 -22
- telegrinder/tools/i18n/simple.py +0 -43
- telegrinder/tools/keyboard.py +0 -132
- telegrinder/tools/loop_wrapper/__init__.py +0 -4
- telegrinder/tools/loop_wrapper/abc.py +0 -20
- telegrinder/tools/loop_wrapper/loop_wrapper.py +0 -169
- telegrinder/tools/magic.py +0 -344
- telegrinder-0.4.1.dist-info/METADATA +0 -143
- telegrinder-0.4.1.dist-info/RECORD +0 -182
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import typing
|
|
2
2
|
|
|
3
|
-
from telegrinder.api.api import API
|
|
4
3
|
from telegrinder.bot.cute_types.message import MessageCute
|
|
5
4
|
from telegrinder.bot.dispatch.context import Context
|
|
6
5
|
from telegrinder.bot.dispatch.handler.base import BaseReplyHandler
|
|
@@ -31,8 +30,8 @@ class PhotoReplyHandler(BaseReplyHandler):
|
|
|
31
30
|
**default_params,
|
|
32
31
|
)
|
|
33
32
|
|
|
34
|
-
async def
|
|
35
|
-
method =
|
|
33
|
+
async def handle(self, message: MessageCute) -> None:
|
|
34
|
+
method = message.answer_photo if not self.as_reply else message.reply_photo
|
|
36
35
|
await method(
|
|
37
36
|
photo=self.photo,
|
|
38
37
|
parse_mode=self.parse_mode,
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import typing
|
|
2
2
|
|
|
3
|
-
from telegrinder.api.api import API
|
|
4
3
|
from telegrinder.bot.cute_types.message import MessageCute
|
|
5
4
|
from telegrinder.bot.dispatch.context import Context
|
|
6
5
|
from telegrinder.bot.dispatch.handler.base import BaseReplyHandler
|
|
@@ -29,8 +28,8 @@ class StickerReplyHandler(BaseReplyHandler):
|
|
|
29
28
|
**default_params,
|
|
30
29
|
)
|
|
31
30
|
|
|
32
|
-
async def
|
|
33
|
-
method =
|
|
31
|
+
async def handle(self, message: MessageCute) -> None:
|
|
32
|
+
method = message.answer_sticker if not self.as_reply else message.reply_sticker
|
|
34
33
|
await method(sticker=self.sticker, emoji=self.emoji, **self.default_params)
|
|
35
34
|
|
|
36
35
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import typing
|
|
2
2
|
|
|
3
|
-
from telegrinder.api.api import API
|
|
4
3
|
from telegrinder.bot.cute_types.message import MessageCute
|
|
5
4
|
from telegrinder.bot.dispatch.context import Context
|
|
6
5
|
from telegrinder.bot.dispatch.handler.base import BaseReplyHandler
|
|
@@ -31,8 +30,8 @@ class VideoReplyHandler(BaseReplyHandler):
|
|
|
31
30
|
**default_params,
|
|
32
31
|
)
|
|
33
32
|
|
|
34
|
-
async def run(self,
|
|
35
|
-
method =
|
|
33
|
+
async def run(self, message: MessageCute) -> None:
|
|
34
|
+
method = message.answer_video if not self.as_reply else message.reply_video
|
|
36
35
|
await method(
|
|
37
36
|
video=self.video,
|
|
38
37
|
parse_mode=self.parse_mode,
|
|
File without changes
|
|
@@ -1,95 +1,119 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
1
|
from abc import ABC
|
|
2
|
+
from functools import cached_property
|
|
4
3
|
|
|
5
4
|
import typing_extensions as typing
|
|
6
|
-
from fntypes import
|
|
5
|
+
from fntypes.result import Error, Ok
|
|
7
6
|
|
|
8
|
-
from telegrinder.api import API
|
|
7
|
+
from telegrinder.api.api import API
|
|
9
8
|
from telegrinder.bot.cute_types.base import BaseCute
|
|
10
9
|
from telegrinder.bot.dispatch.context import Context
|
|
11
|
-
from telegrinder.model import Model
|
|
12
10
|
from telegrinder.modules import logger
|
|
13
|
-
from telegrinder.
|
|
11
|
+
from telegrinder.node.base import IsNode, get_nodes
|
|
12
|
+
from telegrinder.node.composer import compose_nodes
|
|
13
|
+
from telegrinder.tools.aio import maybe_awaitable
|
|
14
14
|
from telegrinder.tools.lifespan import Lifespan
|
|
15
|
+
from telegrinder.tools.magic.function import bundle
|
|
15
16
|
from telegrinder.types.objects import Update
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
type Event = Update | BaseCute[typing.Any]
|
|
19
|
+
type MiddlewareResult = bool | None | typing.Coroutine[typing.Any, typing.Any, bool | None]
|
|
18
20
|
|
|
19
21
|
|
|
20
|
-
async def run_middleware
|
|
21
|
-
method: typing.Callable[
|
|
22
|
-
api: API
|
|
23
|
-
event:
|
|
22
|
+
async def run_middleware(
|
|
23
|
+
method: typing.Callable[..., MiddlewareResult],
|
|
24
|
+
api: API,
|
|
25
|
+
event: Update,
|
|
24
26
|
ctx: Context,
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
if
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
case
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
27
|
+
required_nodes: typing.Mapping[str, IsNode] | None = None,
|
|
28
|
+
) -> bool | None:
|
|
29
|
+
node_col = None
|
|
30
|
+
data = {API: api, Update: event, Context: ctx}
|
|
31
|
+
|
|
32
|
+
if required_nodes:
|
|
33
|
+
match await compose_nodes(required_nodes, ctx, data=data):
|
|
34
|
+
case Ok(value):
|
|
35
|
+
node_col = value
|
|
36
|
+
case Error(compose_error):
|
|
37
|
+
logger.debug(
|
|
38
|
+
"Cannot compose nodes for `{}`, error: {!r}",
|
|
39
|
+
method.__qualname__,
|
|
40
|
+
compose_error.message,
|
|
41
|
+
)
|
|
42
|
+
return False
|
|
43
|
+
|
|
44
|
+
try:
|
|
45
|
+
bundle_method = bundle(method, ctx | ({} if node_col is None else node_col.values))
|
|
46
|
+
bundle_method &= bundle(method, data, typebundle=True)
|
|
47
|
+
return await maybe_awaitable(bundle_method())
|
|
48
|
+
finally:
|
|
49
|
+
if node_col is not None:
|
|
50
|
+
await node_col.close_all()
|
|
51
|
+
|
|
45
52
|
|
|
53
|
+
class ABCMiddleware(ABC):
|
|
46
54
|
def __repr__(self) -> str:
|
|
47
|
-
name = f"middleware {self.
|
|
48
|
-
|
|
49
|
-
has_post = self.post.__qualname__.split(".")[0] != "ABCMiddleware"
|
|
55
|
+
name = f"middleware {type(self).__name__!r}"
|
|
56
|
+
middleware_class = type(self)
|
|
50
57
|
|
|
51
|
-
if
|
|
58
|
+
if middleware_class.post is not ABCMiddleware.post:
|
|
52
59
|
name = "post-" + name
|
|
53
|
-
|
|
60
|
+
|
|
61
|
+
if middleware_class.pre is not ABCMiddleware.pre:
|
|
54
62
|
name = "pre-" + name
|
|
55
63
|
|
|
56
|
-
return "<{}
|
|
64
|
+
return f"<{name}>"
|
|
65
|
+
|
|
66
|
+
if typing.TYPE_CHECKING:
|
|
67
|
+
|
|
68
|
+
def pre(self, *args: typing.Any, **kwargs: typing.Any) -> MiddlewareResult: ...
|
|
69
|
+
|
|
70
|
+
def post(self, *args: typing.Any, **kwargs: typing.Any) -> MiddlewareResult: ...
|
|
57
71
|
|
|
58
|
-
|
|
72
|
+
else:
|
|
59
73
|
|
|
60
|
-
|
|
74
|
+
def pre(self, *args, **kwargs): ...
|
|
61
75
|
|
|
62
|
-
|
|
63
|
-
def to_lifespan(self, event: Event, ctx: Context | None = None, *, api: API) -> Lifespan: ...
|
|
76
|
+
def post(self, *args, **kwargs): ...
|
|
64
77
|
|
|
65
|
-
@
|
|
66
|
-
def
|
|
78
|
+
@cached_property
|
|
79
|
+
def pre_required_nodes(self) -> dict[str, IsNode]:
|
|
80
|
+
return get_nodes(self.pre)
|
|
81
|
+
|
|
82
|
+
@cached_property
|
|
83
|
+
def post_required_nodes(self) -> dict[str, IsNode]:
|
|
84
|
+
return get_nodes(self.post)
|
|
67
85
|
|
|
68
86
|
def to_lifespan(
|
|
69
87
|
self,
|
|
70
88
|
event: Event,
|
|
71
|
-
ctx: Context
|
|
89
|
+
ctx: Context,
|
|
90
|
+
*,
|
|
72
91
|
api: API | None = None,
|
|
73
|
-
**add_context: typing.Any,
|
|
74
92
|
) -> Lifespan:
|
|
93
|
+
if isinstance(event, BaseCute):
|
|
94
|
+
event, api = event.raw_update, event.api
|
|
95
|
+
|
|
75
96
|
if api is None:
|
|
76
|
-
|
|
77
|
-
raise LookupError("Cannot get api, please pass as kwarg or provide BaseCute api-bound event")
|
|
78
|
-
api = event.api
|
|
97
|
+
raise LookupError("Cannot get api, please pass as kwarg or provide BaseCute api-bound event.")
|
|
79
98
|
|
|
80
|
-
ctx = ctx or Context()
|
|
81
|
-
ctx |= add_context
|
|
82
99
|
return Lifespan(
|
|
83
|
-
startup_tasks=[
|
|
100
|
+
startup_tasks=[
|
|
101
|
+
run_middleware(
|
|
102
|
+
self.pre,
|
|
103
|
+
api,
|
|
104
|
+
event,
|
|
105
|
+
ctx,
|
|
106
|
+
required_nodes=self.pre_required_nodes,
|
|
107
|
+
),
|
|
108
|
+
],
|
|
84
109
|
shutdown_tasks=[
|
|
85
110
|
run_middleware(
|
|
86
111
|
self.post,
|
|
87
112
|
api,
|
|
88
113
|
event,
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
)
|
|
114
|
+
ctx,
|
|
115
|
+
required_nodes=self.post_required_nodes,
|
|
116
|
+
),
|
|
93
117
|
],
|
|
94
118
|
)
|
|
95
119
|
|
|
@@ -1,50 +1,36 @@
|
|
|
1
|
-
import inspect
|
|
2
1
|
import typing
|
|
3
2
|
from contextlib import contextmanager
|
|
4
3
|
|
|
5
|
-
from telegrinder.api import API
|
|
6
|
-
from telegrinder.bot.cute_types.update import UpdateCute
|
|
4
|
+
from telegrinder.api.api import API
|
|
7
5
|
from telegrinder.bot.dispatch.context import Context
|
|
8
6
|
from telegrinder.bot.dispatch.middleware.abc import ABCMiddleware
|
|
9
7
|
from telegrinder.bot.rules.abc import ABCRule, check_rule
|
|
10
|
-
from telegrinder.node import IsNode
|
|
11
|
-
from telegrinder.
|
|
12
|
-
from telegrinder.tools.adapter.raw_update import RawUpdateAdapter
|
|
8
|
+
from telegrinder.node.base import IsNode
|
|
9
|
+
from telegrinder.node.composer import compose_nodes
|
|
13
10
|
from telegrinder.types import Update
|
|
11
|
+
from telegrinder.types.objects import Update
|
|
14
12
|
|
|
15
13
|
|
|
16
14
|
class GlobalMiddleware(ABCMiddleware):
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def __init__(self):
|
|
15
|
+
def __init__(self) -> None:
|
|
20
16
|
self.filters: set[ABCRule] = set()
|
|
21
|
-
self.source_filters: dict[
|
|
17
|
+
self.source_filters: dict[IsNode, dict[typing.Any, ABCRule]] = {}
|
|
22
18
|
|
|
23
|
-
async def pre(self, event:
|
|
19
|
+
async def pre(self, event: Update, api: API, ctx: Context) -> bool:
|
|
24
20
|
for filter in self.filters:
|
|
25
|
-
if not await check_rule(
|
|
21
|
+
if not await check_rule(api, filter, event, ctx):
|
|
26
22
|
return False
|
|
27
23
|
|
|
28
|
-
# Simple implication
|
|
24
|
+
# Simple implication. Grouped by source categories
|
|
29
25
|
for source, identifiers in self.source_filters.items():
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
result = await result
|
|
34
|
-
|
|
35
|
-
result = result.unwrap_or_none()
|
|
36
|
-
if result is None:
|
|
37
|
-
return True
|
|
38
|
-
|
|
26
|
+
result = await compose_nodes({"value": source}, ctx, {Update: event, API: api})
|
|
27
|
+
if result := result.unwrap():
|
|
28
|
+
result = result.values["value"]
|
|
39
29
|
else:
|
|
40
|
-
|
|
41
|
-
if result := result.unwrap():
|
|
42
|
-
result = result.values["value"]
|
|
43
|
-
else:
|
|
44
|
-
return True
|
|
30
|
+
return True
|
|
45
31
|
|
|
46
32
|
if result in identifiers:
|
|
47
|
-
return await check_rule(
|
|
33
|
+
return await check_rule(api, identifiers[result], event, ctx)
|
|
48
34
|
|
|
49
35
|
return True
|
|
50
36
|
|
|
@@ -52,8 +38,8 @@ class GlobalMiddleware(ABCMiddleware):
|
|
|
52
38
|
def apply_filters(
|
|
53
39
|
self,
|
|
54
40
|
*filters: ABCRule,
|
|
55
|
-
source_filter: tuple[
|
|
56
|
-
):
|
|
41
|
+
source_filter: tuple[IsNode, typing.Any, ABCRule] | None = None,
|
|
42
|
+
) -> typing.Generator[None, typing.Any, None]:
|
|
57
43
|
if source_filter is not None:
|
|
58
44
|
self.source_filters.setdefault(source_filter[0], {})
|
|
59
45
|
self.source_filters[source_filter[0]].update({source_filter[1]: source_filter[2]})
|
|
@@ -62,9 +48,8 @@ class GlobalMiddleware(ABCMiddleware):
|
|
|
62
48
|
yield
|
|
63
49
|
self.filters.difference_update(filters)
|
|
64
50
|
|
|
65
|
-
if source_filter is not None
|
|
66
|
-
|
|
67
|
-
identifiers.pop(source_filter[1], None)
|
|
51
|
+
if source_filter is not None and (identifiers := self.source_filters.get(source_filter[0])):
|
|
52
|
+
identifiers.pop(source_filter[1], None)
|
|
68
53
|
|
|
69
54
|
|
|
70
55
|
__all__ = ("GlobalMiddleware",)
|
|
@@ -1,151 +1,130 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import typing
|
|
2
4
|
|
|
3
|
-
from fntypes.
|
|
5
|
+
from fntypes.result import Error, Ok
|
|
4
6
|
|
|
5
7
|
from telegrinder.api.api import API
|
|
6
|
-
from telegrinder.bot.cute_types.update import UpdateCute
|
|
7
8
|
from telegrinder.bot.dispatch.context import Context
|
|
8
9
|
from telegrinder.bot.dispatch.middleware.abc import ABCMiddleware, run_middleware
|
|
9
|
-
from telegrinder.bot.dispatch.return_manager.abc import ABCReturnManager
|
|
10
|
-
from telegrinder.model import Model
|
|
11
10
|
from telegrinder.modules import logger
|
|
12
|
-
from telegrinder.node.composer import CONTEXT_STORE_NODES_KEY,
|
|
13
|
-
from telegrinder.tools.
|
|
14
|
-
from telegrinder.tools.
|
|
11
|
+
from telegrinder.node.composer import CONTEXT_STORE_NODES_KEY, compose_nodes
|
|
12
|
+
from telegrinder.tools.aio import maybe_awaitable
|
|
13
|
+
from telegrinder.tools.fullname import fullname
|
|
14
|
+
from telegrinder.tools.magic.function import bundle
|
|
15
15
|
from telegrinder.types.objects import Update
|
|
16
16
|
|
|
17
17
|
if typing.TYPE_CHECKING:
|
|
18
|
-
from telegrinder.bot.dispatch.
|
|
18
|
+
from telegrinder.bot.dispatch.view.base import BaseView
|
|
19
19
|
from telegrinder.bot.rules.abc import ABCRule
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
async def process_inner
|
|
22
|
+
async def process_inner(
|
|
23
23
|
api: API,
|
|
24
|
-
event:
|
|
25
|
-
raw_event: Update,
|
|
24
|
+
event: Update,
|
|
26
25
|
ctx: Context,
|
|
27
|
-
|
|
28
|
-
handlers: list["ABCHandler[Event]"],
|
|
29
|
-
return_manager: ABCReturnManager[Event] | None = None,
|
|
26
|
+
view: BaseView,
|
|
30
27
|
) -> bool:
|
|
31
|
-
logger.debug("Processing {!r}...", event.__class__.__name__)
|
|
32
28
|
ctx[CONTEXT_STORE_NODES_KEY] = {} # For per-event shared nodes
|
|
33
29
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
30
|
+
for m in view.middlewares:
|
|
31
|
+
if (
|
|
32
|
+
type(m).pre is not ABCMiddleware.pre
|
|
33
|
+
and await run_middleware(m.pre, api, event, ctx, required_nodes=m.pre_required_nodes) is False
|
|
34
|
+
):
|
|
35
|
+
logger.info(
|
|
36
|
+
"Update(id={}, type={!r}) processed with view `{}`. Pre-middleware `{}` raised failure.",
|
|
37
|
+
event.update_id,
|
|
38
|
+
event.update_type,
|
|
39
|
+
type(view).__name__,
|
|
40
|
+
fullname(m),
|
|
41
|
+
)
|
|
39
42
|
return False
|
|
40
43
|
|
|
41
|
-
|
|
42
|
-
responses = []
|
|
44
|
+
found_handlers = []
|
|
45
|
+
responses = list[typing.Any]()
|
|
43
46
|
ctx_copy = ctx.copy()
|
|
44
47
|
|
|
45
|
-
for handler in handlers:
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
)
|
|
81
|
-
|
|
82
|
-
for session in ctx.get(CONTEXT_STORE_NODES_KEY, {}).values():
|
|
83
|
-
await session.close(scopes=(NodeScope.PER_EVENT,))
|
|
84
|
-
|
|
85
|
-
logger.debug(
|
|
86
|
-
"{} handlers, returns {!r}",
|
|
87
|
-
"No found" if not found else "Found",
|
|
88
|
-
found,
|
|
48
|
+
for handler in view.handlers:
|
|
49
|
+
match await handler.run(api, event, ctx):
|
|
50
|
+
case Ok(response):
|
|
51
|
+
found_handlers.append(handler)
|
|
52
|
+
responses.append(response)
|
|
53
|
+
|
|
54
|
+
if view.return_manager is not None:
|
|
55
|
+
await view.return_manager.run(response, api, event, ctx)
|
|
56
|
+
|
|
57
|
+
if handler.final is True:
|
|
58
|
+
break
|
|
59
|
+
case Error(error):
|
|
60
|
+
logger.debug(error)
|
|
61
|
+
|
|
62
|
+
ctx = ctx_copy
|
|
63
|
+
ctx.responses = responses
|
|
64
|
+
|
|
65
|
+
for m in view.middlewares:
|
|
66
|
+
if type(m).post is not ABCMiddleware.post:
|
|
67
|
+
await run_middleware(
|
|
68
|
+
m.post,
|
|
69
|
+
api,
|
|
70
|
+
event,
|
|
71
|
+
ctx,
|
|
72
|
+
required_nodes=m.post_required_nodes,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
logger.info(
|
|
76
|
+
"Update(id={}, type={!r}) processed with view `{}`. {}",
|
|
77
|
+
event.update_id,
|
|
78
|
+
event.update_type,
|
|
79
|
+
type(view).__name__,
|
|
80
|
+
"No found corresponded handlers."
|
|
81
|
+
if not found_handlers
|
|
82
|
+
else f"Handler{'s' if len(found_handlers) > 1 else ''}: {', '.join(f'`{x!r}`' for x in found_handlers)}",
|
|
89
83
|
)
|
|
90
|
-
return
|
|
84
|
+
return bool(found_handlers)
|
|
91
85
|
|
|
92
86
|
|
|
93
87
|
async def check_rule(
|
|
94
88
|
api: API,
|
|
95
|
-
rule:
|
|
89
|
+
rule: ABCRule,
|
|
96
90
|
update: Update,
|
|
97
91
|
ctx: Context,
|
|
98
92
|
) -> bool:
|
|
99
93
|
"""Checks requirements, adapts update.
|
|
100
94
|
Returns check result.
|
|
101
95
|
"""
|
|
102
|
-
update_cute = None if not isinstance(update, UpdateCute) else update
|
|
103
|
-
|
|
104
|
-
# Running adapter
|
|
105
|
-
match await run_adapter(rule.adapter, api, update, ctx):
|
|
106
|
-
case Some(val):
|
|
107
|
-
adapted_value = val
|
|
108
|
-
case Nothing():
|
|
109
|
-
return False
|
|
110
|
-
|
|
111
|
-
# Preparing update
|
|
112
|
-
if isinstance(adapted_value, UpdateCute):
|
|
113
|
-
update_cute = adapted_value
|
|
114
|
-
elif isinstance(adapted_val := ctx.get(rule.adapter.ADAPTED_VALUE_KEY or ""), UpdateCute):
|
|
115
|
-
update_cute = adapted_val
|
|
116
|
-
else:
|
|
117
|
-
update_cute = UpdateCute.from_update(update, bound_api=api)
|
|
118
|
-
|
|
119
96
|
# Running subrules to fetch requirements
|
|
120
97
|
ctx_copy = ctx.copy()
|
|
121
98
|
for requirement in rule.requires:
|
|
122
|
-
if not await check_rule(api, requirement,
|
|
99
|
+
if not await check_rule(api, requirement, update, ctx_copy):
|
|
123
100
|
return False
|
|
124
101
|
|
|
125
|
-
# Translating translatable rules
|
|
126
|
-
if I18nEnum.I18N in ctx:
|
|
127
|
-
rule = await rule.translate(ctx[I18nEnum.I18N])
|
|
128
|
-
|
|
129
102
|
ctx |= ctx_copy
|
|
103
|
+
node_col = None
|
|
104
|
+
data = {Update: update, API: api, Context: ctx}
|
|
130
105
|
|
|
131
106
|
# Composing required nodes
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
107
|
+
if rule.required_nodes:
|
|
108
|
+
match await compose_nodes(rule.required_nodes, ctx, data=data):
|
|
109
|
+
case Ok(value):
|
|
110
|
+
node_col = value
|
|
111
|
+
case Error(compose_error):
|
|
112
|
+
logger.debug(
|
|
113
|
+
"Cannot compose nodes for rule `{!r}`, error: {!r}",
|
|
114
|
+
rule,
|
|
115
|
+
compose_error.message,
|
|
116
|
+
)
|
|
117
|
+
return False
|
|
140
118
|
|
|
141
119
|
# Running check
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
120
|
+
try:
|
|
121
|
+
bundle_check = bundle(rule.check, data, typebundle=True)
|
|
122
|
+
bundle_check &= bundle(rule.check, ctx | ({} if node_col is None else node_col.values))
|
|
123
|
+
return await maybe_awaitable(bundle_check())
|
|
124
|
+
finally:
|
|
125
|
+
# Closing node sessions if there are any
|
|
126
|
+
if node_col is not None:
|
|
127
|
+
await node_col.close_all()
|
|
149
128
|
|
|
150
129
|
|
|
151
130
|
__all__ = ("check_rule", "process_inner")
|
|
File without changes
|