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