telegrinder 0.1.dev170__py3-none-any.whl → 0.1.dev171__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/api/abc.py +7 -1
- telegrinder/api/api.py +12 -3
- telegrinder/api/error.py +2 -1
- telegrinder/bot/bot.py +6 -1
- telegrinder/bot/cute_types/base.py +144 -17
- telegrinder/bot/cute_types/callback_query.py +6 -1
- telegrinder/bot/cute_types/chat_member_updated.py +1 -2
- telegrinder/bot/cute_types/message.py +23 -11
- telegrinder/bot/cute_types/update.py +48 -0
- telegrinder/bot/cute_types/utils.py +2 -465
- telegrinder/bot/dispatch/__init__.py +2 -3
- telegrinder/bot/dispatch/abc.py +6 -3
- telegrinder/bot/dispatch/context.py +6 -6
- telegrinder/bot/dispatch/dispatch.py +61 -23
- telegrinder/bot/dispatch/handler/abc.py +2 -2
- telegrinder/bot/dispatch/handler/func.py +36 -17
- telegrinder/bot/dispatch/handler/message_reply.py +2 -2
- telegrinder/bot/dispatch/middleware/abc.py +2 -2
- telegrinder/bot/dispatch/process.py +10 -10
- telegrinder/bot/dispatch/return_manager/abc.py +3 -3
- telegrinder/bot/dispatch/view/abc.py +12 -15
- telegrinder/bot/dispatch/view/box.py +73 -62
- telegrinder/bot/dispatch/view/message.py +11 -3
- telegrinder/bot/dispatch/view/raw.py +3 -0
- telegrinder/bot/dispatch/waiter_machine/machine.py +2 -2
- telegrinder/bot/dispatch/waiter_machine/middleware.py +1 -1
- telegrinder/bot/dispatch/waiter_machine/short_state.py +2 -1
- telegrinder/bot/polling/polling.py +3 -3
- telegrinder/bot/rules/abc.py +11 -7
- telegrinder/bot/rules/adapter/event.py +7 -4
- telegrinder/bot/rules/adapter/node.py +1 -1
- telegrinder/bot/rules/command.py +5 -7
- telegrinder/bot/rules/func.py +1 -1
- telegrinder/bot/rules/fuzzy.py +1 -1
- telegrinder/bot/rules/integer.py +1 -2
- telegrinder/bot/rules/markup.py +3 -3
- telegrinder/bot/rules/message_entities.py +1 -1
- telegrinder/bot/rules/node.py +2 -2
- telegrinder/bot/rules/regex.py +1 -1
- telegrinder/bot/rules/rule_enum.py +1 -1
- telegrinder/bot/scenario/checkbox.py +2 -2
- telegrinder/model.py +85 -46
- telegrinder/modules.py +3 -3
- telegrinder/msgspec_utils.py +33 -5
- telegrinder/node/__init__.py +20 -11
- telegrinder/node/attachment.py +19 -16
- telegrinder/node/base.py +120 -24
- telegrinder/node/callback_query.py +5 -9
- telegrinder/node/command.py +6 -2
- telegrinder/node/composer.py +82 -54
- telegrinder/node/container.py +4 -4
- telegrinder/node/event.py +59 -0
- telegrinder/node/me.py +3 -0
- telegrinder/node/message.py +6 -10
- telegrinder/node/polymorphic.py +11 -12
- telegrinder/node/rule.py +27 -5
- telegrinder/node/source.py +10 -11
- telegrinder/node/text.py +4 -4
- telegrinder/node/update.py +1 -2
- telegrinder/py.typed +0 -0
- telegrinder/tools/__init__.py +2 -2
- telegrinder/tools/buttons.py +5 -10
- telegrinder/tools/error_handler/error.py +2 -0
- telegrinder/tools/error_handler/error_handler.py +1 -1
- telegrinder/tools/formatting/spec_html_formats.py +10 -10
- telegrinder/tools/global_context/__init__.py +2 -2
- telegrinder/tools/global_context/global_context.py +2 -2
- telegrinder/tools/global_context/telegrinder_ctx.py +4 -4
- telegrinder/tools/keyboard.py +2 -2
- telegrinder/tools/loop_wrapper/loop_wrapper.py +39 -5
- telegrinder/tools/magic.py +48 -15
- telegrinder/types/enums.py +1 -0
- telegrinder/types/methods.py +14 -5
- telegrinder/types/objects.py +3 -0
- {telegrinder-0.1.dev170.dist-info → telegrinder-0.1.dev171.dist-info}/METADATA +2 -2
- telegrinder-0.1.dev171.dist-info/RECORD +145 -0
- telegrinder-0.1.dev170.dist-info/RECORD +0 -143
- {telegrinder-0.1.dev170.dist-info → telegrinder-0.1.dev171.dist-info}/LICENSE +0 -0
- {telegrinder-0.1.dev170.dist-info → telegrinder-0.1.dev171.dist-info}/WHEEL +0 -0
|
@@ -8,15 +8,15 @@ from telegrinder.api.abc import ABCAPI
|
|
|
8
8
|
from telegrinder.bot.cute_types.base import BaseCute
|
|
9
9
|
from telegrinder.bot.cute_types.update import UpdateCute
|
|
10
10
|
from telegrinder.bot.dispatch.context import Context
|
|
11
|
-
from telegrinder.bot.
|
|
11
|
+
from telegrinder.bot.dispatch.handler.abc import ABCHandler
|
|
12
|
+
from telegrinder.bot.dispatch.handler.func import ErrorHandlerT, FuncHandler
|
|
12
13
|
from telegrinder.modules import logger
|
|
13
14
|
from telegrinder.tools.error_handler.error_handler import ErrorHandler
|
|
14
|
-
from telegrinder.tools.global_context import
|
|
15
|
-
from telegrinder.types import
|
|
15
|
+
from telegrinder.tools.global_context import TelegrinderContext
|
|
16
|
+
from telegrinder.types.enums import UpdateType
|
|
17
|
+
from telegrinder.types.objects import Update
|
|
16
18
|
|
|
17
19
|
from .abc import ABCDispatch
|
|
18
|
-
from .handler import ABCHandler, FuncHandler
|
|
19
|
-
from .handler.func import ErrorHandlerT
|
|
20
20
|
from .view.box import (
|
|
21
21
|
CallbackQueryView,
|
|
22
22
|
ChatJoinRequestView,
|
|
@@ -27,9 +27,10 @@ from .view.box import (
|
|
|
27
27
|
ViewBox,
|
|
28
28
|
)
|
|
29
29
|
|
|
30
|
+
if typing.TYPE_CHECKING:
|
|
31
|
+
from telegrinder.bot.rules.abc import ABCRule
|
|
32
|
+
|
|
30
33
|
T = typing.TypeVar("T")
|
|
31
|
-
R = typing.TypeVar("R")
|
|
32
|
-
P = typing.ParamSpec("P")
|
|
33
34
|
Handler = typing.Callable[typing.Concatenate[T, ...], typing.Coroutine[typing.Any, typing.Any, typing.Any]]
|
|
34
35
|
Event = typing.TypeVar("Event", bound=BaseCute)
|
|
35
36
|
|
|
@@ -48,18 +49,22 @@ class Dispatch(
|
|
|
48
49
|
RawEventView,
|
|
49
50
|
],
|
|
50
51
|
):
|
|
51
|
-
global_context: TelegrinderCtx = dataclasses.field(
|
|
52
|
-
init=False,
|
|
53
|
-
default_factory=lambda: TelegrinderCtx(),
|
|
54
|
-
)
|
|
55
52
|
default_handlers: list[ABCHandler] = dataclasses.field(
|
|
56
53
|
init=False,
|
|
57
54
|
default_factory=lambda: [],
|
|
58
55
|
)
|
|
56
|
+
_global_context: TelegrinderContext = dataclasses.field(
|
|
57
|
+
init=False,
|
|
58
|
+
default_factory=lambda: TelegrinderContext(),
|
|
59
|
+
)
|
|
59
60
|
|
|
60
61
|
def __repr__(self) -> str:
|
|
61
62
|
return "Dispatch(%s)" % ", ".join(f"{k}={v!r}" for k, v in self.__dict__.items())
|
|
62
63
|
|
|
64
|
+
@property
|
|
65
|
+
def global_context(self) -> TelegrinderContext:
|
|
66
|
+
return self._global_context
|
|
67
|
+
|
|
63
68
|
@property
|
|
64
69
|
def patcher(self) -> Patcher:
|
|
65
70
|
"""Alias `patcher` to get `vbml.Patcher` from the global context."""
|
|
@@ -69,28 +74,57 @@ class Dispatch(
|
|
|
69
74
|
@typing.overload
|
|
70
75
|
def handle(
|
|
71
76
|
self,
|
|
72
|
-
*rules: ABCRule,
|
|
77
|
+
*rules: "ABCRule",
|
|
78
|
+
is_blocking: bool = True,
|
|
79
|
+
) -> typing.Callable[[Handler[T]], FuncHandler[UpdateCute, Handler[T], ErrorHandler]]: ...
|
|
80
|
+
|
|
81
|
+
@typing.overload
|
|
82
|
+
def handle(
|
|
83
|
+
self,
|
|
84
|
+
*rules: "ABCRule",
|
|
85
|
+
dataclass: type[T],
|
|
86
|
+
is_blocking: bool = True,
|
|
73
87
|
) -> typing.Callable[[Handler[T]], FuncHandler[UpdateCute, Handler[T], ErrorHandler]]: ...
|
|
74
88
|
|
|
75
89
|
@typing.overload
|
|
76
90
|
def handle(
|
|
77
91
|
self,
|
|
78
|
-
*rules: ABCRule,
|
|
92
|
+
*rules: "ABCRule",
|
|
93
|
+
update_type: UpdateType,
|
|
79
94
|
is_blocking: bool = True,
|
|
80
95
|
) -> typing.Callable[[Handler[T]], FuncHandler[UpdateCute, Handler[T], ErrorHandler]]: ...
|
|
81
96
|
|
|
82
97
|
@typing.overload
|
|
83
98
|
def handle(
|
|
84
99
|
self,
|
|
85
|
-
*rules: ABCRule,
|
|
100
|
+
*rules: "ABCRule",
|
|
86
101
|
dataclass: type[T],
|
|
102
|
+
update_type: UpdateType,
|
|
87
103
|
is_blocking: bool = True,
|
|
88
104
|
) -> typing.Callable[[Handler[T]], FuncHandler[UpdateCute, Handler[T], ErrorHandler]]: ...
|
|
89
105
|
|
|
90
106
|
@typing.overload
|
|
91
107
|
def handle( # type: ignore
|
|
92
108
|
self,
|
|
93
|
-
*rules: ABCRule,
|
|
109
|
+
*rules: "ABCRule",
|
|
110
|
+
error_handler: ErrorHandlerT,
|
|
111
|
+
is_blocking: bool = True,
|
|
112
|
+
) -> typing.Callable[[Handler[T]], FuncHandler[UpdateCute, Handler[T], ErrorHandlerT]]: ...
|
|
113
|
+
|
|
114
|
+
@typing.overload
|
|
115
|
+
def handle( # type: ignore
|
|
116
|
+
self,
|
|
117
|
+
*rules: "ABCRule",
|
|
118
|
+
update_type: UpdateType,
|
|
119
|
+
error_handler: ErrorHandlerT,
|
|
120
|
+
is_blocking: bool = True,
|
|
121
|
+
) -> typing.Callable[[Handler[T]], FuncHandler[UpdateCute, Handler[T], ErrorHandlerT]]: ...
|
|
122
|
+
|
|
123
|
+
@typing.overload
|
|
124
|
+
def handle(
|
|
125
|
+
self,
|
|
126
|
+
*rules: "ABCRule",
|
|
127
|
+
dataclass: type[T],
|
|
94
128
|
error_handler: ErrorHandlerT,
|
|
95
129
|
is_blocking: bool = True,
|
|
96
130
|
) -> typing.Callable[[Handler[T]], FuncHandler[UpdateCute, Handler[T], ErrorHandlerT]]: ...
|
|
@@ -98,8 +132,9 @@ class Dispatch(
|
|
|
98
132
|
@typing.overload
|
|
99
133
|
def handle(
|
|
100
134
|
self,
|
|
101
|
-
*rules: ABCRule,
|
|
135
|
+
*rules: "ABCRule",
|
|
102
136
|
dataclass: type[T],
|
|
137
|
+
update_type: UpdateType,
|
|
103
138
|
error_handler: ErrorHandlerT,
|
|
104
139
|
is_blocking: bool = True,
|
|
105
140
|
) -> typing.Callable[[Handler[T]], FuncHandler[UpdateCute, Handler[T], ErrorHandlerT]]: ...
|
|
@@ -107,7 +142,8 @@ class Dispatch(
|
|
|
107
142
|
@typing.overload
|
|
108
143
|
def handle(
|
|
109
144
|
self,
|
|
110
|
-
*rules: ABCRule,
|
|
145
|
+
*rules: "ABCRule",
|
|
146
|
+
update_type: UpdateType | None = None,
|
|
111
147
|
dataclass: type[T] = DEFAULT_DATACLASS,
|
|
112
148
|
error_handler: typing.Literal[None] = None,
|
|
113
149
|
is_blocking: bool = True,
|
|
@@ -115,7 +151,8 @@ class Dispatch(
|
|
|
115
151
|
|
|
116
152
|
def handle( # type: ignore
|
|
117
153
|
self,
|
|
118
|
-
*rules: ABCRule,
|
|
154
|
+
*rules: "ABCRule",
|
|
155
|
+
update_type: UpdateType | None = None,
|
|
119
156
|
dataclass: type[typing.Any] = DEFAULT_DATACLASS,
|
|
120
157
|
error_handler: ErrorHandlerT | None = None,
|
|
121
158
|
is_blocking: bool = True,
|
|
@@ -127,6 +164,7 @@ class Dispatch(
|
|
|
127
164
|
is_blocking=is_blocking,
|
|
128
165
|
dataclass=dataclass,
|
|
129
166
|
error_handler=error_handler or ErrorHandler(),
|
|
167
|
+
update_type=update_type,
|
|
130
168
|
)
|
|
131
169
|
self.default_handlers.append(handler)
|
|
132
170
|
return handler
|
|
@@ -142,18 +180,18 @@ class Dispatch(
|
|
|
142
180
|
logger.debug(
|
|
143
181
|
"Update (update_id={}) matched view {!r}.",
|
|
144
182
|
event.update_id,
|
|
145
|
-
view
|
|
183
|
+
view,
|
|
146
184
|
)
|
|
147
|
-
await view.process(event, api)
|
|
148
|
-
|
|
185
|
+
if await view.process(event, api):
|
|
186
|
+
return True
|
|
149
187
|
|
|
150
|
-
ctx = Context()
|
|
188
|
+
ctx = Context(raw_update=event)
|
|
151
189
|
loop = asyncio.get_running_loop()
|
|
152
190
|
found = False
|
|
153
191
|
for handler in self.default_handlers:
|
|
154
192
|
if await handler.check(api, event, ctx):
|
|
155
193
|
found = True
|
|
156
|
-
loop.create_task(handler.run(event, ctx))
|
|
194
|
+
loop.create_task(handler.run(api, event, ctx))
|
|
157
195
|
if handler.is_blocking:
|
|
158
196
|
break
|
|
159
197
|
return found
|
|
@@ -4,7 +4,7 @@ from abc import ABC, abstractmethod
|
|
|
4
4
|
from telegrinder.api.abc import ABCAPI
|
|
5
5
|
from telegrinder.bot.dispatch.context import Context
|
|
6
6
|
from telegrinder.model import Model
|
|
7
|
-
from telegrinder.types import Update
|
|
7
|
+
from telegrinder.types.objects import Update
|
|
8
8
|
|
|
9
9
|
T = typing.TypeVar("T", bound=Model)
|
|
10
10
|
|
|
@@ -17,7 +17,7 @@ class ABCHandler(ABC, typing.Generic[T]):
|
|
|
17
17
|
pass
|
|
18
18
|
|
|
19
19
|
@abstractmethod
|
|
20
|
-
async def run(self, event: T, ctx: Context) -> typing.Any:
|
|
20
|
+
async def run(self, api: ABCAPI, event: T, ctx: Context) -> typing.Any:
|
|
21
21
|
pass
|
|
22
22
|
|
|
23
23
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import dataclasses
|
|
2
|
+
from functools import cached_property
|
|
2
3
|
|
|
3
4
|
import typing_extensions as typing
|
|
4
5
|
|
|
@@ -6,12 +7,14 @@ from telegrinder.api.abc import ABCAPI
|
|
|
6
7
|
from telegrinder.bot.cute_types import BaseCute, UpdateCute
|
|
7
8
|
from telegrinder.bot.dispatch.context import Context
|
|
8
9
|
from telegrinder.bot.dispatch.process import check_rule
|
|
10
|
+
from telegrinder.model import Model
|
|
9
11
|
from telegrinder.modules import logger
|
|
10
|
-
from telegrinder.node import Node,
|
|
12
|
+
from telegrinder.node.base import Node, get_nodes
|
|
13
|
+
from telegrinder.node.composer import compose_nodes
|
|
14
|
+
from telegrinder.node.event import EVENT_NODE_KEY
|
|
11
15
|
from telegrinder.tools.error_handler import ABCErrorHandler, ErrorHandler
|
|
12
|
-
from telegrinder.tools.magic import get_annotations
|
|
13
|
-
from telegrinder.types import Update
|
|
14
16
|
from telegrinder.types.enums import UpdateType
|
|
17
|
+
from telegrinder.types.objects import Update
|
|
15
18
|
|
|
16
19
|
from .abc import ABCHandler
|
|
17
20
|
|
|
@@ -22,11 +25,11 @@ F = typing.TypeVar(
|
|
|
22
25
|
"F",
|
|
23
26
|
bound=typing.Callable[typing.Concatenate[typing.Any, ...], typing.Awaitable[typing.Any]],
|
|
24
27
|
)
|
|
25
|
-
Event = typing.TypeVar("Event", bound=
|
|
28
|
+
Event = typing.TypeVar("Event", bound=Model)
|
|
26
29
|
ErrorHandlerT = typing.TypeVar("ErrorHandlerT", bound=ABCErrorHandler, default=ErrorHandler)
|
|
27
30
|
|
|
28
31
|
|
|
29
|
-
@dataclasses.dataclass(repr=False)
|
|
32
|
+
@dataclasses.dataclass(repr=False, slots=True)
|
|
30
33
|
class FuncHandler(ABCHandler[Event], typing.Generic[Event, F, ErrorHandlerT]):
|
|
31
34
|
func: F
|
|
32
35
|
rules: list["ABCRule"]
|
|
@@ -49,26 +52,37 @@ class FuncHandler(ABCHandler[Event], typing.Generic[Event, F, ErrorHandlerT]):
|
|
|
49
52
|
self.error_handler,
|
|
50
53
|
)
|
|
51
54
|
|
|
52
|
-
|
|
53
|
-
|
|
55
|
+
@cached_property
|
|
56
|
+
def required_nodes(self) -> dict[str, type[Node]]:
|
|
57
|
+
return get_nodes(self.func)
|
|
54
58
|
|
|
55
59
|
async def check(self, api: ABCAPI, event: Update, ctx: Context | None = None) -> bool:
|
|
56
60
|
if self.update_type is not None and self.update_type != event.update_type:
|
|
57
61
|
return False
|
|
58
62
|
|
|
59
|
-
ctx = ctx
|
|
63
|
+
ctx = Context(raw_update=event) if ctx is None else ctx
|
|
60
64
|
temp_ctx = ctx.copy()
|
|
61
65
|
temp_ctx |= self.preset_context
|
|
62
66
|
|
|
63
|
-
nodes = self.
|
|
67
|
+
nodes = self.required_nodes
|
|
64
68
|
node_col = None
|
|
65
69
|
|
|
66
70
|
if nodes:
|
|
67
|
-
node_col = await compose_nodes(
|
|
71
|
+
node_col = await compose_nodes(
|
|
72
|
+
UpdateCute.from_update(event, api),
|
|
73
|
+
ctx,
|
|
74
|
+
nodes,
|
|
75
|
+
)
|
|
76
|
+
|
|
68
77
|
if node_col is None:
|
|
69
78
|
return False
|
|
70
79
|
temp_ctx |= node_col.values()
|
|
71
80
|
|
|
81
|
+
if EVENT_NODE_KEY in ctx:
|
|
82
|
+
for name, node in nodes.items():
|
|
83
|
+
if node is ctx[EVENT_NODE_KEY] and name in temp_ctx:
|
|
84
|
+
ctx[EVENT_NODE_KEY] = temp_ctx.pop(name)
|
|
85
|
+
|
|
72
86
|
for rule in self.rules:
|
|
73
87
|
if not await check_rule(api, rule, event, temp_ctx):
|
|
74
88
|
logger.debug("Rule {!r} failed!", rule)
|
|
@@ -78,19 +92,24 @@ class FuncHandler(ABCHandler[Event], typing.Generic[Event, F, ErrorHandlerT]):
|
|
|
78
92
|
ctx |= temp_ctx
|
|
79
93
|
return True
|
|
80
94
|
|
|
81
|
-
async def run(self, event: Event, ctx: Context) -> typing.Any:
|
|
82
|
-
|
|
95
|
+
async def run(self, api: ABCAPI, event: Event, ctx: Context) -> typing.Any:
|
|
96
|
+
dataclass_type = typing.get_origin(self.dataclass) or self.dataclass
|
|
97
|
+
|
|
98
|
+
if dataclass_type is Update and (event_node := ctx.pop(EVENT_NODE_KEY, None)) is not None:
|
|
99
|
+
event = event_node
|
|
83
100
|
|
|
84
|
-
|
|
85
|
-
if self.update_type is not None:
|
|
101
|
+
elif dataclass_type is not None:
|
|
102
|
+
if self.update_type is not None and isinstance(event, Update):
|
|
86
103
|
update = event.to_dict()[self.update_type.value].unwrap()
|
|
87
104
|
event = (
|
|
88
105
|
self.dataclass.from_update(update, bound_api=api) # type: ignore
|
|
89
|
-
if issubclass(
|
|
90
|
-
else self.dataclass(**update.to_dict())
|
|
106
|
+
if issubclass(dataclass_type, BaseCute)
|
|
107
|
+
else self.dataclass(**update.to_dict()) # type: ignore
|
|
91
108
|
)
|
|
109
|
+
elif issubclass(dataclass_type, UpdateCute) and isinstance(event, Update):
|
|
110
|
+
event = self.dataclass.from_update(event, bound_api=api) # type: ignore
|
|
92
111
|
else:
|
|
93
|
-
event = self.dataclass(**event.to_dict())
|
|
112
|
+
event = self.dataclass(**event.to_dict()) # type: ignore
|
|
94
113
|
|
|
95
114
|
result = (await self.error_handler.run(self.func, event, api, ctx)).unwrap()
|
|
96
115
|
if node_col := ctx.node_col:
|
|
@@ -37,7 +37,7 @@ class MessageReplyHandler(ABCHandler[MessageCute]):
|
|
|
37
37
|
)
|
|
38
38
|
|
|
39
39
|
async def check(self, api: ABCAPI, event: Update, ctx: Context | None = None) -> bool:
|
|
40
|
-
ctx = ctx
|
|
40
|
+
ctx = Context() if ctx is None else ctx
|
|
41
41
|
temp_ctx = ctx.copy()
|
|
42
42
|
temp_ctx |= self.preset_context
|
|
43
43
|
|
|
@@ -49,7 +49,7 @@ class MessageReplyHandler(ABCHandler[MessageCute]):
|
|
|
49
49
|
ctx |= temp_ctx
|
|
50
50
|
return True
|
|
51
51
|
|
|
52
|
-
async def run(self, event: MessageCute,
|
|
52
|
+
async def run(self, _: ABCAPI, event: MessageCute, __: Context) -> typing.Any:
|
|
53
53
|
await event.answer(
|
|
54
54
|
text=self.text,
|
|
55
55
|
reply_parameters=ReplyParameters(event.message_id) if self.as_reply else None,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import typing
|
|
2
2
|
from abc import ABC
|
|
3
3
|
|
|
4
|
-
from telegrinder.bot.cute_types.base import BaseCute
|
|
5
4
|
from telegrinder.bot.dispatch.context import Context
|
|
5
|
+
from telegrinder.model import Model
|
|
6
6
|
|
|
7
|
-
Event = typing.TypeVar("Event", bound=
|
|
7
|
+
Event = typing.TypeVar("Event", bound=Model)
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class ABCMiddleware(ABC, typing.Generic[Event]):
|
|
@@ -3,26 +3,26 @@ import typing
|
|
|
3
3
|
from fntypes.result import Error, Ok
|
|
4
4
|
|
|
5
5
|
from telegrinder.api.abc import ABCAPI
|
|
6
|
-
from telegrinder.bot.cute_types import BaseCute
|
|
7
6
|
from telegrinder.bot.cute_types.update import UpdateCute
|
|
8
7
|
from telegrinder.bot.dispatch.context import Context
|
|
8
|
+
from telegrinder.bot.dispatch.middleware.abc import ABCMiddleware
|
|
9
|
+
from telegrinder.bot.dispatch.return_manager.abc import ABCReturnManager
|
|
10
|
+
from telegrinder.model import Model
|
|
9
11
|
from telegrinder.modules import logger
|
|
10
12
|
from telegrinder.node.composer import CONTEXT_STORE_NODES_KEY, NodeScope, compose_nodes
|
|
11
13
|
from telegrinder.tools.i18n.base import I18nEnum
|
|
12
|
-
from telegrinder.types import Update
|
|
13
|
-
|
|
14
|
-
from .middleware.abc import ABCMiddleware
|
|
15
|
-
from .return_manager.abc import ABCReturnManager
|
|
14
|
+
from telegrinder.types.objects import Update
|
|
16
15
|
|
|
17
16
|
if typing.TYPE_CHECKING:
|
|
18
17
|
from telegrinder.bot.dispatch.handler.abc import ABCHandler
|
|
19
18
|
from telegrinder.bot.rules.abc import ABCRule
|
|
20
19
|
|
|
21
|
-
Event = typing.TypeVar("Event", bound=
|
|
20
|
+
Event = typing.TypeVar("Event", bound=Model)
|
|
22
21
|
_: typing.TypeAlias = typing.Any
|
|
23
22
|
|
|
24
23
|
|
|
25
24
|
async def process_inner(
|
|
25
|
+
api: ABCAPI,
|
|
26
26
|
event: Event,
|
|
27
27
|
raw_event: Update,
|
|
28
28
|
middlewares: list[ABCMiddleware[Event]],
|
|
@@ -42,9 +42,9 @@ async def process_inner(
|
|
|
42
42
|
ctx_copy = ctx.copy()
|
|
43
43
|
|
|
44
44
|
for handler in handlers:
|
|
45
|
-
if await handler.check(
|
|
45
|
+
if await handler.check(api, raw_event, ctx):
|
|
46
46
|
found = True
|
|
47
|
-
response = await handler.run(event, ctx)
|
|
47
|
+
response = await handler.run(api, event, ctx)
|
|
48
48
|
logger.debug("Handler {!r} returned: {!r}", handler, response)
|
|
49
49
|
responses.append(response)
|
|
50
50
|
if return_manager is not None:
|
|
@@ -93,10 +93,10 @@ async def check_rule(
|
|
|
93
93
|
ctx |= ctx_copy
|
|
94
94
|
|
|
95
95
|
# Composing required nodes
|
|
96
|
-
nodes = rule.
|
|
96
|
+
nodes = rule.required_nodes
|
|
97
97
|
node_col = None
|
|
98
98
|
if nodes:
|
|
99
|
-
node_col = await compose_nodes(
|
|
99
|
+
node_col = await compose_nodes(UpdateCute.from_update(update, api), ctx, nodes)
|
|
100
100
|
if node_col is None:
|
|
101
101
|
return False
|
|
102
102
|
|
|
@@ -3,12 +3,12 @@ import types
|
|
|
3
3
|
import typing
|
|
4
4
|
from abc import ABC, abstractmethod
|
|
5
5
|
|
|
6
|
-
from telegrinder.bot.cute_types import BaseCute
|
|
7
6
|
from telegrinder.bot.dispatch.context import Context
|
|
7
|
+
from telegrinder.model import Model
|
|
8
8
|
from telegrinder.modules import logger
|
|
9
9
|
|
|
10
10
|
T = typing.TypeVar("T")
|
|
11
|
-
Event = typing.TypeVar("Event", bound=
|
|
11
|
+
Event = typing.TypeVar("Event", bound=Model, contravariant=True)
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
def get_union_types(t: types.UnionType) -> tuple[type, ...] | None:
|
|
@@ -24,7 +24,7 @@ def register_manager(return_type: type[typing.Any] | types.UnionType):
|
|
|
24
24
|
return wrapper
|
|
25
25
|
|
|
26
26
|
|
|
27
|
-
@dataclasses.dataclass(frozen=True)
|
|
27
|
+
@dataclasses.dataclass(frozen=True, slots=True)
|
|
28
28
|
class Manager:
|
|
29
29
|
types: tuple[type, ...]
|
|
30
30
|
callback: typing.Callable[..., typing.Awaitable]
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import typing
|
|
2
2
|
from abc import ABC, abstractmethod
|
|
3
|
+
from functools import cached_property
|
|
3
4
|
|
|
4
5
|
from fntypes.co import Nothing, Some
|
|
5
6
|
|
|
@@ -28,17 +29,14 @@ FuncType: typing.TypeAlias = typing.Callable[
|
|
|
28
29
|
|
|
29
30
|
class ABCView(ABC):
|
|
30
31
|
def __repr__(self) -> str:
|
|
31
|
-
return "<{!r}
|
|
32
|
-
self.__class__.__name__,
|
|
33
|
-
", ".join(f"{k}={v!r}" for k, v in self.__dict__.items()),
|
|
34
|
-
)
|
|
32
|
+
return "<{!r}>".format(self.__class__.__name__)
|
|
35
33
|
|
|
36
34
|
@abstractmethod
|
|
37
35
|
async def check(self, event: Update) -> bool:
|
|
38
36
|
pass
|
|
39
37
|
|
|
40
38
|
@abstractmethod
|
|
41
|
-
async def process(self, event: Update, api: ABCAPI) ->
|
|
39
|
+
async def process(self, event: Update, api: ABCAPI) -> bool:
|
|
42
40
|
pass
|
|
43
41
|
|
|
44
42
|
@abstractmethod
|
|
@@ -62,9 +60,9 @@ class BaseView(ABCView, typing.Generic[Event]):
|
|
|
62
60
|
def get_raw_event(update: Update) -> Option[Model]:
|
|
63
61
|
return getattr(update, update.update_type.value)
|
|
64
62
|
|
|
65
|
-
@
|
|
66
|
-
def get_event_type(
|
|
67
|
-
for base in
|
|
63
|
+
@cached_property
|
|
64
|
+
def get_event_type(self) -> Option[type[Event]]:
|
|
65
|
+
for base in self.__class__.__dict__.get("__orig_bases__", ()):
|
|
68
66
|
if issubclass(typing.get_origin(base) or base, ABCView):
|
|
69
67
|
for generic_type in typing.get_args(base):
|
|
70
68
|
if issubclass(typing.get_origin(generic_type) or generic_type, BaseCute):
|
|
@@ -177,20 +175,19 @@ class BaseView(ABCView, typing.Generic[Event]):
|
|
|
177
175
|
async def check(self, event: Update) -> bool:
|
|
178
176
|
match self.get_raw_event(event):
|
|
179
177
|
case Some(e) if issubclass(
|
|
180
|
-
self.get_event_type
|
|
178
|
+
self.get_event_type.expect(
|
|
181
179
|
"{!r} has no event type in generic.".format(self.__class__.__name__),
|
|
182
180
|
),
|
|
183
181
|
e.__class__,
|
|
184
|
-
):
|
|
182
|
+
) and (self.handlers or self.middlewares):
|
|
185
183
|
return True
|
|
186
184
|
case _:
|
|
187
185
|
return False
|
|
188
186
|
|
|
189
|
-
async def process(self, event: Update, api: ABCAPI) ->
|
|
190
|
-
await process_inner(
|
|
191
|
-
|
|
192
|
-
.unwrap()
|
|
193
|
-
.from_update(
|
|
187
|
+
async def process(self, event: Update, api: ABCAPI) -> bool:
|
|
188
|
+
return await process_inner(
|
|
189
|
+
api,
|
|
190
|
+
self.get_event_type.unwrap().from_update(
|
|
194
191
|
update=self.get_raw_event(event).unwrap(),
|
|
195
192
|
bound_api=api,
|
|
196
193
|
),
|
|
@@ -36,78 +36,89 @@ class ViewBox(
|
|
|
36
36
|
RawEventView,
|
|
37
37
|
],
|
|
38
38
|
):
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
callback_query_view: dataclasses.InitVar[CallbackQueryView | None] = None
|
|
40
|
+
chat_join_request_view: dataclasses.InitVar[ChatJoinRequestView | None] = None
|
|
41
|
+
chat_member_view: dataclasses.InitVar[ChatMemberView | None] = None
|
|
42
|
+
my_chat_member_view: dataclasses.InitVar[ChatMemberView | None] = None
|
|
43
|
+
inline_query_view: dataclasses.InitVar[InlineQueryView | None] = None
|
|
44
|
+
message_view: dataclasses.InitVar[MessageView | None] = None
|
|
45
|
+
business_message_view: dataclasses.InitVar[MessageView | None] = None
|
|
46
|
+
channel_post_view: dataclasses.InitVar[MessageView | None] = None
|
|
47
|
+
edited_message_view: dataclasses.InitVar[MessageView | None] = None
|
|
48
|
+
edited_business_message_view: dataclasses.InitVar[MessageView | None] = None
|
|
49
|
+
edited_channel_post_view: dataclasses.InitVar[MessageView | None] = None
|
|
50
|
+
any_message_view: dataclasses.InitVar[MessageView | None] = None
|
|
51
|
+
chat_member_updated_view: dataclasses.InitVar[ChatMemberView | None] = None
|
|
52
|
+
raw_event_view: dataclasses.InitVar[RawEventView | None] = None
|
|
53
|
+
|
|
54
|
+
def __post_init__(
|
|
55
|
+
self,
|
|
56
|
+
callback_query_view: CallbackQueryView | None = None,
|
|
57
|
+
chat_join_request_view: ChatJoinRequestView | None = None,
|
|
58
|
+
chat_member_view: ChatMemberView | None = None,
|
|
59
|
+
my_chat_member_view: ChatMemberView | None = None,
|
|
60
|
+
inline_query_view: InlineQueryView | None = None,
|
|
61
|
+
message_view: MessageView | None = None,
|
|
62
|
+
business_message_view: MessageView | None = None,
|
|
63
|
+
channel_post_view: MessageView | None = None,
|
|
64
|
+
edited_message_view: MessageView | None = None,
|
|
65
|
+
edited_business_message_view: MessageView | None = None,
|
|
66
|
+
edited_channel_post_view: MessageView | None = None,
|
|
67
|
+
any_message_view: MessageView | None = None,
|
|
68
|
+
chat_member_updated_view: ChatMemberView | None = None,
|
|
69
|
+
raw_event_view: RawEventView | None = None,
|
|
70
|
+
) -> None:
|
|
71
|
+
self.callback_query = typing.cast(
|
|
41
72
|
CallbackQueryView,
|
|
42
|
-
callback_query.CallbackQueryView(),
|
|
43
|
-
)
|
|
44
|
-
|
|
45
|
-
chat_join_request: ChatJoinRequestView = dataclasses.field(
|
|
46
|
-
default_factory=lambda: typing.cast(
|
|
73
|
+
callback_query_view or callback_query.CallbackQueryView(),
|
|
74
|
+
)
|
|
75
|
+
self.chat_join_request = typing.cast(
|
|
47
76
|
ChatJoinRequestView,
|
|
48
|
-
chat_join_request.ChatJoinRequestView(),
|
|
49
|
-
)
|
|
50
|
-
|
|
51
|
-
chat_member: ChatMemberView = dataclasses.field(
|
|
52
|
-
default_factory=lambda: typing.cast(
|
|
77
|
+
chat_join_request_view or chat_join_request.ChatJoinRequestView(),
|
|
78
|
+
)
|
|
79
|
+
self.chat_member = typing.cast(
|
|
53
80
|
ChatMemberView,
|
|
54
|
-
chat_member.ChatMemberView(update_type=UpdateType.CHAT_MEMBER),
|
|
55
|
-
)
|
|
56
|
-
|
|
57
|
-
my_chat_member: ChatMemberView = dataclasses.field(
|
|
58
|
-
default_factory=lambda: typing.cast(
|
|
81
|
+
chat_member_view or chat_member.ChatMemberView(update_type=UpdateType.CHAT_MEMBER),
|
|
82
|
+
)
|
|
83
|
+
self.my_chat_member = typing.cast(
|
|
59
84
|
ChatMemberView,
|
|
60
|
-
chat_member.ChatMemberView(update_type=UpdateType.MY_CHAT_MEMBER),
|
|
61
|
-
)
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
default_factory=lambda: typing.cast(
|
|
85
|
+
my_chat_member_view or chat_member.ChatMemberView(update_type=UpdateType.MY_CHAT_MEMBER),
|
|
86
|
+
)
|
|
87
|
+
self.inline_query = typing.cast(
|
|
88
|
+
InlineQueryView,
|
|
89
|
+
inline_query_view or inline_query.InlineQueryView(),
|
|
90
|
+
)
|
|
91
|
+
self.message = typing.cast(
|
|
68
92
|
MessageView,
|
|
69
|
-
message.MessageView(update_type=UpdateType.MESSAGE),
|
|
70
|
-
)
|
|
71
|
-
|
|
72
|
-
business_message: MessageView = dataclasses.field(
|
|
73
|
-
default_factory=lambda: typing.cast(
|
|
93
|
+
message_view or message.MessageView(update_type=UpdateType.MESSAGE),
|
|
94
|
+
)
|
|
95
|
+
self.business_message = typing.cast(
|
|
74
96
|
MessageView,
|
|
75
|
-
message.MessageView(update_type=UpdateType.BUSINESS_MESSAGE),
|
|
76
|
-
)
|
|
77
|
-
|
|
78
|
-
channel_post: MessageView = dataclasses.field(
|
|
79
|
-
default_factory=lambda: typing.cast(
|
|
97
|
+
business_message_view or message.MessageView(update_type=UpdateType.BUSINESS_MESSAGE),
|
|
98
|
+
)
|
|
99
|
+
self.channel_post = typing.cast(
|
|
80
100
|
MessageView,
|
|
81
|
-
message.MessageView(update_type=UpdateType.CHANNEL_POST),
|
|
82
|
-
)
|
|
83
|
-
|
|
84
|
-
edited_message: MessageView = dataclasses.field(
|
|
85
|
-
default_factory=lambda: typing.cast(
|
|
101
|
+
channel_post_view or message.MessageView(update_type=UpdateType.CHANNEL_POST),
|
|
102
|
+
)
|
|
103
|
+
self.edited_message = typing.cast(
|
|
86
104
|
MessageView,
|
|
87
|
-
message.MessageView(update_type=UpdateType.EDITED_MESSAGE),
|
|
88
|
-
)
|
|
89
|
-
|
|
90
|
-
edited_business_message: MessageView = dataclasses.field(
|
|
91
|
-
default_factory=lambda: typing.cast(
|
|
105
|
+
edited_message_view or message.MessageView(update_type=UpdateType.EDITED_MESSAGE),
|
|
106
|
+
)
|
|
107
|
+
self.edited_business_message = typing.cast(
|
|
92
108
|
MessageView,
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
default_factory=lambda: typing.cast(
|
|
109
|
+
edited_business_message_view
|
|
110
|
+
or message.MessageView(update_type=UpdateType.EDITED_BUSINESS_MESSAGE),
|
|
111
|
+
)
|
|
112
|
+
self.edited_channel_post = typing.cast(
|
|
98
113
|
MessageView,
|
|
99
|
-
message.MessageView(update_type=UpdateType.EDITED_CHANNEL_POST),
|
|
100
|
-
)
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
)
|
|
108
|
-
raw_event: RawEventView = dataclasses.field(
|
|
109
|
-
default_factory=lambda: typing.cast(RawEventView, raw.RawEventView()),
|
|
110
|
-
)
|
|
114
|
+
edited_channel_post_view or message.MessageView(update_type=UpdateType.EDITED_CHANNEL_POST),
|
|
115
|
+
)
|
|
116
|
+
self.any_message = typing.cast(MessageView, any_message_view or message.MessageView())
|
|
117
|
+
self.chat_member_updated = typing.cast(
|
|
118
|
+
ChatMemberView,
|
|
119
|
+
chat_member_updated_view or chat_member.ChatMemberView(),
|
|
120
|
+
)
|
|
121
|
+
self.raw_event = typing.cast(RawEventView, raw_event_view or raw.RawEventView())
|
|
111
122
|
|
|
112
123
|
def get_views(self) -> dict[str, ABCView]:
|
|
113
124
|
"""Get all views."""
|
|
@@ -24,13 +24,21 @@ class MessageView(BaseStateView[MessageCute]):
|
|
|
24
24
|
self.middlewares = []
|
|
25
25
|
self.return_manager = MessageReturnManager()
|
|
26
26
|
|
|
27
|
+
def __repr__(self) -> str:
|
|
28
|
+
return "<{}: {!r}>".format(
|
|
29
|
+
self.__class__.__name__,
|
|
30
|
+
"any message update" if self.update_type is None else self.update_type.value,
|
|
31
|
+
)
|
|
32
|
+
|
|
27
33
|
def get_state_key(self, event: MessageCute) -> int | None:
|
|
28
34
|
return event.chat_id
|
|
29
35
|
|
|
30
36
|
async def check(self, event: Update) -> bool:
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
37
|
+
return not (
|
|
38
|
+
self.update_type is not None
|
|
39
|
+
and self.update_type != event.update_type
|
|
40
|
+
or not await super().check(event)
|
|
41
|
+
)
|
|
34
42
|
|
|
35
43
|
|
|
36
44
|
__all__ = ("MessageView",)
|