telegrinder 0.1.dev168__py3-none-any.whl → 0.1.dev170__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 +9 -3
- telegrinder/bot/__init__.py +7 -5
- telegrinder/bot/cute_types/base.py +12 -14
- telegrinder/bot/cute_types/callback_query.py +54 -43
- telegrinder/bot/cute_types/chat_join_request.py +8 -7
- telegrinder/bot/cute_types/chat_member_updated.py +23 -17
- telegrinder/bot/cute_types/inline_query.py +1 -1
- telegrinder/bot/cute_types/message.py +331 -183
- telegrinder/bot/cute_types/update.py +4 -8
- telegrinder/bot/cute_types/utils.py +1 -5
- telegrinder/bot/dispatch/__init__.py +2 -3
- telegrinder/bot/dispatch/abc.py +4 -0
- telegrinder/bot/dispatch/context.py +9 -4
- telegrinder/bot/dispatch/dispatch.py +35 -33
- telegrinder/bot/dispatch/handler/func.py +34 -13
- telegrinder/bot/dispatch/handler/message_reply.py +6 -3
- telegrinder/bot/dispatch/middleware/abc.py +4 -4
- telegrinder/bot/dispatch/process.py +40 -13
- telegrinder/bot/dispatch/return_manager/abc.py +12 -12
- telegrinder/bot/dispatch/return_manager/callback_query.py +1 -3
- telegrinder/bot/dispatch/return_manager/inline_query.py +1 -3
- telegrinder/bot/dispatch/view/abc.py +37 -45
- telegrinder/bot/dispatch/view/box.py +66 -50
- telegrinder/bot/dispatch/view/message.py +1 -5
- telegrinder/bot/dispatch/view/raw.py +6 -6
- telegrinder/bot/dispatch/waiter_machine/__init__.py +2 -1
- telegrinder/bot/dispatch/waiter_machine/machine.py +77 -35
- telegrinder/bot/dispatch/waiter_machine/middleware.py +31 -7
- telegrinder/bot/dispatch/waiter_machine/short_state.py +17 -8
- telegrinder/bot/polling/polling.py +4 -4
- telegrinder/bot/rules/__init__.py +9 -6
- telegrinder/bot/rules/abc.py +99 -22
- telegrinder/bot/rules/adapter/__init__.py +4 -1
- telegrinder/bot/rules/adapter/abc.py +11 -6
- telegrinder/bot/rules/adapter/errors.py +1 -2
- telegrinder/bot/rules/adapter/event.py +14 -9
- telegrinder/bot/rules/adapter/node.py +42 -0
- telegrinder/bot/rules/callback_data.py +4 -4
- telegrinder/bot/rules/chat_join.py +3 -2
- telegrinder/bot/rules/command.py +26 -14
- telegrinder/bot/rules/enum_text.py +5 -5
- telegrinder/bot/rules/func.py +6 -6
- telegrinder/bot/rules/fuzzy.py +5 -7
- telegrinder/bot/rules/inline.py +4 -5
- telegrinder/bot/rules/integer.py +10 -8
- telegrinder/bot/rules/is_from.py +63 -91
- telegrinder/bot/rules/markup.py +5 -5
- telegrinder/bot/rules/mention.py +4 -4
- telegrinder/bot/rules/message.py +1 -1
- telegrinder/bot/rules/node.py +27 -0
- telegrinder/bot/rules/regex.py +5 -5
- telegrinder/bot/rules/rule_enum.py +4 -4
- telegrinder/bot/rules/start.py +5 -5
- telegrinder/bot/rules/text.py +9 -13
- telegrinder/bot/rules/update.py +4 -4
- telegrinder/bot/scenario/__init__.py +3 -3
- telegrinder/bot/scenario/choice.py +2 -3
- telegrinder/model.py +51 -16
- telegrinder/modules.py +14 -6
- telegrinder/msgspec_utils.py +67 -23
- telegrinder/node/__init__.py +26 -8
- telegrinder/node/attachment.py +13 -9
- telegrinder/node/base.py +27 -14
- telegrinder/node/callback_query.py +18 -0
- telegrinder/node/command.py +29 -0
- telegrinder/node/composer.py +119 -30
- telegrinder/node/me.py +14 -0
- telegrinder/node/message.py +2 -4
- telegrinder/node/polymorphic.py +44 -0
- telegrinder/node/rule.py +26 -22
- telegrinder/node/scope.py +36 -0
- telegrinder/node/source.py +37 -10
- telegrinder/node/text.py +11 -5
- telegrinder/node/tools/__init__.py +2 -2
- telegrinder/node/tools/generator.py +6 -6
- telegrinder/tools/__init__.py +8 -13
- telegrinder/tools/buttons.py +23 -17
- telegrinder/tools/error_handler/error_handler.py +11 -14
- telegrinder/tools/formatting/__init__.py +0 -6
- telegrinder/tools/formatting/html.py +10 -12
- telegrinder/tools/formatting/links.py +0 -5
- telegrinder/tools/formatting/spec_html_formats.py +0 -11
- telegrinder/tools/global_context/abc.py +1 -3
- telegrinder/tools/global_context/global_context.py +6 -16
- telegrinder/tools/i18n/simple.py +1 -3
- telegrinder/tools/kb_set/yaml.py +1 -2
- telegrinder/tools/keyboard.py +7 -8
- telegrinder/tools/limited_dict.py +1 -1
- telegrinder/tools/loop_wrapper/loop_wrapper.py +6 -5
- telegrinder/tools/magic.py +27 -5
- telegrinder/types/__init__.py +20 -0
- telegrinder/types/enums.py +37 -31
- telegrinder/types/methods.py +608 -327
- telegrinder/types/objects.py +1139 -716
- {telegrinder-0.1.dev168.dist-info → telegrinder-0.1.dev170.dist-info}/LICENSE +1 -1
- {telegrinder-0.1.dev168.dist-info → telegrinder-0.1.dev170.dist-info}/METADATA +6 -5
- telegrinder-0.1.dev170.dist-info/RECORD +143 -0
- telegrinder/bot/dispatch/composition.py +0 -88
- telegrinder-0.1.dev168.dist-info/RECORD +0 -137
- {telegrinder-0.1.dev168.dist-info → telegrinder-0.1.dev170.dist-info}/WHEEL +0 -0
|
@@ -15,16 +15,12 @@ class UpdateCute(BaseCute[Update], Update, kw_only=True):
|
|
|
15
15
|
api: ABCAPI
|
|
16
16
|
|
|
17
17
|
@property
|
|
18
|
-
def incoming_update(self) ->
|
|
19
|
-
return getattr(
|
|
20
|
-
self,
|
|
21
|
-
self.update_type.expect("Update object has no incoming update.").value,
|
|
22
|
-
)
|
|
18
|
+
def incoming_update(self) -> Model:
|
|
19
|
+
return getattr(self, self.update_type.value).unwrap()
|
|
23
20
|
|
|
24
21
|
def get_event(self, event_model: type[ModelT]) -> Option[ModelT]:
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
return Some(event)
|
|
22
|
+
if isinstance(self.incoming_update, event_model):
|
|
23
|
+
return Some(self.incoming_update)
|
|
28
24
|
return Nothing()
|
|
29
25
|
|
|
30
26
|
|
|
@@ -78,11 +78,7 @@ def compose_reactions(
|
|
|
78
78
|
(
|
|
79
79
|
ReactionTypeEmoji("emoji", emoji)
|
|
80
80
|
if isinstance(emoji, ReactionEmoji)
|
|
81
|
-
else (
|
|
82
|
-
ReactionTypeEmoji("emoji", ReactionEmoji(emoji))
|
|
83
|
-
if isinstance(emoji, str)
|
|
84
|
-
else emoji
|
|
85
|
-
)
|
|
81
|
+
else (ReactionTypeEmoji("emoji", ReactionEmoji(emoji)) if isinstance(emoji, str) else emoji)
|
|
86
82
|
)
|
|
87
83
|
for emoji in ([reactions] if isinstance(reactions, str) else reactions)
|
|
88
84
|
]
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
from .abc import ABCDispatch
|
|
2
|
-
from .composition import CompositionDispatch
|
|
3
2
|
from .context import Context
|
|
4
3
|
from .dispatch import ABCRule, Dispatch, TelegrinderCtx
|
|
5
4
|
from .handler import ABCHandler, FuncHandler, MessageReplyHandler
|
|
@@ -27,7 +26,7 @@ from .view import (
|
|
|
27
26
|
RawEventView,
|
|
28
27
|
ViewBox,
|
|
29
28
|
)
|
|
30
|
-
from .waiter_machine import ShortState, WaiterMachine
|
|
29
|
+
from .waiter_machine import ShortState, WaiterMachine, clear_wm_storage_worker
|
|
31
30
|
|
|
32
31
|
__all__ = (
|
|
33
32
|
"ABCDispatch",
|
|
@@ -44,7 +43,6 @@ __all__ = (
|
|
|
44
43
|
"CallbackQueryView",
|
|
45
44
|
"ChatJoinRequestView",
|
|
46
45
|
"ChatMemberView",
|
|
47
|
-
"CompositionDispatch",
|
|
48
46
|
"Context",
|
|
49
47
|
"Dispatch",
|
|
50
48
|
"FuncHandler",
|
|
@@ -62,4 +60,5 @@ __all__ = (
|
|
|
62
60
|
"check_rule",
|
|
63
61
|
"process_inner",
|
|
64
62
|
"register_manager",
|
|
63
|
+
"clear_wm_storage_worker",
|
|
65
64
|
)
|
telegrinder/bot/dispatch/abc.py
CHANGED
|
@@ -15,7 +15,7 @@ class Context(dict[str, AnyValue]):
|
|
|
15
15
|
"""Context class for rules and middlewares.
|
|
16
16
|
```
|
|
17
17
|
class MyRule(ABCRule[T]):
|
|
18
|
-
adapter
|
|
18
|
+
adapter = RawUpdateAdapter()
|
|
19
19
|
|
|
20
20
|
async def check(self, event: T, ctx: Context) -> bool:
|
|
21
21
|
ctx.set("value", (await event.ctx_api.get_me()).unwrap())
|
|
@@ -28,17 +28,17 @@ class Context(dict[str, AnyValue]):
|
|
|
28
28
|
def __init__(self, **kwargs: AnyValue) -> None:
|
|
29
29
|
cls_vars = vars(self.__class__)
|
|
30
30
|
defaults = {}
|
|
31
|
+
|
|
31
32
|
for k in self.__class__.__annotations__:
|
|
32
33
|
if k in cls_vars:
|
|
33
34
|
defaults[k] = cls_vars[k]
|
|
34
35
|
delattr(self.__class__, k)
|
|
36
|
+
|
|
35
37
|
dict.__init__(self, **defaults | kwargs)
|
|
36
38
|
|
|
37
39
|
@recursive_repr()
|
|
38
40
|
def __repr__(self) -> str:
|
|
39
|
-
return "{}({})".format(
|
|
40
|
-
self.__class__.__name__, ", ".join(f"{k}={v!r}" for k, v in self.items())
|
|
41
|
-
)
|
|
41
|
+
return "{}({})".format(self.__class__.__name__, ", ".join(f"{k}={v!r}" for k, v in self.items()))
|
|
42
42
|
|
|
43
43
|
def __setitem__(self, __key: Key, __value: AnyValue) -> None:
|
|
44
44
|
dict.__setitem__(self, self.key_to_str(__key), __value)
|
|
@@ -71,6 +71,11 @@ class Context(dict[str, AnyValue]):
|
|
|
71
71
|
def get(self, key: Key, default: T | None = None) -> T | AnyValue:
|
|
72
72
|
return dict.get(self, key, default)
|
|
73
73
|
|
|
74
|
+
def get_or_set(self, key: Key, default: T) -> T:
|
|
75
|
+
if key not in self:
|
|
76
|
+
self.set(key, default)
|
|
77
|
+
return self.get(key)
|
|
78
|
+
|
|
74
79
|
def delete(self, key: Key) -> None:
|
|
75
80
|
del self[key]
|
|
76
81
|
|
|
@@ -6,6 +6,7 @@ from vbml.patcher import Patcher
|
|
|
6
6
|
|
|
7
7
|
from telegrinder.api.abc import ABCAPI
|
|
8
8
|
from telegrinder.bot.cute_types.base import BaseCute
|
|
9
|
+
from telegrinder.bot.cute_types.update import UpdateCute
|
|
9
10
|
from telegrinder.bot.dispatch.context import Context
|
|
10
11
|
from telegrinder.bot.rules import ABCRule
|
|
11
12
|
from telegrinder.modules import logger
|
|
@@ -17,21 +18,19 @@ from .abc import ABCDispatch
|
|
|
17
18
|
from .handler import ABCHandler, FuncHandler
|
|
18
19
|
from .handler.func import ErrorHandlerT
|
|
19
20
|
from .view.box import (
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
CallbackQueryView,
|
|
22
|
+
ChatJoinRequestView,
|
|
23
|
+
ChatMemberView,
|
|
24
|
+
InlineQueryView,
|
|
25
|
+
MessageView,
|
|
26
|
+
RawEventView,
|
|
26
27
|
ViewBox,
|
|
27
28
|
)
|
|
28
29
|
|
|
29
30
|
T = typing.TypeVar("T")
|
|
30
31
|
R = typing.TypeVar("R")
|
|
31
32
|
P = typing.ParamSpec("P")
|
|
32
|
-
Handler = typing.Callable[
|
|
33
|
-
typing.Concatenate[T, ...], typing.Coroutine[typing.Any, typing.Any, typing.Any]
|
|
34
|
-
]
|
|
33
|
+
Handler = typing.Callable[typing.Concatenate[T, ...], typing.Coroutine[typing.Any, typing.Any, typing.Any]]
|
|
35
34
|
Event = typing.TypeVar("Event", bound=BaseCute)
|
|
36
35
|
|
|
37
36
|
DEFAULT_DATACLASS: typing.Final[type[Update]] = Update
|
|
@@ -41,12 +40,12 @@ DEFAULT_DATACLASS: typing.Final[type[Update]] = Update
|
|
|
41
40
|
class Dispatch(
|
|
42
41
|
ABCDispatch,
|
|
43
42
|
ViewBox[
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
43
|
+
CallbackQueryView,
|
|
44
|
+
ChatJoinRequestView,
|
|
45
|
+
ChatMemberView,
|
|
46
|
+
InlineQueryView,
|
|
47
|
+
MessageView,
|
|
48
|
+
RawEventView,
|
|
50
49
|
],
|
|
51
50
|
):
|
|
52
51
|
global_context: TelegrinderCtx = dataclasses.field(
|
|
@@ -63,55 +62,56 @@ class Dispatch(
|
|
|
63
62
|
|
|
64
63
|
@property
|
|
65
64
|
def patcher(self) -> Patcher:
|
|
66
|
-
"""Alias `patcher` to get `vbml.Patcher` from the global context"""
|
|
65
|
+
"""Alias `patcher` to get `vbml.Patcher` from the global context."""
|
|
66
|
+
|
|
67
67
|
return self.global_context.vbml_patcher
|
|
68
68
|
|
|
69
69
|
@typing.overload
|
|
70
70
|
def handle(
|
|
71
71
|
self,
|
|
72
|
-
*rules: ABCRule
|
|
73
|
-
) -> typing.Callable[[Handler[T]], FuncHandler[
|
|
72
|
+
*rules: ABCRule,
|
|
73
|
+
) -> typing.Callable[[Handler[T]], FuncHandler[UpdateCute, Handler[T], ErrorHandler]]: ...
|
|
74
74
|
|
|
75
75
|
@typing.overload
|
|
76
76
|
def handle(
|
|
77
77
|
self,
|
|
78
|
-
*rules: ABCRule
|
|
78
|
+
*rules: ABCRule,
|
|
79
79
|
is_blocking: bool = True,
|
|
80
|
-
) -> typing.Callable[[Handler[T]], FuncHandler[
|
|
80
|
+
) -> typing.Callable[[Handler[T]], FuncHandler[UpdateCute, Handler[T], ErrorHandler]]: ...
|
|
81
81
|
|
|
82
82
|
@typing.overload
|
|
83
83
|
def handle(
|
|
84
84
|
self,
|
|
85
|
-
*rules: ABCRule
|
|
85
|
+
*rules: ABCRule,
|
|
86
86
|
dataclass: type[T],
|
|
87
87
|
is_blocking: bool = True,
|
|
88
|
-
) -> typing.Callable[[Handler[T]], FuncHandler[
|
|
88
|
+
) -> typing.Callable[[Handler[T]], FuncHandler[UpdateCute, Handler[T], ErrorHandler]]: ...
|
|
89
89
|
|
|
90
90
|
@typing.overload
|
|
91
91
|
def handle( # type: ignore
|
|
92
92
|
self,
|
|
93
|
-
*rules: ABCRule
|
|
93
|
+
*rules: ABCRule,
|
|
94
94
|
error_handler: ErrorHandlerT,
|
|
95
95
|
is_blocking: bool = True,
|
|
96
|
-
) -> typing.Callable[[Handler[T]], FuncHandler[
|
|
96
|
+
) -> typing.Callable[[Handler[T]], FuncHandler[UpdateCute, Handler[T], ErrorHandlerT]]: ...
|
|
97
97
|
|
|
98
98
|
@typing.overload
|
|
99
99
|
def handle(
|
|
100
100
|
self,
|
|
101
|
-
*rules: ABCRule
|
|
101
|
+
*rules: ABCRule,
|
|
102
102
|
dataclass: type[T],
|
|
103
103
|
error_handler: ErrorHandlerT,
|
|
104
104
|
is_blocking: bool = True,
|
|
105
|
-
) -> typing.Callable[[Handler[T]], FuncHandler[
|
|
105
|
+
) -> typing.Callable[[Handler[T]], FuncHandler[UpdateCute, Handler[T], ErrorHandlerT]]: ...
|
|
106
106
|
|
|
107
107
|
@typing.overload
|
|
108
108
|
def handle(
|
|
109
109
|
self,
|
|
110
|
-
*rules: ABCRule
|
|
110
|
+
*rules: ABCRule,
|
|
111
111
|
dataclass: type[T] = DEFAULT_DATACLASS,
|
|
112
112
|
error_handler: typing.Literal[None] = None,
|
|
113
113
|
is_blocking: bool = True,
|
|
114
|
-
) -> typing.Callable[[Handler[T]], FuncHandler[
|
|
114
|
+
) -> typing.Callable[[Handler[T]], FuncHandler[UpdateCute, Handler[T], ErrorHandler]]: ...
|
|
115
115
|
|
|
116
116
|
def handle( # type: ignore
|
|
117
117
|
self,
|
|
@@ -135,20 +135,20 @@ class Dispatch(
|
|
|
135
135
|
|
|
136
136
|
async def feed(self, event: Update, api: ABCAPI) -> bool:
|
|
137
137
|
logger.debug("Processing update (update_id={})", event.update_id)
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
138
|
+
await self.raw_event.process(event, api)
|
|
139
|
+
|
|
141
140
|
for view in self.get_views().values():
|
|
142
141
|
if await view.check(event):
|
|
143
142
|
logger.debug(
|
|
144
|
-
"Update (update_id={}) matched view {!r}",
|
|
143
|
+
"Update (update_id={}) matched view {!r}.",
|
|
145
144
|
event.update_id,
|
|
146
145
|
view.__class__.__name__,
|
|
147
146
|
)
|
|
148
|
-
|
|
147
|
+
await view.process(event, api)
|
|
149
148
|
return True
|
|
150
149
|
|
|
151
150
|
ctx = Context()
|
|
151
|
+
loop = asyncio.get_running_loop()
|
|
152
152
|
found = False
|
|
153
153
|
for handler in self.default_handlers:
|
|
154
154
|
if await handler.check(api, event, ctx):
|
|
@@ -165,5 +165,7 @@ class Dispatch(
|
|
|
165
165
|
view.load(view_external[name])
|
|
166
166
|
setattr(external, name, view)
|
|
167
167
|
|
|
168
|
+
__call__ = handle
|
|
169
|
+
|
|
168
170
|
|
|
169
171
|
__all__ = ("Dispatch",)
|
|
@@ -3,11 +3,13 @@ import dataclasses
|
|
|
3
3
|
import typing_extensions as typing
|
|
4
4
|
|
|
5
5
|
from telegrinder.api.abc import ABCAPI
|
|
6
|
-
from telegrinder.bot.cute_types import BaseCute
|
|
6
|
+
from telegrinder.bot.cute_types import BaseCute, UpdateCute
|
|
7
7
|
from telegrinder.bot.dispatch.context import Context
|
|
8
8
|
from telegrinder.bot.dispatch.process import check_rule
|
|
9
9
|
from telegrinder.modules import logger
|
|
10
|
+
from telegrinder.node import Node, compose_nodes, is_node
|
|
10
11
|
from telegrinder.tools.error_handler import ABCErrorHandler, ErrorHandler
|
|
12
|
+
from telegrinder.tools.magic import get_annotations
|
|
11
13
|
from telegrinder.types import Update
|
|
12
14
|
from telegrinder.types.enums import UpdateType
|
|
13
15
|
|
|
@@ -20,22 +22,22 @@ F = typing.TypeVar(
|
|
|
20
22
|
"F",
|
|
21
23
|
bound=typing.Callable[typing.Concatenate[typing.Any, ...], typing.Awaitable[typing.Any]],
|
|
22
24
|
)
|
|
23
|
-
|
|
25
|
+
Event = typing.TypeVar("Event", bound=BaseCute)
|
|
24
26
|
ErrorHandlerT = typing.TypeVar("ErrorHandlerT", bound=ABCErrorHandler, default=ErrorHandler)
|
|
25
27
|
|
|
26
28
|
|
|
27
29
|
@dataclasses.dataclass(repr=False)
|
|
28
|
-
class FuncHandler(ABCHandler[
|
|
30
|
+
class FuncHandler(ABCHandler[Event], typing.Generic[Event, F, ErrorHandlerT]):
|
|
29
31
|
func: F
|
|
30
|
-
rules: list["ABCRule
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
dataclass: type[typing.Any] | None = dataclasses.field(default=dict)
|
|
32
|
+
rules: list["ABCRule"]
|
|
33
|
+
is_blocking: bool = dataclasses.field(default=True, kw_only=True)
|
|
34
|
+
dataclass: type[typing.Any] | None = dataclasses.field(default=dict, kw_only=True)
|
|
34
35
|
error_handler: ErrorHandlerT = dataclasses.field(
|
|
35
36
|
default_factory=lambda: typing.cast(ErrorHandlerT, ErrorHandler()),
|
|
37
|
+
kw_only=True,
|
|
36
38
|
)
|
|
37
|
-
preset_context: Context = dataclasses.field(default_factory=lambda: Context())
|
|
38
|
-
update_type: UpdateType | None = dataclasses.field(default=None)
|
|
39
|
+
preset_context: Context = dataclasses.field(default_factory=lambda: Context(), kw_only=True)
|
|
40
|
+
update_type: UpdateType | None = dataclasses.field(default=None, kw_only=True)
|
|
39
41
|
|
|
40
42
|
def __repr__(self) -> str:
|
|
41
43
|
return "<{}: {}={!r} with rules={!r}, dataclass={!r}, error_handler={!r}>".format(
|
|
@@ -46,24 +48,39 @@ class FuncHandler(ABCHandler[EventT], typing.Generic[EventT, F, ErrorHandlerT]):
|
|
|
46
48
|
self.dataclass,
|
|
47
49
|
self.error_handler,
|
|
48
50
|
)
|
|
49
|
-
|
|
51
|
+
|
|
52
|
+
def get_required_nodes(self) -> dict[str, type[Node]]:
|
|
53
|
+
return {k: v for k, v in get_annotations(self.func).items() if is_node(v)}
|
|
54
|
+
|
|
50
55
|
async def check(self, api: ABCAPI, event: Update, ctx: Context | None = None) -> bool:
|
|
51
|
-
if self.update_type is not None and self.update_type != event.update_type
|
|
56
|
+
if self.update_type is not None and self.update_type != event.update_type:
|
|
52
57
|
return False
|
|
58
|
+
|
|
53
59
|
ctx = ctx or Context()
|
|
54
60
|
temp_ctx = ctx.copy()
|
|
55
61
|
temp_ctx |= self.preset_context
|
|
56
62
|
|
|
63
|
+
nodes = self.get_required_nodes()
|
|
64
|
+
node_col = None
|
|
65
|
+
|
|
66
|
+
if nodes:
|
|
67
|
+
node_col = await compose_nodes(nodes, UpdateCute.from_update(event, api), ctx)
|
|
68
|
+
if node_col is None:
|
|
69
|
+
return False
|
|
70
|
+
temp_ctx |= node_col.values()
|
|
71
|
+
|
|
57
72
|
for rule in self.rules:
|
|
58
73
|
if not await check_rule(api, rule, event, temp_ctx):
|
|
59
74
|
logger.debug("Rule {!r} failed!", rule)
|
|
60
75
|
return False
|
|
61
76
|
|
|
77
|
+
temp_ctx["node_col"] = node_col
|
|
62
78
|
ctx |= temp_ctx
|
|
63
79
|
return True
|
|
64
80
|
|
|
65
|
-
async def run(self, event:
|
|
81
|
+
async def run(self, event: Event, ctx: Context) -> typing.Any:
|
|
66
82
|
api = event.api
|
|
83
|
+
|
|
67
84
|
if self.dataclass is not None:
|
|
68
85
|
if self.update_type is not None:
|
|
69
86
|
update = event.to_dict()[self.update_type.value].unwrap()
|
|
@@ -74,7 +91,11 @@ class FuncHandler(ABCHandler[EventT], typing.Generic[EventT, F, ErrorHandlerT]):
|
|
|
74
91
|
)
|
|
75
92
|
else:
|
|
76
93
|
event = self.dataclass(**event.to_dict())
|
|
77
|
-
|
|
94
|
+
|
|
95
|
+
result = (await self.error_handler.run(self.func, event, api, ctx)).unwrap()
|
|
96
|
+
if node_col := ctx.node_col:
|
|
97
|
+
await node_col.close_all()
|
|
98
|
+
return result
|
|
78
99
|
|
|
79
100
|
|
|
80
101
|
__all__ = ("FuncHandler",)
|
|
@@ -6,7 +6,6 @@ from telegrinder.bot.dispatch.context import Context
|
|
|
6
6
|
from telegrinder.bot.dispatch.process import check_rule
|
|
7
7
|
from telegrinder.bot.rules.abc import ABCRule
|
|
8
8
|
from telegrinder.modules import logger
|
|
9
|
-
from telegrinder.msgspec_utils import Nothing
|
|
10
9
|
from telegrinder.types.objects import ReplyParameters, Update
|
|
11
10
|
|
|
12
11
|
from .abc import ABCHandler
|
|
@@ -16,15 +15,18 @@ class MessageReplyHandler(ABCHandler[MessageCute]):
|
|
|
16
15
|
def __init__(
|
|
17
16
|
self,
|
|
18
17
|
text: str,
|
|
19
|
-
*rules: ABCRule
|
|
18
|
+
*rules: ABCRule,
|
|
20
19
|
is_blocking: bool = True,
|
|
21
20
|
as_reply: bool = False,
|
|
21
|
+
preset_context: Context | None = None,
|
|
22
|
+
**default_params: typing.Any,
|
|
22
23
|
) -> None:
|
|
23
24
|
self.text = text
|
|
24
25
|
self.rules = list(rules)
|
|
25
26
|
self.as_reply = as_reply
|
|
26
27
|
self.is_blocking = is_blocking
|
|
27
|
-
self.
|
|
28
|
+
self.default_params = default_params
|
|
29
|
+
self.preset_context = preset_context or Context()
|
|
28
30
|
|
|
29
31
|
def __repr__(self) -> str:
|
|
30
32
|
return "<{}: with rules={!r}, {}: {!r}>".format(
|
|
@@ -51,6 +53,7 @@ class MessageReplyHandler(ABCHandler[MessageCute]):
|
|
|
51
53
|
await event.answer(
|
|
52
54
|
text=self.text,
|
|
53
55
|
reply_parameters=ReplyParameters(event.message_id) if self.as_reply else None,
|
|
56
|
+
**self.default_params,
|
|
54
57
|
)
|
|
55
58
|
|
|
56
59
|
|
|
@@ -4,13 +4,13 @@ from abc import ABC
|
|
|
4
4
|
from telegrinder.bot.cute_types.base import BaseCute
|
|
5
5
|
from telegrinder.bot.dispatch.context import Context
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Event = typing.TypeVar("Event", bound=BaseCute)
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
class ABCMiddleware(ABC, typing.Generic[
|
|
11
|
-
async def pre(self, event:
|
|
10
|
+
class ABCMiddleware(ABC, typing.Generic[Event]):
|
|
11
|
+
async def pre(self, event: Event, ctx: Context) -> bool: ...
|
|
12
12
|
|
|
13
|
-
async def post(self, event:
|
|
13
|
+
async def post(self, event: Event, responses: list[typing.Any], ctx: Context) -> None: ...
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
__all__ = ("ABCMiddleware",)
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import typing
|
|
2
2
|
|
|
3
|
-
from fntypes.result import Error
|
|
3
|
+
from fntypes.result import Error, Ok
|
|
4
4
|
|
|
5
5
|
from telegrinder.api.abc import ABCAPI
|
|
6
6
|
from telegrinder.bot.cute_types import BaseCute
|
|
7
|
+
from telegrinder.bot.cute_types.update import UpdateCute
|
|
7
8
|
from telegrinder.bot.dispatch.context import Context
|
|
8
9
|
from telegrinder.modules import logger
|
|
10
|
+
from telegrinder.node.composer import CONTEXT_STORE_NODES_KEY, NodeScope, compose_nodes
|
|
9
11
|
from telegrinder.tools.i18n.base import I18nEnum
|
|
10
12
|
from telegrinder.types import Update
|
|
11
13
|
|
|
@@ -16,19 +18,20 @@ if typing.TYPE_CHECKING:
|
|
|
16
18
|
from telegrinder.bot.dispatch.handler.abc import ABCHandler
|
|
17
19
|
from telegrinder.bot.rules.abc import ABCRule
|
|
18
20
|
|
|
19
|
-
|
|
21
|
+
Event = typing.TypeVar("Event", bound=BaseCute)
|
|
20
22
|
_: typing.TypeAlias = typing.Any
|
|
21
23
|
|
|
22
24
|
|
|
23
25
|
async def process_inner(
|
|
24
|
-
event:
|
|
26
|
+
event: Event,
|
|
25
27
|
raw_event: Update,
|
|
26
|
-
middlewares: list[ABCMiddleware[
|
|
27
|
-
handlers: list["ABCHandler[
|
|
28
|
-
return_manager: ABCReturnManager[
|
|
28
|
+
middlewares: list[ABCMiddleware[Event]],
|
|
29
|
+
handlers: list["ABCHandler[Event]"],
|
|
30
|
+
return_manager: ABCReturnManager[Event] | None = None,
|
|
29
31
|
) -> bool:
|
|
30
32
|
logger.debug("Processing {!r}...", event.__class__.__name__)
|
|
31
33
|
ctx = Context(raw_update=raw_event)
|
|
34
|
+
ctx[CONTEXT_STORE_NODES_KEY] = {} # for per-event shared nodes
|
|
32
35
|
|
|
33
36
|
for middleware in middlewares:
|
|
34
37
|
if await middleware.pre(event, ctx) is False:
|
|
@@ -42,45 +45,69 @@ async def process_inner(
|
|
|
42
45
|
if await handler.check(event.api, raw_event, ctx):
|
|
43
46
|
found = True
|
|
44
47
|
response = await handler.run(event, ctx)
|
|
48
|
+
logger.debug("Handler {!r} returned: {!r}", handler, response)
|
|
45
49
|
responses.append(response)
|
|
46
50
|
if return_manager is not None:
|
|
47
51
|
await return_manager.run(response, event, ctx)
|
|
48
52
|
if handler.is_blocking:
|
|
49
53
|
break
|
|
50
|
-
|
|
54
|
+
|
|
55
|
+
ctx = ctx_copy
|
|
51
56
|
|
|
52
57
|
for middleware in middlewares:
|
|
53
58
|
await middleware.post(event, responses, ctx)
|
|
54
59
|
|
|
60
|
+
for session in ctx.get(CONTEXT_STORE_NODES_KEY, {}).values():
|
|
61
|
+
await session.close(scopes=(NodeScope.PER_EVENT,))
|
|
62
|
+
|
|
55
63
|
return found
|
|
56
64
|
|
|
57
65
|
|
|
58
66
|
async def check_rule(
|
|
59
67
|
api: ABCAPI,
|
|
60
|
-
rule: "ABCRule
|
|
68
|
+
rule: "ABCRule",
|
|
61
69
|
update: Update,
|
|
62
70
|
ctx: Context,
|
|
63
71
|
) -> bool:
|
|
64
72
|
"""Checks requirements, adapts update.
|
|
65
73
|
Returns check result."""
|
|
66
74
|
|
|
67
|
-
|
|
68
|
-
match
|
|
75
|
+
# Running adapter
|
|
76
|
+
match await rule.adapter.adapt(api, update):
|
|
77
|
+
case Ok(value):
|
|
78
|
+
adapted_value = value
|
|
69
79
|
case Error(err):
|
|
70
80
|
logger.debug("Adapter failed with error message: {!r}", str(err))
|
|
71
81
|
return False
|
|
72
82
|
|
|
83
|
+
# Running subrules to fetch requirements
|
|
73
84
|
ctx_copy = ctx.copy()
|
|
74
85
|
for requirement in rule.requires:
|
|
75
86
|
if not await check_rule(api, requirement, update, ctx_copy):
|
|
76
87
|
return False
|
|
77
88
|
|
|
78
|
-
|
|
79
|
-
|
|
89
|
+
# Translating translatable rules
|
|
80
90
|
if I18nEnum.I18N in ctx:
|
|
81
91
|
rule = await rule.translate(ctx.get(I18nEnum.I18N))
|
|
82
92
|
|
|
83
|
-
|
|
93
|
+
ctx |= ctx_copy
|
|
94
|
+
|
|
95
|
+
# Composing required nodes
|
|
96
|
+
nodes = rule.get_required_nodes()
|
|
97
|
+
node_col = None
|
|
98
|
+
if nodes:
|
|
99
|
+
node_col = await compose_nodes(nodes, UpdateCute.from_update(update, api), ctx)
|
|
100
|
+
if node_col is None:
|
|
101
|
+
return False
|
|
102
|
+
|
|
103
|
+
# Running check
|
|
104
|
+
result = await rule.bounding_check(adapted_value, ctx, node_col)
|
|
105
|
+
|
|
106
|
+
# Closing node sessions if there are any
|
|
107
|
+
if node_col is not None:
|
|
108
|
+
await node_col.close_all()
|
|
109
|
+
|
|
110
|
+
return result
|
|
84
111
|
|
|
85
112
|
|
|
86
113
|
__all__ = ("check_rule", "process_inner")
|
|
@@ -8,7 +8,7 @@ from telegrinder.bot.dispatch.context import Context
|
|
|
8
8
|
from telegrinder.modules import logger
|
|
9
9
|
|
|
10
10
|
T = typing.TypeVar("T")
|
|
11
|
-
|
|
11
|
+
Event = typing.TypeVar("Event", bound=BaseCute, contravariant=True)
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
def get_union_types(t: types.UnionType) -> tuple[type, ...] | None:
|
|
@@ -36,13 +36,13 @@ class Manager:
|
|
|
36
36
|
logger.exception(ex)
|
|
37
37
|
|
|
38
38
|
|
|
39
|
-
class ABCReturnManager(ABC, typing.Generic[
|
|
39
|
+
class ABCReturnManager(ABC, typing.Generic[Event]):
|
|
40
40
|
@abstractmethod
|
|
41
|
-
async def run(self, response: typing.Any, event:
|
|
41
|
+
async def run(self, response: typing.Any, event: Event, ctx: Context) -> None:
|
|
42
42
|
pass
|
|
43
43
|
|
|
44
44
|
|
|
45
|
-
class BaseReturnManager(ABCReturnManager[
|
|
45
|
+
class BaseReturnManager(ABCReturnManager[Event]):
|
|
46
46
|
def __repr__(self) -> str:
|
|
47
47
|
return "<{}: {}>".format(
|
|
48
48
|
self.__class__.__name__,
|
|
@@ -59,29 +59,29 @@ class BaseReturnManager(ABCReturnManager[EventT]):
|
|
|
59
59
|
|
|
60
60
|
@register_manager(Context)
|
|
61
61
|
@staticmethod
|
|
62
|
-
async def ctx_manager(value: Context, event:
|
|
62
|
+
async def ctx_manager(value: Context, event: Event, ctx: Context) -> None:
|
|
63
63
|
"""Basic manager for returning context from handler."""
|
|
64
64
|
|
|
65
65
|
ctx.update(value)
|
|
66
66
|
|
|
67
|
-
async def run(self, response: typing.Any, event:
|
|
67
|
+
async def run(self, response: typing.Any, event: Event, ctx: Context) -> None:
|
|
68
|
+
logger.debug("Run return manager for response: {!r}", response)
|
|
68
69
|
for manager in self.managers:
|
|
69
70
|
if typing.Any in manager.types or any(type(response) is x for x in manager.types):
|
|
71
|
+
logger.debug("Run manager {!r}...", manager.callback.__name__)
|
|
70
72
|
await manager(response, event, ctx)
|
|
71
73
|
|
|
72
74
|
@typing.overload
|
|
73
75
|
def register_manager(
|
|
74
76
|
self, return_type: type[T]
|
|
75
|
-
) -> typing.Callable[
|
|
76
|
-
[typing.Callable[[T, EventT, Context], typing.Awaitable[typing.Any]]], Manager
|
|
77
|
-
]: ...
|
|
77
|
+
) -> typing.Callable[[typing.Callable[[T, Event, Context], typing.Awaitable[typing.Any]]], Manager]: ...
|
|
78
78
|
|
|
79
79
|
@typing.overload
|
|
80
80
|
def register_manager(
|
|
81
81
|
self,
|
|
82
82
|
return_type: tuple[type[T], ...],
|
|
83
83
|
) -> typing.Callable[
|
|
84
|
-
[typing.Callable[[tuple[T, ...],
|
|
84
|
+
[typing.Callable[[tuple[T, ...], Event, Context], typing.Awaitable[typing.Any]]],
|
|
85
85
|
Manager,
|
|
86
86
|
]: ...
|
|
87
87
|
|
|
@@ -89,10 +89,10 @@ class BaseReturnManager(ABCReturnManager[EventT]):
|
|
|
89
89
|
self,
|
|
90
90
|
return_type: type[T] | tuple[type[T], ...],
|
|
91
91
|
) -> typing.Callable[
|
|
92
|
-
[typing.Callable[[T | tuple[T, ...],
|
|
92
|
+
[typing.Callable[[T | tuple[T, ...], Event, Context], typing.Awaitable[typing.Any]]],
|
|
93
93
|
Manager,
|
|
94
94
|
]:
|
|
95
|
-
def wrapper(func: typing.Callable[[T,
|
|
95
|
+
def wrapper(func: typing.Callable[[T, Event, Context], typing.Awaitable]) -> Manager:
|
|
96
96
|
manager = Manager(get_union_types(return_type) or (return_type,), func) # type: ignore
|
|
97
97
|
setattr(self.__class__, func.__name__, manager)
|
|
98
98
|
return manager
|
|
@@ -14,9 +14,7 @@ class CallbackQueryReturnManager(BaseReturnManager[CallbackQueryCute]):
|
|
|
14
14
|
|
|
15
15
|
@register_manager(dict)
|
|
16
16
|
@staticmethod
|
|
17
|
-
async def dict_manager(
|
|
18
|
-
value: dict[str, typing.Any], event: CallbackQueryCute, ctx: Context
|
|
19
|
-
) -> None:
|
|
17
|
+
async def dict_manager(value: dict[str, typing.Any], event: CallbackQueryCute, ctx: Context) -> None:
|
|
20
18
|
await event.answer(**value)
|
|
21
19
|
|
|
22
20
|
|
|
@@ -9,9 +9,7 @@ from .abc import BaseReturnManager, register_manager
|
|
|
9
9
|
class InlineQueryReturnManager(BaseReturnManager[InlineQueryCute]):
|
|
10
10
|
@register_manager(dict)
|
|
11
11
|
@staticmethod
|
|
12
|
-
async def dict_manager(
|
|
13
|
-
value: dict[str, typing.Any], event: InlineQueryCute, ctx: Context
|
|
14
|
-
) -> None:
|
|
12
|
+
async def dict_manager(value: dict[str, typing.Any], event: InlineQueryCute, ctx: Context) -> None:
|
|
15
13
|
await event.answer(**value)
|
|
16
14
|
|
|
17
15
|
|