telegrinder 0.1.dev19__py3-none-any.whl → 0.1.dev158__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 +129 -22
- telegrinder/api/__init__.py +11 -2
- telegrinder/api/abc.py +25 -9
- telegrinder/api/api.py +29 -24
- telegrinder/api/error.py +14 -4
- telegrinder/api/response.py +11 -7
- telegrinder/bot/__init__.py +68 -7
- telegrinder/bot/bot.py +30 -24
- telegrinder/bot/cute_types/__init__.py +11 -1
- telegrinder/bot/cute_types/base.py +47 -0
- telegrinder/bot/cute_types/callback_query.py +64 -14
- telegrinder/bot/cute_types/inline_query.py +22 -16
- telegrinder/bot/cute_types/message.py +163 -43
- telegrinder/bot/cute_types/update.py +23 -0
- telegrinder/bot/dispatch/__init__.py +56 -3
- telegrinder/bot/dispatch/abc.py +9 -7
- telegrinder/bot/dispatch/composition.py +74 -0
- telegrinder/bot/dispatch/context.py +71 -0
- telegrinder/bot/dispatch/dispatch.py +86 -49
- telegrinder/bot/dispatch/handler/__init__.py +3 -0
- telegrinder/bot/dispatch/handler/abc.py +11 -5
- telegrinder/bot/dispatch/handler/func.py +41 -32
- telegrinder/bot/dispatch/handler/message_reply.py +46 -0
- telegrinder/bot/dispatch/middleware/__init__.py +2 -0
- telegrinder/bot/dispatch/middleware/abc.py +10 -4
- telegrinder/bot/dispatch/process.py +53 -49
- telegrinder/bot/dispatch/return_manager/__init__.py +19 -0
- telegrinder/bot/dispatch/return_manager/abc.py +95 -0
- telegrinder/bot/dispatch/return_manager/callback_query.py +19 -0
- telegrinder/bot/dispatch/return_manager/inline_query.py +14 -0
- telegrinder/bot/dispatch/return_manager/message.py +25 -0
- telegrinder/bot/dispatch/view/__init__.py +14 -2
- telegrinder/bot/dispatch/view/abc.py +121 -2
- telegrinder/bot/dispatch/view/box.py +38 -0
- telegrinder/bot/dispatch/view/callback_query.py +13 -38
- telegrinder/bot/dispatch/view/inline_query.py +11 -38
- telegrinder/bot/dispatch/view/message.py +11 -46
- telegrinder/bot/dispatch/waiter_machine/__init__.py +9 -0
- telegrinder/bot/dispatch/waiter_machine/machine.py +116 -0
- telegrinder/bot/dispatch/waiter_machine/middleware.py +76 -0
- telegrinder/bot/dispatch/waiter_machine/short_state.py +37 -0
- telegrinder/bot/polling/__init__.py +2 -0
- telegrinder/bot/polling/abc.py +11 -4
- telegrinder/bot/polling/polling.py +89 -40
- telegrinder/bot/rules/__init__.py +92 -5
- telegrinder/bot/rules/abc.py +81 -63
- telegrinder/bot/rules/adapter/__init__.py +11 -0
- telegrinder/bot/rules/adapter/abc.py +21 -0
- telegrinder/bot/rules/adapter/errors.py +5 -0
- telegrinder/bot/rules/adapter/event.py +43 -0
- telegrinder/bot/rules/adapter/raw_update.py +24 -0
- telegrinder/bot/rules/callback_data.py +159 -38
- telegrinder/bot/rules/command.py +116 -0
- telegrinder/bot/rules/enum_text.py +28 -0
- telegrinder/bot/rules/func.py +17 -17
- telegrinder/bot/rules/fuzzy.py +13 -10
- telegrinder/bot/rules/inline.py +61 -0
- telegrinder/bot/rules/integer.py +12 -7
- telegrinder/bot/rules/is_from.py +148 -7
- telegrinder/bot/rules/markup.py +21 -18
- telegrinder/bot/rules/mention.py +17 -0
- telegrinder/bot/rules/message_entities.py +33 -0
- telegrinder/bot/rules/regex.py +27 -19
- telegrinder/bot/rules/rule_enum.py +74 -0
- telegrinder/bot/rules/start.py +42 -0
- telegrinder/bot/rules/text.py +23 -14
- telegrinder/bot/scenario/__init__.py +2 -0
- telegrinder/bot/scenario/abc.py +12 -5
- telegrinder/bot/scenario/checkbox.py +48 -30
- telegrinder/bot/scenario/choice.py +16 -10
- telegrinder/client/__init__.py +2 -0
- telegrinder/client/abc.py +8 -21
- telegrinder/client/aiohttp.py +30 -21
- telegrinder/model.py +68 -37
- telegrinder/modules.py +189 -21
- telegrinder/msgspec_json.py +14 -0
- telegrinder/msgspec_utils.py +207 -0
- telegrinder/node/__init__.py +31 -0
- telegrinder/node/attachment.py +71 -0
- telegrinder/node/base.py +93 -0
- telegrinder/node/composer.py +71 -0
- telegrinder/node/container.py +22 -0
- telegrinder/node/message.py +18 -0
- telegrinder/node/rule.py +56 -0
- telegrinder/node/source.py +31 -0
- telegrinder/node/text.py +13 -0
- telegrinder/node/tools/__init__.py +3 -0
- telegrinder/node/tools/generator.py +40 -0
- telegrinder/node/update.py +12 -0
- telegrinder/rules.py +1 -1
- telegrinder/tools/__init__.py +165 -4
- telegrinder/tools/buttons.py +75 -51
- telegrinder/tools/error_handler/__init__.py +8 -0
- telegrinder/tools/error_handler/abc.py +30 -0
- telegrinder/tools/error_handler/error_handler.py +156 -0
- telegrinder/tools/formatting/__init__.py +81 -3
- telegrinder/tools/formatting/html.py +283 -37
- telegrinder/tools/formatting/links.py +32 -0
- telegrinder/tools/formatting/spec_html_formats.py +121 -0
- telegrinder/tools/global_context/__init__.py +12 -0
- telegrinder/tools/global_context/abc.py +66 -0
- telegrinder/tools/global_context/global_context.py +451 -0
- telegrinder/tools/global_context/telegrinder_ctx.py +25 -0
- telegrinder/tools/i18n/__init__.py +12 -0
- telegrinder/tools/i18n/base.py +31 -0
- telegrinder/tools/i18n/middleware/__init__.py +3 -0
- telegrinder/tools/i18n/middleware/base.py +26 -0
- telegrinder/tools/i18n/simple.py +48 -0
- telegrinder/tools/inline_query.py +684 -0
- telegrinder/tools/kb_set/__init__.py +2 -0
- telegrinder/tools/kb_set/base.py +3 -0
- telegrinder/tools/kb_set/yaml.py +28 -17
- telegrinder/tools/keyboard.py +84 -62
- telegrinder/tools/loop_wrapper/__init__.py +4 -0
- telegrinder/tools/loop_wrapper/abc.py +18 -0
- telegrinder/tools/loop_wrapper/loop_wrapper.py +132 -0
- telegrinder/tools/magic.py +48 -23
- telegrinder/tools/parse_mode.py +1 -2
- telegrinder/types/__init__.py +1 -0
- telegrinder/types/enums.py +651 -0
- telegrinder/types/methods.py +3933 -1128
- telegrinder/types/objects.py +4755 -1633
- {telegrinder-0.1.dev19.dist-info → telegrinder-0.1.dev158.dist-info}/LICENSE +2 -1
- telegrinder-0.1.dev158.dist-info/METADATA +108 -0
- telegrinder-0.1.dev158.dist-info/RECORD +126 -0
- {telegrinder-0.1.dev19.dist-info → telegrinder-0.1.dev158.dist-info}/WHEEL +1 -1
- telegrinder/bot/dispatch/waiter.py +0 -37
- telegrinder/result.py +0 -38
- telegrinder/tools/formatting/abc.py +0 -52
- telegrinder/tools/formatting/markdown.py +0 -57
- telegrinder/typegen/__init__.py +0 -1
- telegrinder/typegen/__main__.py +0 -3
- telegrinder/typegen/nicification.py +0 -20
- telegrinder/typegen/schema_generator.py +0 -259
- telegrinder-0.1.dev19.dist-info/METADATA +0 -22
- telegrinder-0.1.dev19.dist-info/RECORD +0 -74
|
@@ -1,68 +1,76 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
import dataclasses
|
|
3
|
+
import typing
|
|
4
|
+
|
|
5
|
+
from vbml.patcher import Patcher
|
|
2
6
|
|
|
3
|
-
from .abc import ABCDispatch
|
|
4
|
-
from telegrinder.bot.rules import ABCRule
|
|
5
|
-
from .handler import ABCHandler, FuncHandler
|
|
6
|
-
from telegrinder.types import Update
|
|
7
7
|
from telegrinder.api.abc import ABCAPI
|
|
8
|
+
from telegrinder.bot.cute_types.base import BaseCute
|
|
9
|
+
from telegrinder.bot.rules import ABCRule
|
|
8
10
|
from telegrinder.modules import logger
|
|
9
|
-
from .
|
|
10
|
-
import
|
|
11
|
+
from telegrinder.tools.global_context import TelegrinderCtx
|
|
12
|
+
from telegrinder.types import Update
|
|
13
|
+
|
|
14
|
+
from .abc import ABCDispatch
|
|
15
|
+
from .handler import ABCHandler, FuncHandler
|
|
16
|
+
from .handler.func import ErrorHandlerT
|
|
17
|
+
from .view.box import CallbackQueryViewT, InlineQueryViewT, MessageViewT, ViewBox
|
|
11
18
|
|
|
12
19
|
T = typing.TypeVar("T")
|
|
13
20
|
|
|
21
|
+
Event = typing.TypeVar("Event", bound=BaseCute)
|
|
22
|
+
R = typing.TypeVar("R")
|
|
23
|
+
P = typing.ParamSpec("P")
|
|
24
|
+
|
|
14
25
|
DEFAULT_DATACLASS = Update
|
|
15
26
|
|
|
16
27
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
28
|
+
@dataclasses.dataclass(repr=False, kw_only=True)
|
|
29
|
+
class Dispatch(
|
|
30
|
+
ABCDispatch,
|
|
31
|
+
ViewBox[CallbackQueryViewT, InlineQueryViewT, MessageViewT],
|
|
32
|
+
):
|
|
33
|
+
global_context: TelegrinderCtx = dataclasses.field(
|
|
34
|
+
init=False,
|
|
35
|
+
default_factory=lambda: TelegrinderCtx(),
|
|
36
|
+
)
|
|
37
|
+
default_handlers: list[ABCHandler] = dataclasses.field(
|
|
38
|
+
init=False,
|
|
39
|
+
default_factory=lambda: [],
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
def __repr__(self) -> str:
|
|
43
|
+
return "Dispatch(%s)" % ", ".join(
|
|
44
|
+
f"{k}={v!r}" for k, v in self.__dict__.items()
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
@property
|
|
48
|
+
def patcher(self) -> Patcher:
|
|
49
|
+
"""Alias `patcher` to get `vbml.Patcher` from the global context"""
|
|
50
|
+
return self.global_context.vbml_patcher
|
|
24
51
|
|
|
25
52
|
def handle(
|
|
26
53
|
self,
|
|
27
54
|
*rules: ABCRule,
|
|
28
55
|
is_blocking: bool = True,
|
|
29
|
-
dataclass: typing.Any = DEFAULT_DATACLASS,
|
|
56
|
+
dataclass: type[typing.Any] = DEFAULT_DATACLASS,
|
|
57
|
+
error_handler: ErrorHandlerT | None = None,
|
|
30
58
|
):
|
|
31
59
|
def wrapper(func: typing.Callable):
|
|
32
|
-
|
|
33
|
-
|
|
60
|
+
handler = FuncHandler(
|
|
61
|
+
func, list(rules), is_blocking, dataclass, error_handler,
|
|
34
62
|
)
|
|
35
|
-
|
|
63
|
+
self.default_handlers.append(handler)
|
|
64
|
+
return handler
|
|
36
65
|
|
|
37
66
|
return wrapper
|
|
38
67
|
|
|
39
|
-
def get_views(self) -> typing.Iterator[ABCView]:
|
|
40
|
-
for view_name in self.views:
|
|
41
|
-
view = getattr(self, view_name)
|
|
42
|
-
assert view, f"View {view_name} is undefined in dispatch"
|
|
43
|
-
yield view
|
|
44
|
-
|
|
45
|
-
def get_view(self, view_t: typing.Type[T], name: str) -> typing.Optional[T]:
|
|
46
|
-
if name not in self.views:
|
|
47
|
-
return None
|
|
48
|
-
view = getattr(self, name)
|
|
49
|
-
assert isinstance(view, view_t)
|
|
50
|
-
return view # type: ignore
|
|
51
|
-
|
|
52
|
-
def load(self, external: "Dispatch"):
|
|
53
|
-
for view_name in self.views:
|
|
54
|
-
view = getattr(self, view_name)
|
|
55
|
-
assert view, f"View {view_name} is undefined in dispatch"
|
|
56
|
-
view_external = getattr(external, view_name)
|
|
57
|
-
assert view_external, f"View {view_name} is undefined in external dispatch"
|
|
58
|
-
view.load(view_external)
|
|
59
|
-
|
|
60
68
|
async def feed(self, event: Update, api: ABCAPI) -> bool:
|
|
61
|
-
logger.debug("
|
|
62
|
-
for view in self.get_views():
|
|
69
|
+
logger.debug("Processing update (update_id={})", event.update_id)
|
|
70
|
+
for view in self.get_views().values():
|
|
63
71
|
if await view.check(event):
|
|
64
72
|
logger.debug(
|
|
65
|
-
"
|
|
73
|
+
"Update (update_id={}) matched view {!r}",
|
|
66
74
|
event.update_id,
|
|
67
75
|
view.__class__.__name__,
|
|
68
76
|
)
|
|
@@ -70,18 +78,47 @@ class Dispatch(ABCDispatch):
|
|
|
70
78
|
return True
|
|
71
79
|
|
|
72
80
|
loop = asyncio.get_running_loop()
|
|
73
|
-
assert loop, "No running loop"
|
|
74
|
-
|
|
75
81
|
found = False
|
|
76
82
|
for handler in self.default_handlers:
|
|
77
|
-
|
|
78
|
-
if result:
|
|
83
|
+
if await handler.check(api, event):
|
|
79
84
|
found = True
|
|
80
85
|
loop.create_task(handler.run(event))
|
|
81
86
|
if handler.is_blocking:
|
|
82
|
-
|
|
87
|
+
break
|
|
83
88
|
return found
|
|
84
89
|
|
|
85
|
-
def
|
|
86
|
-
|
|
87
|
-
|
|
90
|
+
def load(self, external: typing.Self):
|
|
91
|
+
view_external = external.get_views()
|
|
92
|
+
for name, view in self.get_views().items():
|
|
93
|
+
assert (
|
|
94
|
+
name in view_external
|
|
95
|
+
), f"View {name!r} is undefined in external dispatch."
|
|
96
|
+
view.load(view_external[name])
|
|
97
|
+
setattr(external, name, view)
|
|
98
|
+
|
|
99
|
+
@classmethod
|
|
100
|
+
def to_handler(
|
|
101
|
+
cls,
|
|
102
|
+
*rules: ABCRule[BaseCute],
|
|
103
|
+
is_blocking: bool = True,
|
|
104
|
+
error_handler: ErrorHandlerT | None = None,
|
|
105
|
+
):
|
|
106
|
+
def wrapper(
|
|
107
|
+
func: typing.Callable[typing.Concatenate[T, P], typing.Awaitable[R]]
|
|
108
|
+
) -> FuncHandler[
|
|
109
|
+
BaseCute,
|
|
110
|
+
typing.Callable[typing.Concatenate[T, P], typing.Awaitable[R]],
|
|
111
|
+
ErrorHandlerT,
|
|
112
|
+
]:
|
|
113
|
+
return FuncHandler(
|
|
114
|
+
func,
|
|
115
|
+
list(rules),
|
|
116
|
+
is_blocking=is_blocking,
|
|
117
|
+
dataclass=None,
|
|
118
|
+
error_handler=error_handler,
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
return wrapper
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
__all__ = ("Dispatch",)
|
|
@@ -1,19 +1,25 @@
|
|
|
1
|
+
import typing
|
|
1
2
|
from abc import ABC, abstractmethod
|
|
2
|
-
|
|
3
|
+
|
|
3
4
|
from telegrinder.api.abc import ABCAPI
|
|
4
|
-
import
|
|
5
|
+
from telegrinder.bot.dispatch.context import Context
|
|
6
|
+
from telegrinder.model import Model
|
|
7
|
+
from telegrinder.types import Update
|
|
5
8
|
|
|
6
|
-
T = typing.TypeVar("T")
|
|
9
|
+
T = typing.TypeVar("T", bound=Model)
|
|
7
10
|
|
|
8
11
|
|
|
9
12
|
class ABCHandler(ABC, typing.Generic[T]):
|
|
10
13
|
is_blocking: bool
|
|
11
|
-
ctx:
|
|
14
|
+
ctx: Context
|
|
12
15
|
|
|
13
16
|
@abstractmethod
|
|
14
17
|
async def run(self, event: T) -> typing.Any:
|
|
15
18
|
pass
|
|
16
19
|
|
|
17
20
|
@abstractmethod
|
|
18
|
-
async def check(self, api: ABCAPI, event: Update) -> bool:
|
|
21
|
+
async def check(self, api: ABCAPI, event: Update, ctx: Context | None = None) -> bool:
|
|
19
22
|
pass
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
__all__ = ("ABCHandler",)
|
|
@@ -1,49 +1,58 @@
|
|
|
1
|
-
import typing
|
|
2
|
-
|
|
3
|
-
from telegrinder.bot.rules import ABCRule
|
|
4
|
-
from telegrinder.tools import magic_bundle
|
|
5
|
-
from telegrinder.types import Update
|
|
1
|
+
import typing_extensions as typing
|
|
2
|
+
|
|
6
3
|
from telegrinder.api.abc import ABCAPI
|
|
7
|
-
from .
|
|
4
|
+
from telegrinder.bot.cute_types import BaseCute
|
|
5
|
+
from telegrinder.bot.dispatch.context import Context
|
|
6
|
+
from telegrinder.bot.dispatch.process import check_rule
|
|
8
7
|
from telegrinder.modules import logger
|
|
8
|
+
from telegrinder.tools.error_handler import ABCErrorHandler, ErrorHandler
|
|
9
|
+
from telegrinder.types import Update
|
|
10
|
+
|
|
11
|
+
from .abc import ABCHandler
|
|
12
|
+
|
|
13
|
+
if typing.TYPE_CHECKING:
|
|
14
|
+
from telegrinder.bot.rules import ABCRule
|
|
9
15
|
|
|
10
|
-
|
|
16
|
+
F = typing.TypeVar("F", bound=typing.Callable[typing.Concatenate[typing.Any, ...], typing.Awaitable])
|
|
17
|
+
EventT = typing.TypeVar("EventT", bound=BaseCute)
|
|
18
|
+
ErrorHandlerT = typing.TypeVar("ErrorHandlerT", bound=ABCErrorHandler, default=ErrorHandler)
|
|
11
19
|
|
|
12
20
|
|
|
13
|
-
class FuncHandler(ABCHandler, typing.Generic[
|
|
21
|
+
class FuncHandler(ABCHandler[EventT], typing.Generic[EventT, F, ErrorHandlerT]):
|
|
14
22
|
def __init__(
|
|
15
23
|
self,
|
|
16
|
-
func:
|
|
17
|
-
rules:
|
|
24
|
+
func: F,
|
|
25
|
+
rules: list["ABCRule[EventT]"],
|
|
18
26
|
is_blocking: bool = True,
|
|
19
|
-
dataclass:
|
|
27
|
+
dataclass: type[typing.Any] | None = dict,
|
|
28
|
+
error_handler: ErrorHandlerT | None = None,
|
|
20
29
|
):
|
|
21
30
|
self.func = func
|
|
22
31
|
self.is_blocking = is_blocking
|
|
23
32
|
self.rules = rules
|
|
24
33
|
self.dataclass = dataclass
|
|
25
|
-
self.
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
34
|
+
self.error_handler: ErrorHandlerT = error_handler or ErrorHandler() # type: ignore
|
|
35
|
+
self.ctx = Context()
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def on_error(self):
|
|
39
|
+
return self.error_handler.catch
|
|
40
|
+
|
|
41
|
+
async def check(self, api: ABCAPI, event: Update, ctx: Context | None = None) -> bool:
|
|
42
|
+
ctx = ctx or Context()
|
|
43
|
+
preset_ctx = self.ctx.copy()
|
|
44
|
+
self.ctx |= ctx
|
|
29
45
|
for rule in self.rules:
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
else:
|
|
34
|
-
event_dict = event.to_dict()
|
|
35
|
-
if event_dict.get(rule.__event__.name) is None:
|
|
36
|
-
continue
|
|
37
|
-
e = rule.__event__.dataclass(
|
|
38
|
-
**event_dict[rule.__event__.name].to_dict(),
|
|
39
|
-
api=api,
|
|
40
|
-
)
|
|
41
|
-
if not await rule.run_check(e, self.ctx):
|
|
42
|
-
logger.debug("Rule {} failed", rule)
|
|
46
|
+
if not await check_rule(api, rule, event, self.ctx):
|
|
47
|
+
logger.debug("Rule {!r} failed!", rule)
|
|
48
|
+
self.ctx = preset_ctx
|
|
43
49
|
return False
|
|
44
50
|
return True
|
|
45
51
|
|
|
46
|
-
async def run(self, event:
|
|
47
|
-
if self.dataclass:
|
|
48
|
-
event = self.dataclass(**event)
|
|
49
|
-
return await self.func
|
|
52
|
+
async def run(self, event: EventT) -> typing.Any:
|
|
53
|
+
if self.dataclass is not None:
|
|
54
|
+
event = self.dataclass(**event.to_dict())
|
|
55
|
+
return (await self.error_handler.run(self.func, event, event.api, self.ctx)).unwrap()
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
__all__ = ("FuncHandler",)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
|
|
3
|
+
from telegrinder.api.abc import ABCAPI
|
|
4
|
+
from telegrinder.bot.cute_types import MessageCute
|
|
5
|
+
from telegrinder.bot.dispatch.context import Context
|
|
6
|
+
from telegrinder.bot.dispatch.process import check_rule
|
|
7
|
+
from telegrinder.bot.rules.abc import ABCRule
|
|
8
|
+
from telegrinder.modules import logger
|
|
9
|
+
from telegrinder.msgspec_utils import Nothing
|
|
10
|
+
from telegrinder.types.objects import Update
|
|
11
|
+
|
|
12
|
+
from .abc import ABCHandler
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class MessageReplyHandler(ABCHandler[MessageCute]):
|
|
16
|
+
def __init__(
|
|
17
|
+
self,
|
|
18
|
+
text: str,
|
|
19
|
+
*rules: ABCRule[MessageCute],
|
|
20
|
+
as_reply: bool = False,
|
|
21
|
+
is_blocking: bool = True,
|
|
22
|
+
):
|
|
23
|
+
self.text = text
|
|
24
|
+
self.rules = list(rules)
|
|
25
|
+
self.as_reply = as_reply
|
|
26
|
+
self.is_blocking = is_blocking
|
|
27
|
+
self.dataclass = MessageCute
|
|
28
|
+
self.ctx = Context()
|
|
29
|
+
|
|
30
|
+
async def check(self, api: ABCAPI, event: Update, ctx: Context | None = None) -> bool:
|
|
31
|
+
ctx = ctx or Context()
|
|
32
|
+
self.ctx |= ctx
|
|
33
|
+
for rule in self.rules:
|
|
34
|
+
if not await check_rule(api, rule, event, self.ctx):
|
|
35
|
+
logger.debug("Rule {!r} failed!", rule)
|
|
36
|
+
return False
|
|
37
|
+
return True
|
|
38
|
+
|
|
39
|
+
async def run(self, event: MessageCute) -> typing.Any:
|
|
40
|
+
await event.answer(
|
|
41
|
+
text=self.text,
|
|
42
|
+
reply_to_message_id=(event.message_id if self.as_reply else Nothing),
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
__all__ = ("MessageReplyHandler",)
|
|
@@ -1,12 +1,18 @@
|
|
|
1
|
-
from abc import ABC
|
|
2
1
|
import typing
|
|
2
|
+
from abc import ABC
|
|
3
|
+
|
|
4
|
+
from telegrinder.bot.cute_types.base import BaseCute
|
|
5
|
+
from telegrinder.bot.dispatch.context import Context
|
|
3
6
|
|
|
4
|
-
T = typing.TypeVar("T")
|
|
7
|
+
T = typing.TypeVar("T", bound=BaseCute)
|
|
5
8
|
|
|
6
9
|
|
|
7
10
|
class ABCMiddleware(ABC, typing.Generic[T]):
|
|
8
|
-
async def pre(self, event: T, ctx:
|
|
11
|
+
async def pre(self, event: T, ctx: Context) -> bool:
|
|
9
12
|
...
|
|
10
13
|
|
|
11
|
-
async def post(self, event: T, responses:
|
|
14
|
+
async def post(self, event: T, responses: list[typing.Any], ctx: Context):
|
|
12
15
|
...
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
__all__ = ("ABCMiddleware",)
|
|
@@ -1,61 +1,34 @@
|
|
|
1
1
|
import typing
|
|
2
2
|
|
|
3
|
-
from .
|
|
4
|
-
from .middleware.abc import ABCMiddleware
|
|
5
|
-
from .handler.abc import ABCHandler
|
|
6
|
-
from telegrinder.types import Update
|
|
7
|
-
from telegrinder.modules import logger
|
|
8
|
-
|
|
9
|
-
T = typing.TypeVar("T")
|
|
10
|
-
E = typing.TypeVar("E")
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
async def process_waiters(
|
|
14
|
-
waiters: typing.Dict[T, Waiter],
|
|
15
|
-
key: T,
|
|
16
|
-
event: typing.Optional[E],
|
|
17
|
-
raw_event: dict,
|
|
18
|
-
str_handler: typing.Callable,
|
|
19
|
-
) -> bool:
|
|
20
|
-
if key not in waiters:
|
|
21
|
-
return False
|
|
22
|
-
|
|
23
|
-
logger.debug(
|
|
24
|
-
"update {} found in waiter (key={})", event.__class__.__name__, str(key)
|
|
25
|
-
)
|
|
3
|
+
from fntypes.result import Error
|
|
26
4
|
|
|
27
|
-
|
|
28
|
-
|
|
5
|
+
from telegrinder.api.abc import ABCAPI
|
|
6
|
+
from telegrinder.bot.cute_types import BaseCute
|
|
7
|
+
from telegrinder.bot.dispatch.context import Context
|
|
8
|
+
from telegrinder.modules import logger
|
|
9
|
+
from telegrinder.tools.i18n.base import I18nEnum
|
|
10
|
+
from telegrinder.types import Update
|
|
29
11
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
if rule.__event__ is None:
|
|
33
|
-
chk_event = raw_event
|
|
34
|
-
if not await rule.run_check(chk_event, ctx):
|
|
35
|
-
if not waiter.default:
|
|
36
|
-
return True
|
|
37
|
-
elif isinstance(waiter.default, str):
|
|
38
|
-
await str_handler(waiter.default)
|
|
39
|
-
else:
|
|
40
|
-
await waiter.default(event)
|
|
41
|
-
return True
|
|
12
|
+
from .middleware.abc import ABCMiddleware
|
|
13
|
+
from .return_manager.abc import ABCReturnManager
|
|
42
14
|
|
|
43
|
-
|
|
15
|
+
if typing.TYPE_CHECKING:
|
|
16
|
+
from telegrinder.bot.dispatch.handler.abc import ABCHandler
|
|
17
|
+
from telegrinder.bot.rules.abc import ABCRule
|
|
44
18
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
waiter.event.set()
|
|
48
|
-
return True
|
|
19
|
+
T = typing.TypeVar("T", bound=BaseCute)
|
|
20
|
+
_ = typing.Any
|
|
49
21
|
|
|
50
22
|
|
|
51
23
|
async def process_inner(
|
|
52
24
|
event: T,
|
|
53
25
|
raw_event: Update,
|
|
54
|
-
middlewares:
|
|
55
|
-
handlers:
|
|
26
|
+
middlewares: list[ABCMiddleware[T]],
|
|
27
|
+
handlers: list["ABCHandler[T]"],
|
|
28
|
+
return_manager: ABCReturnManager[T],
|
|
56
29
|
) -> bool:
|
|
57
|
-
logger.debug("
|
|
58
|
-
ctx =
|
|
30
|
+
logger.debug("Processing {!r}...", event.__class__.__name__)
|
|
31
|
+
ctx = Context(raw_update=raw_event)
|
|
59
32
|
|
|
60
33
|
for middleware in middlewares:
|
|
61
34
|
if await middleware.pre(event, ctx) is False:
|
|
@@ -64,12 +37,12 @@ async def process_inner(
|
|
|
64
37
|
found = False
|
|
65
38
|
responses = []
|
|
66
39
|
for handler in handlers:
|
|
67
|
-
|
|
68
|
-
if result:
|
|
69
|
-
handler.ctx.update(ctx)
|
|
40
|
+
if await handler.check(event.api, raw_event, ctx):
|
|
70
41
|
found = True
|
|
42
|
+
handler.ctx |= ctx
|
|
71
43
|
response = await handler.run(event)
|
|
72
44
|
responses.append(response)
|
|
45
|
+
await return_manager.run(response, event, ctx)
|
|
73
46
|
if handler.is_blocking:
|
|
74
47
|
break
|
|
75
48
|
|
|
@@ -77,3 +50,34 @@ async def process_inner(
|
|
|
77
50
|
await middleware.post(event, responses, ctx)
|
|
78
51
|
|
|
79
52
|
return found
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
async def check_rule(
|
|
56
|
+
api: ABCAPI,
|
|
57
|
+
rule: "ABCRule",
|
|
58
|
+
update: Update,
|
|
59
|
+
ctx: Context,
|
|
60
|
+
) -> bool:
|
|
61
|
+
"""Checks requirements, adapts update.
|
|
62
|
+
Returns check result."""
|
|
63
|
+
|
|
64
|
+
cute_model = await rule.adapter.adapt(api, update)
|
|
65
|
+
match cute_model:
|
|
66
|
+
case Error(err):
|
|
67
|
+
logger.debug("Adapter failed with error message: {!r}", str(err))
|
|
68
|
+
return False
|
|
69
|
+
|
|
70
|
+
ctx_copy = ctx.copy()
|
|
71
|
+
for requirement in rule.requires:
|
|
72
|
+
if not await check_rule(api, requirement, update, ctx_copy):
|
|
73
|
+
return False
|
|
74
|
+
|
|
75
|
+
ctx |= ctx_copy
|
|
76
|
+
|
|
77
|
+
if I18nEnum.I18N in ctx:
|
|
78
|
+
rule = await rule.translate(ctx.get(I18nEnum.I18N))
|
|
79
|
+
|
|
80
|
+
return await rule.check(cute_model.unwrap(), ctx)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
__all__ = ("check_rule", "process_inner")
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from .abc import (
|
|
2
|
+
ABCReturnManager,
|
|
3
|
+
BaseReturnManager,
|
|
4
|
+
Manager,
|
|
5
|
+
register_manager,
|
|
6
|
+
)
|
|
7
|
+
from .callback_query import CallbackQueryReturnManager
|
|
8
|
+
from .inline_query import InlineQueryReturnManager
|
|
9
|
+
from .message import MessageReturnManager
|
|
10
|
+
|
|
11
|
+
__all__ = (
|
|
12
|
+
"ABCReturnManager",
|
|
13
|
+
"BaseReturnManager",
|
|
14
|
+
"CallbackQueryReturnManager",
|
|
15
|
+
"InlineQueryReturnManager",
|
|
16
|
+
"Manager",
|
|
17
|
+
"MessageReturnManager",
|
|
18
|
+
"register_manager",
|
|
19
|
+
)
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
import types
|
|
3
|
+
import typing
|
|
4
|
+
from abc import ABC, abstractmethod
|
|
5
|
+
|
|
6
|
+
from telegrinder.bot.cute_types import BaseCute
|
|
7
|
+
from telegrinder.bot.dispatch.context import Context
|
|
8
|
+
from telegrinder.modules import logger
|
|
9
|
+
|
|
10
|
+
T = typing.TypeVar("T")
|
|
11
|
+
EventT = typing.TypeVar("EventT", bound=BaseCute, contravariant=True)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def get_union_types(t: types.UnionType) -> tuple[type, ...] | None:
|
|
15
|
+
if type(t) in (types.UnionType, typing._UnionGenericAlias): # type: ignore
|
|
16
|
+
return tuple(typing.get_origin(x) or x for x in typing.get_args(t))
|
|
17
|
+
return None
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def register_manager(return_type: type | types.UnionType):
|
|
21
|
+
def wrapper(func: typing.Callable[..., typing.Awaitable]):
|
|
22
|
+
return Manager(get_union_types(return_type) or (return_type,), func) # type: ignore
|
|
23
|
+
|
|
24
|
+
return wrapper
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclasses.dataclass(frozen=True)
|
|
28
|
+
class Manager:
|
|
29
|
+
types: tuple[type, ...]
|
|
30
|
+
callback: typing.Callable[..., typing.Awaitable]
|
|
31
|
+
|
|
32
|
+
async def __call__(self, *args: typing.Any, **kwargs: typing.Any) -> None:
|
|
33
|
+
try:
|
|
34
|
+
await self.callback(*args, **kwargs)
|
|
35
|
+
except BaseException as ex:
|
|
36
|
+
logger.exception(ex)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class ABCReturnManager(ABC, typing.Generic[EventT]):
|
|
40
|
+
@abstractmethod
|
|
41
|
+
async def run(self, response: typing.Any, event: EventT, ctx: Context) -> None:
|
|
42
|
+
pass
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class BaseReturnManager(ABCReturnManager[EventT]):
|
|
46
|
+
@property
|
|
47
|
+
def managers(self) -> list[Manager]:
|
|
48
|
+
return [
|
|
49
|
+
manager
|
|
50
|
+
for manager in (vars(BaseReturnManager) | vars(self.__class__)).values()
|
|
51
|
+
if isinstance(manager, Manager)
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
@register_manager(Context)
|
|
55
|
+
@staticmethod
|
|
56
|
+
async def ctx_manager(value: Context, event: EventT, ctx: Context) -> None:
|
|
57
|
+
"""Basic manager for returning context from handler."""
|
|
58
|
+
|
|
59
|
+
ctx.update(value)
|
|
60
|
+
|
|
61
|
+
async def run(self, response: typing.Any, event: EventT, ctx: Context) -> None:
|
|
62
|
+
for manager in self.managers:
|
|
63
|
+
if typing.Any in manager.types or any(type(response) is x for x in manager.types):
|
|
64
|
+
await manager(response, event, ctx)
|
|
65
|
+
|
|
66
|
+
@typing.overload
|
|
67
|
+
def register_manager(self, return_type: type[T]) -> typing.Callable[
|
|
68
|
+
[typing.Callable[[T, EventT, Context], typing.Awaitable]], Manager
|
|
69
|
+
]:
|
|
70
|
+
...
|
|
71
|
+
|
|
72
|
+
@typing.overload
|
|
73
|
+
def register_manager(self, return_type: tuple[type[T], ...]) -> typing.Callable[
|
|
74
|
+
[typing.Callable[[tuple[T, ...], EventT, Context], typing.Awaitable]], Manager
|
|
75
|
+
]:
|
|
76
|
+
...
|
|
77
|
+
|
|
78
|
+
def register_manager(self, return_type: type[T] | tuple[type[T], ...]) -> typing.Callable[
|
|
79
|
+
[typing.Callable[[T | tuple[T, ...], EventT, Context], typing.Awaitable]], Manager
|
|
80
|
+
]:
|
|
81
|
+
def wrapper(func: typing.Callable[[T, EventT, Context], typing.Awaitable]) -> Manager:
|
|
82
|
+
manager = Manager(get_union_types(return_type) or (return_type,), func) # type: ignore
|
|
83
|
+
setattr(self.__class__, func.__name__, manager)
|
|
84
|
+
return manager
|
|
85
|
+
|
|
86
|
+
return wrapper
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
__all__ = (
|
|
90
|
+
"ABCReturnManager",
|
|
91
|
+
"BaseReturnManager",
|
|
92
|
+
"Manager",
|
|
93
|
+
"register_manager",
|
|
94
|
+
"get_union_types",
|
|
95
|
+
)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from telegrinder.bot.cute_types import CallbackQueryCute
|
|
2
|
+
from telegrinder.bot.dispatch.context import Context
|
|
3
|
+
|
|
4
|
+
from .abc import BaseReturnManager, register_manager
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class CallbackQueryReturnManager(BaseReturnManager[CallbackQueryCute]):
|
|
8
|
+
@register_manager(str)
|
|
9
|
+
@staticmethod
|
|
10
|
+
async def str_manager(value: str, event: CallbackQueryCute, ctx: Context) -> None:
|
|
11
|
+
await event.answer(value)
|
|
12
|
+
|
|
13
|
+
@register_manager(dict)
|
|
14
|
+
@staticmethod
|
|
15
|
+
async def dict_manager(value: dict, event: CallbackQueryCute, ctx: Context) -> None:
|
|
16
|
+
await event.answer(**value)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
__all__ = ("CallbackQueryReturnManager",)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from telegrinder.bot.cute_types import InlineQueryCute
|
|
2
|
+
from telegrinder.bot.dispatch.context import Context
|
|
3
|
+
|
|
4
|
+
from .abc import BaseReturnManager, register_manager
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class InlineQueryReturnManager(BaseReturnManager[InlineQueryCute]):
|
|
8
|
+
@register_manager(dict)
|
|
9
|
+
@staticmethod
|
|
10
|
+
async def dict_manager(value: dict, event: InlineQueryCute, ctx: Context) -> None:
|
|
11
|
+
await event.answer(**value)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
__all__ = ("InlineQueryReturnManager",)
|