telegrinder 0.1.dev20__py3-none-any.whl → 0.1.dev159__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 +47 -28
- 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 +138 -0
- telegrinder/bot/cute_types/callback_query.py +458 -15
- telegrinder/bot/cute_types/inline_query.py +30 -24
- telegrinder/bot/cute_types/message.py +2982 -78
- telegrinder/bot/cute_types/update.py +30 -0
- telegrinder/bot/cute_types/utils.py +794 -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 +128 -2
- telegrinder/bot/dispatch/view/box.py +38 -0
- telegrinder/bot/dispatch/view/callback_query.py +13 -39
- telegrinder/bot/dispatch/view/inline_query.py +11 -39
- telegrinder/bot/dispatch/view/message.py +11 -47
- 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 +91 -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 +49 -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 +25 -13
- 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 +3 -1
- telegrinder/client/abc.py +26 -16
- telegrinder/client/aiohttp.py +54 -32
- telegrinder/model.py +119 -40
- telegrinder/modules.py +189 -21
- telegrinder/msgspec_json.py +14 -0
- telegrinder/msgspec_utils.py +227 -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 +138 -4
- telegrinder/tools/buttons.py +89 -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/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 +653 -0
- telegrinder/types/methods.py +4107 -1279
- telegrinder/types/objects.py +4771 -1745
- {telegrinder-0.1.dev20.dist-info → telegrinder-0.1.dev159.dist-info}/LICENSE +2 -1
- telegrinder-0.1.dev159.dist-info/METADATA +109 -0
- telegrinder-0.1.dev159.dist-info/RECORD +126 -0
- {telegrinder-0.1.dev20.dist-info → telegrinder-0.1.dev159.dist-info}/WHEEL +1 -1
- telegrinder/bot/dispatch/waiter.py +0 -38
- telegrinder/result.py +0 -38
- telegrinder/tools/formatting/abc.py +0 -52
- telegrinder/tools/formatting/markdown.py +0 -57
- telegrinder-0.1.dev20.dist-info/METADATA +0 -22
- telegrinder-0.1.dev20.dist-info/RECORD +0 -71
|
@@ -1,5 +1,58 @@
|
|
|
1
1
|
from .abc import ABCDispatch
|
|
2
|
-
from .
|
|
3
|
-
from .
|
|
2
|
+
from .composition import CompositionDispatch
|
|
3
|
+
from .context import Context
|
|
4
|
+
from .dispatch import ABCRule, Dispatch, TelegrinderCtx
|
|
5
|
+
from .handler import ABCHandler, FuncHandler, MessageReplyHandler
|
|
4
6
|
from .middleware import ABCMiddleware
|
|
5
|
-
from .
|
|
7
|
+
from .process import check_rule, process_inner
|
|
8
|
+
from .return_manager import (
|
|
9
|
+
ABCReturnManager,
|
|
10
|
+
BaseReturnManager,
|
|
11
|
+
CallbackQueryReturnManager,
|
|
12
|
+
InlineQueryReturnManager,
|
|
13
|
+
Manager,
|
|
14
|
+
MessageReturnManager,
|
|
15
|
+
register_manager,
|
|
16
|
+
)
|
|
17
|
+
from .view import (
|
|
18
|
+
ABCStateView,
|
|
19
|
+
ABCView,
|
|
20
|
+
BaseStateView,
|
|
21
|
+
BaseView,
|
|
22
|
+
CallbackQueryView,
|
|
23
|
+
InlineQueryView,
|
|
24
|
+
MessageView,
|
|
25
|
+
ViewBox,
|
|
26
|
+
)
|
|
27
|
+
from .waiter_machine import WaiterMachine
|
|
28
|
+
|
|
29
|
+
__all__ = (
|
|
30
|
+
"ABCDispatch",
|
|
31
|
+
"ABCHandler",
|
|
32
|
+
"ABCMiddleware",
|
|
33
|
+
"ABCReturnManager",
|
|
34
|
+
"ABCRule",
|
|
35
|
+
"ABCStateView",
|
|
36
|
+
"ABCView",
|
|
37
|
+
"BaseReturnManager",
|
|
38
|
+
"BaseStateView",
|
|
39
|
+
"BaseView",
|
|
40
|
+
"CallbackQueryReturnManager",
|
|
41
|
+
"CallbackQueryView",
|
|
42
|
+
"CompositionDispatch",
|
|
43
|
+
"Context",
|
|
44
|
+
"Dispatch",
|
|
45
|
+
"FuncHandler",
|
|
46
|
+
"InlineQueryReturnManager",
|
|
47
|
+
"InlineQueryView",
|
|
48
|
+
"Manager",
|
|
49
|
+
"MessageReplyHandler",
|
|
50
|
+
"MessageReturnManager",
|
|
51
|
+
"MessageView",
|
|
52
|
+
"TelegrinderCtx",
|
|
53
|
+
"ViewBox",
|
|
54
|
+
"WaiterMachine",
|
|
55
|
+
"check_rule",
|
|
56
|
+
"process_inner",
|
|
57
|
+
"register_manager",
|
|
58
|
+
)
|
telegrinder/bot/dispatch/abc.py
CHANGED
|
@@ -1,19 +1,21 @@
|
|
|
1
|
+
import typing
|
|
1
2
|
from abc import ABC, abstractmethod
|
|
3
|
+
|
|
2
4
|
from telegrinder.api.abc import ABCAPI
|
|
5
|
+
from telegrinder.tools.global_context import ABCGlobalContext
|
|
3
6
|
from telegrinder.types import Update
|
|
4
|
-
from .view.abc import ABCView
|
|
5
|
-
import typing
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
class ABCDispatch(ABC):
|
|
9
|
-
|
|
10
|
-
def feed(self, event: Update, api: ABCAPI) -> bool:
|
|
11
|
-
pass
|
|
10
|
+
global_context: ABCGlobalContext
|
|
12
11
|
|
|
13
12
|
@abstractmethod
|
|
14
|
-
def
|
|
13
|
+
async def feed(self, event: Update, api: ABCAPI) -> bool:
|
|
15
14
|
pass
|
|
16
15
|
|
|
17
16
|
@abstractmethod
|
|
18
|
-
def
|
|
17
|
+
def load(self, external: typing.Self):
|
|
19
18
|
pass
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
__all__ = ("ABCDispatch",)
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import inspect
|
|
2
|
+
import typing
|
|
3
|
+
|
|
4
|
+
from telegrinder.api.abc import ABCAPI
|
|
5
|
+
from telegrinder.bot.cute_types import UpdateCute
|
|
6
|
+
from telegrinder.bot.dispatch.abc import ABCDispatch
|
|
7
|
+
from telegrinder.node import (
|
|
8
|
+
ComposeError,
|
|
9
|
+
ContainerNode,
|
|
10
|
+
Node,
|
|
11
|
+
NodeCollection,
|
|
12
|
+
NodeSession,
|
|
13
|
+
compose_node,
|
|
14
|
+
)
|
|
15
|
+
from telegrinder.tools import magic_bundle
|
|
16
|
+
from telegrinder.types import Update
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Composition:
|
|
20
|
+
nodes: dict[str, type[Node]]
|
|
21
|
+
|
|
22
|
+
def __init__(self, func: typing.Callable, is_blocking: bool) -> None:
|
|
23
|
+
self.func = func
|
|
24
|
+
self.nodes = {
|
|
25
|
+
name: parameter.annotation
|
|
26
|
+
for name, parameter in inspect.signature(func).parameters.items()
|
|
27
|
+
}
|
|
28
|
+
self.is_blocking = is_blocking
|
|
29
|
+
|
|
30
|
+
async def compose_nodes(self, update: UpdateCute) -> NodeCollection | None:
|
|
31
|
+
nodes: dict[str, NodeSession] = {}
|
|
32
|
+
for name, node_t in self.nodes.items():
|
|
33
|
+
try:
|
|
34
|
+
nodes[name] = await compose_node(node_t, update)
|
|
35
|
+
except ComposeError as err:
|
|
36
|
+
await NodeCollection(nodes).close_all()
|
|
37
|
+
return None
|
|
38
|
+
return NodeCollection(nodes)
|
|
39
|
+
|
|
40
|
+
async def __call__(self, **kwargs) -> typing.Any:
|
|
41
|
+
return await self.func(**magic_bundle(self.func, kwargs, start_idx=0, bundle_ctx=False)) # type: ignore
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class CompositionDispatch(ABCDispatch):
|
|
45
|
+
def __init__(self) -> None:
|
|
46
|
+
self.compositions: list[Composition] = []
|
|
47
|
+
|
|
48
|
+
async def feed(self, event: Update, api: ABCAPI) -> bool:
|
|
49
|
+
update = UpdateCute(**event.to_dict(), api=api)
|
|
50
|
+
is_found = False
|
|
51
|
+
for composition in self.compositions:
|
|
52
|
+
nodes = await composition.compose_nodes(update)
|
|
53
|
+
if nodes is not None:
|
|
54
|
+
result = await composition(**nodes.values())
|
|
55
|
+
await nodes.close_all(with_value=result)
|
|
56
|
+
if composition.is_blocking:
|
|
57
|
+
return True
|
|
58
|
+
is_found = True
|
|
59
|
+
return is_found
|
|
60
|
+
|
|
61
|
+
def load(self, external: typing.Self):
|
|
62
|
+
self.compositions.extend(external.compositions)
|
|
63
|
+
|
|
64
|
+
def __call__(self, *container_nodes: type[Node], is_blocking: bool = True):
|
|
65
|
+
def wrapper(func: typing.Callable):
|
|
66
|
+
composition = Composition(func, is_blocking)
|
|
67
|
+
if container_nodes:
|
|
68
|
+
composition.nodes["container"] = ContainerNode.link_nodes(list(container_nodes))
|
|
69
|
+
self.compositions.append(composition)
|
|
70
|
+
return func
|
|
71
|
+
return wrapper
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
__all__ = ("Composition", "CompositionDispatch")
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import enum
|
|
2
|
+
import typing
|
|
3
|
+
|
|
4
|
+
from telegrinder.types import Update
|
|
5
|
+
|
|
6
|
+
T = typing.TypeVar("T")
|
|
7
|
+
|
|
8
|
+
Key: typing.TypeAlias = str | enum.Enum
|
|
9
|
+
AnyValue: typing.TypeAlias = typing.Any
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@typing.dataclass_transform(kw_only_default=True, order_default=True)
|
|
13
|
+
class Context(dict[str, AnyValue]):
|
|
14
|
+
"""Context class for rules and middlewares.
|
|
15
|
+
```
|
|
16
|
+
class MyRule(ABCRule[T]):
|
|
17
|
+
adapter: ABCAdapter[Update, T] = RawUpdateAdapter()
|
|
18
|
+
|
|
19
|
+
async def check(self, event: T, ctx: Context) -> bool:
|
|
20
|
+
ctx.set("value", (await event.ctx_api.get_me()).unwrap())
|
|
21
|
+
return True
|
|
22
|
+
```
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
raw_update: Update
|
|
26
|
+
|
|
27
|
+
def __init__(self, **kwargs: AnyValue) -> None:
|
|
28
|
+
cls_vars = vars(self.__class__)
|
|
29
|
+
defaults = {}
|
|
30
|
+
for k in self.__class__.__annotations__:
|
|
31
|
+
if k in cls_vars:
|
|
32
|
+
defaults[k] = cls_vars[k]
|
|
33
|
+
delattr(self.__class__, k)
|
|
34
|
+
dict.__init__(self, **defaults | kwargs)
|
|
35
|
+
|
|
36
|
+
def __setitem__(self, __key: Key, __value: AnyValue) -> None:
|
|
37
|
+
dict.__setitem__(self, self.key_to_str(__key), __value)
|
|
38
|
+
|
|
39
|
+
def __getitem__(self, __key: Key) -> AnyValue:
|
|
40
|
+
return dict.__getitem__(self, self.key_to_str(__key))
|
|
41
|
+
|
|
42
|
+
def __delitem__(self, __key: Key) -> None:
|
|
43
|
+
dict.__delitem__(self, self.key_to_str(__key))
|
|
44
|
+
|
|
45
|
+
def __setattr__(self, __name: str, __value: AnyValue) -> None:
|
|
46
|
+
self.__setitem__(__name, __value)
|
|
47
|
+
|
|
48
|
+
def __getattr__(self, __name: str) -> AnyValue:
|
|
49
|
+
return self.__getitem__(__name)
|
|
50
|
+
|
|
51
|
+
def __delattr__(self, __name: str) -> None:
|
|
52
|
+
self.__delitem__(__name)
|
|
53
|
+
|
|
54
|
+
@staticmethod
|
|
55
|
+
def key_to_str(key: Key) -> str:
|
|
56
|
+
return key if isinstance(key, str) else str(key.value)
|
|
57
|
+
|
|
58
|
+
def copy(self) -> typing.Self:
|
|
59
|
+
return self.__class__(**self)
|
|
60
|
+
|
|
61
|
+
def set(self, key: Key, value: AnyValue) -> None:
|
|
62
|
+
self[key] = value
|
|
63
|
+
|
|
64
|
+
def get(self, key: Key, default: T | None = None) -> T | AnyValue:
|
|
65
|
+
return dict.get(self, key, default)
|
|
66
|
+
|
|
67
|
+
def delete(self, key: Key) -> None:
|
|
68
|
+
del self[key]
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
__all__ = ("Context",)
|
|
@@ -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",)
|