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,116 +1,10 @@
|
|
|
1
|
-
import typing
|
|
2
|
-
|
|
3
|
-
from telegrinder.api.api import API
|
|
4
|
-
from telegrinder.bot.cute_types.base import BaseCute
|
|
5
|
-
from telegrinder.bot.cute_types.update import UpdateCute
|
|
6
|
-
from telegrinder.bot.dispatch.context import Context
|
|
7
|
-
from telegrinder.bot.dispatch.handler.func import Func, FuncHandler
|
|
8
|
-
from telegrinder.bot.dispatch.process import process_inner
|
|
9
1
|
from telegrinder.bot.dispatch.view.abc import ABCEventRawView
|
|
10
2
|
from telegrinder.bot.dispatch.view.base import BaseView
|
|
11
|
-
from telegrinder.bot.rules.abc import ABCRule
|
|
12
|
-
from telegrinder.tools.error_handler.error_handler import ABCErrorHandler, ErrorHandler
|
|
13
|
-
from telegrinder.types.enums import UpdateType
|
|
14
|
-
from telegrinder.types.objects import Update
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class RawEventView(ABCEventRawView[UpdateCute], BaseView[UpdateCute]):
|
|
18
|
-
@typing.overload
|
|
19
|
-
def __call__[**P, R](
|
|
20
|
-
self,
|
|
21
|
-
*rules: ABCRule,
|
|
22
|
-
update_type: UpdateType,
|
|
23
|
-
final: bool = True,
|
|
24
|
-
) -> typing.Callable[
|
|
25
|
-
[Func[P, R]],
|
|
26
|
-
FuncHandler[BaseCute[typing.Any], Func[P, R], ErrorHandler[BaseCute[typing.Any]]],
|
|
27
|
-
]: ...
|
|
28
|
-
|
|
29
|
-
@typing.overload
|
|
30
|
-
def __call__[**P, Dataclass, R](
|
|
31
|
-
self,
|
|
32
|
-
*rules: ABCRule,
|
|
33
|
-
dataclass: type[Dataclass],
|
|
34
|
-
final: bool = True,
|
|
35
|
-
) -> typing.Callable[[Func[P, R]], FuncHandler[Dataclass, Func[P, R], ErrorHandler[Dataclass]]]: ...
|
|
36
|
-
|
|
37
|
-
@typing.overload
|
|
38
|
-
def __call__[**P, Dataclass, R](
|
|
39
|
-
self,
|
|
40
|
-
*rules: ABCRule,
|
|
41
|
-
update_type: UpdateType,
|
|
42
|
-
dataclass: type[Dataclass],
|
|
43
|
-
final: bool = True,
|
|
44
|
-
) -> typing.Callable[[Func[P, R]], FuncHandler[Dataclass, Func[P, R], ErrorHandler[Dataclass]]]: ...
|
|
45
|
-
|
|
46
|
-
@typing.overload
|
|
47
|
-
def __call__[**P, ErrorHandlerT: ABCErrorHandler, R](
|
|
48
|
-
self,
|
|
49
|
-
*rules: ABCRule,
|
|
50
|
-
error_handler: ErrorHandlerT,
|
|
51
|
-
final: bool = True,
|
|
52
|
-
) -> typing.Callable[
|
|
53
|
-
[Func[P, R]],
|
|
54
|
-
FuncHandler[BaseCute[typing.Any], Func[P, R], ErrorHandlerT],
|
|
55
|
-
]: ...
|
|
56
|
-
|
|
57
|
-
@typing.overload
|
|
58
|
-
def __call__[**P, ErrorHandlerT: ABCErrorHandler, R](
|
|
59
|
-
self,
|
|
60
|
-
*rules: ABCRule,
|
|
61
|
-
error_handler: ErrorHandlerT,
|
|
62
|
-
update_type: UpdateType,
|
|
63
|
-
final: bool = True,
|
|
64
|
-
) -> typing.Callable[
|
|
65
|
-
[Func[P, R]],
|
|
66
|
-
FuncHandler[BaseCute[typing.Any], Func[P, R], ErrorHandlerT],
|
|
67
|
-
]: ...
|
|
68
|
-
|
|
69
|
-
@typing.overload
|
|
70
|
-
def __call__[**P, Dataclass, ErrorHandlerT: ABCErrorHandler, R](
|
|
71
|
-
self,
|
|
72
|
-
*rules: ABCRule,
|
|
73
|
-
update_type: UpdateType,
|
|
74
|
-
dataclass: type[Dataclass],
|
|
75
|
-
error_handler: ErrorHandlerT,
|
|
76
|
-
final: bool = True,
|
|
77
|
-
) -> typing.Callable[[Func[P, R]], FuncHandler[Dataclass, Func[P, R], ErrorHandlerT]]: ...
|
|
78
|
-
|
|
79
|
-
def __call__(
|
|
80
|
-
self,
|
|
81
|
-
*rules: ABCRule,
|
|
82
|
-
update_type: UpdateType | None = None,
|
|
83
|
-
dataclass: type[typing.Any] | None = None,
|
|
84
|
-
error_handler: ABCErrorHandler | None = None,
|
|
85
|
-
final: bool = True,
|
|
86
|
-
) -> typing.Callable[..., typing.Any]:
|
|
87
|
-
def wrapper(func: typing.Callable[..., typing.Any]):
|
|
88
|
-
func_handler = FuncHandler(
|
|
89
|
-
func,
|
|
90
|
-
rules=[*self.auto_rules, *rules],
|
|
91
|
-
final=final,
|
|
92
|
-
dataclass=dataclass,
|
|
93
|
-
error_handler=error_handler or ErrorHandler(),
|
|
94
|
-
update_type=update_type,
|
|
95
|
-
)
|
|
96
|
-
self.handlers.append(func_handler)
|
|
97
|
-
return func_handler
|
|
98
|
-
|
|
99
|
-
return wrapper
|
|
100
3
|
|
|
101
|
-
async def check(self, event: Update) -> bool:
|
|
102
|
-
return bool(self.handlers) or bool(self.middlewares)
|
|
103
4
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
UpdateCute.from_update(event, bound_api=api),
|
|
108
|
-
event,
|
|
109
|
-
context,
|
|
110
|
-
self.middlewares,
|
|
111
|
-
self.handlers,
|
|
112
|
-
self.return_manager,
|
|
113
|
-
)
|
|
5
|
+
class RawEventView(ABCEventRawView, BaseView):
|
|
6
|
+
def __init__(self) -> None:
|
|
7
|
+
super().__init__()
|
|
114
8
|
|
|
115
9
|
|
|
116
10
|
__all__ = ("RawEventView",)
|
|
@@ -6,9 +6,8 @@ from telegrinder.bot.dispatch.waiter_machine.hasher import (
|
|
|
6
6
|
MESSAGE_FROM_USER_IN_CHAT,
|
|
7
7
|
MESSAGE_IN_CHAT,
|
|
8
8
|
Hasher,
|
|
9
|
-
StateViewHasher,
|
|
10
9
|
)
|
|
11
|
-
from telegrinder.bot.dispatch.waiter_machine.machine import WaiterMachine
|
|
10
|
+
from telegrinder.bot.dispatch.waiter_machine.machine import WaiterMachine
|
|
12
11
|
from telegrinder.bot.dispatch.waiter_machine.middleware import WaiterMiddleware
|
|
13
12
|
from telegrinder.bot.dispatch.waiter_machine.short_state import ShortState
|
|
14
13
|
|
|
@@ -16,13 +15,11 @@ __all__ = (
|
|
|
16
15
|
"CALLBACK_QUERY_FOR_MESSAGE",
|
|
17
16
|
"CALLBACK_QUERY_FROM_CHAT",
|
|
18
17
|
"CALLBACK_QUERY_IN_CHAT_FOR_MESSAGE",
|
|
19
|
-
"Hasher",
|
|
20
18
|
"MESSAGE_FROM_USER",
|
|
21
19
|
"MESSAGE_FROM_USER_IN_CHAT",
|
|
22
20
|
"MESSAGE_IN_CHAT",
|
|
21
|
+
"Hasher",
|
|
23
22
|
"ShortState",
|
|
24
|
-
"StateViewHasher",
|
|
25
23
|
"WaiterMachine",
|
|
26
24
|
"WaiterMiddleware",
|
|
27
|
-
"clear_wm_storage_worker",
|
|
28
25
|
)
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import typing
|
|
2
2
|
|
|
3
|
-
from telegrinder.bot.cute_types import BaseCute
|
|
4
3
|
from telegrinder.bot.dispatch.handler.abc import ABCHandler
|
|
5
4
|
|
|
6
5
|
from .short_state import ShortState
|
|
7
6
|
|
|
7
|
+
if typing.TYPE_CHECKING:
|
|
8
|
+
from telegrinder.bot.cute_types.base import BaseCute
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
|
|
11
|
+
class WaiterActions[Event: BaseCute[typing.Any]](typing.TypedDict):
|
|
12
|
+
on_miss: typing.NotRequired[ABCHandler]
|
|
13
|
+
on_drop: typing.NotRequired[typing.Callable[[ShortState], None]]
|
|
12
14
|
|
|
13
15
|
|
|
14
16
|
__all__ = ("WaiterActions",)
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
from .callback import CALLBACK_QUERY_FOR_MESSAGE, CALLBACK_QUERY_FROM_CHAT, CALLBACK_QUERY_IN_CHAT_FOR_MESSAGE
|
|
2
2
|
from .hasher import Hasher
|
|
3
3
|
from .message import MESSAGE_FROM_USER, MESSAGE_FROM_USER_IN_CHAT, MESSAGE_IN_CHAT
|
|
4
|
-
from .state import StateViewHasher
|
|
5
4
|
|
|
6
5
|
__all__ = (
|
|
7
6
|
"CALLBACK_QUERY_FOR_MESSAGE",
|
|
8
7
|
"CALLBACK_QUERY_FROM_CHAT",
|
|
9
8
|
"CALLBACK_QUERY_IN_CHAT_FOR_MESSAGE",
|
|
10
|
-
"Hasher",
|
|
11
9
|
"MESSAGE_FROM_USER",
|
|
12
10
|
"MESSAGE_FROM_USER_IN_CHAT",
|
|
13
11
|
"MESSAGE_IN_CHAT",
|
|
14
|
-
"
|
|
12
|
+
"Hasher",
|
|
15
13
|
)
|
|
@@ -10,7 +10,7 @@ def from_chat_hash(chat_id: int) -> int:
|
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
def get_chat_from_event(event: CallbackQuery) -> int | None:
|
|
13
|
-
return event.chat.
|
|
13
|
+
return event.chat.then(lambda chat: Some(chat.id)).unwrap_or_none()
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
def for_message_hash(message_id: int) -> int:
|
|
@@ -1,27 +1,28 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import typing
|
|
2
4
|
from functools import cached_property
|
|
3
5
|
|
|
6
|
+
from fntypes.misc import from_optional
|
|
4
7
|
from fntypes.option import Option
|
|
5
8
|
|
|
6
9
|
from telegrinder.bot.cute_types import BaseCute
|
|
7
10
|
from telegrinder.bot.dispatch.view.base import BaseView
|
|
8
|
-
|
|
11
|
+
|
|
12
|
+
type HasherWithData[Event: BaseCute, Data] = tuple[Hasher[Event, Data], Data]
|
|
9
13
|
|
|
10
14
|
Event = typing.TypeVar("Event", bound=BaseCute, covariant=True)
|
|
11
15
|
Data = typing.TypeVar("Data", covariant=True)
|
|
12
16
|
|
|
13
17
|
|
|
14
|
-
def
|
|
18
|
+
def ECHO[T](__x: T) -> T: # noqa
|
|
15
19
|
return __x
|
|
16
20
|
|
|
17
21
|
|
|
18
|
-
ECHO = _echo
|
|
19
|
-
|
|
20
|
-
|
|
21
22
|
class Hasher(typing.Generic[Event, Data]):
|
|
22
23
|
def __init__(
|
|
23
24
|
self,
|
|
24
|
-
view_class: type[BaseView
|
|
25
|
+
view_class: type[BaseView],
|
|
25
26
|
get_hash_from_data: typing.Callable[[Data], typing.Hashable | None] | None = None,
|
|
26
27
|
get_data_from_event: typing.Callable[[Event], Data | None] | None = None,
|
|
27
28
|
) -> None:
|
|
@@ -29,6 +30,9 @@ class Hasher(typing.Generic[Event, Data]):
|
|
|
29
30
|
self._get_hash_from_data = get_hash_from_data
|
|
30
31
|
self._get_data_from_event = get_data_from_event
|
|
31
32
|
|
|
33
|
+
def __call__[D](self: "Hasher[Event, D]", data: D, /) -> HasherWithData[Event, D]:
|
|
34
|
+
return (self, data)
|
|
35
|
+
|
|
32
36
|
def __hash__(self) -> int:
|
|
33
37
|
return hash(self.name)
|
|
34
38
|
|
|
@@ -53,7 +57,7 @@ class Hasher(typing.Generic[Event, Data]):
|
|
|
53
57
|
self: "Hasher[E, Data]",
|
|
54
58
|
event: E,
|
|
55
59
|
) -> Option[typing.Hashable]:
|
|
56
|
-
return self.get_data_from_event(event).
|
|
60
|
+
return self.get_data_from_event(event).then(self.get_hash_from_data) # type: ignore
|
|
57
61
|
|
|
58
62
|
|
|
59
63
|
__all__ = ("Hasher",)
|
|
File without changes
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import asyncio
|
|
2
4
|
import datetime
|
|
3
5
|
import typing
|
|
@@ -5,18 +7,19 @@ import typing
|
|
|
5
7
|
from telegrinder.bot.cute_types.base import BaseCute
|
|
6
8
|
from telegrinder.bot.dispatch.abc import ABCDispatch
|
|
7
9
|
from telegrinder.bot.dispatch.context import Context
|
|
8
|
-
from telegrinder.bot.dispatch.view.base import
|
|
9
|
-
from telegrinder.bot.dispatch.waiter_machine.middleware import
|
|
10
|
+
from telegrinder.bot.dispatch.view.base import BaseView
|
|
11
|
+
from telegrinder.bot.dispatch.waiter_machine.middleware import WaiterMiddleware
|
|
10
12
|
from telegrinder.bot.dispatch.waiter_machine.short_state import (
|
|
11
13
|
ShortState,
|
|
12
14
|
ShortStateContext,
|
|
13
15
|
)
|
|
14
16
|
from telegrinder.bot.rules.abc import ABCRule
|
|
17
|
+
from telegrinder.tools.global_context.builtin_context import TelegrinderContext
|
|
15
18
|
from telegrinder.tools.lifespan import Lifespan
|
|
16
19
|
from telegrinder.tools.limited_dict import LimitedDict
|
|
17
20
|
|
|
18
21
|
from .actions import WaiterActions
|
|
19
|
-
from .hasher import Hasher
|
|
22
|
+
from .hasher import Hasher
|
|
20
23
|
|
|
21
24
|
type Storage[Event: BaseCute, HasherData] = dict[
|
|
22
25
|
Hasher[Event, HasherData],
|
|
@@ -24,35 +27,49 @@ type Storage[Event: BaseCute, HasherData] = dict[
|
|
|
24
27
|
]
|
|
25
28
|
type HasherWithData[Event: BaseCute, Data] = tuple[Hasher[Event, Data], Data]
|
|
26
29
|
|
|
30
|
+
_NODEFAULT: typing.Any = object()
|
|
31
|
+
MAX_STORAGE_SIZE: typing.Final[int] = 10000
|
|
32
|
+
ONE_MINUTE: typing.Final[datetime.timedelta] = datetime.timedelta(minutes=1)
|
|
27
33
|
WEEK: typing.Final[datetime.timedelta] = datetime.timedelta(days=7)
|
|
34
|
+
CONTEXT: typing.Final[TelegrinderContext] = TelegrinderContext()
|
|
28
35
|
|
|
29
36
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def __call__(self, context: Context, /) -> tuple[*Ts]: ...
|
|
37
|
+
async def clear_wm_storage_worker(wm: WaiterMachine, interval: float) -> None:
|
|
38
|
+
await wm.clear_storage()
|
|
39
|
+
await asyncio.sleep(interval)
|
|
34
40
|
|
|
35
41
|
|
|
36
|
-
def unpack_to_context(context: Context) -> tuple[Context]:
|
|
42
|
+
def unpack_to_context(context: Context, /) -> tuple[Context]:
|
|
37
43
|
return (context,)
|
|
38
44
|
|
|
39
45
|
|
|
40
|
-
def no_unpack(_: Context) -> tuple[()]:
|
|
46
|
+
def no_unpack(_: Context, /) -> tuple[()]:
|
|
41
47
|
return ()
|
|
42
48
|
|
|
43
49
|
|
|
50
|
+
class ContextUnpackProto[*Ts](typing.Protocol):
|
|
51
|
+
__name__: str
|
|
52
|
+
|
|
53
|
+
def __call__(self, context: Context, /) -> tuple[*Ts]: ...
|
|
54
|
+
|
|
55
|
+
|
|
44
56
|
class WaiterMachine:
|
|
45
57
|
def __init__(
|
|
46
58
|
self,
|
|
47
59
|
dispatch: ABCDispatch | None = None,
|
|
48
60
|
*,
|
|
49
|
-
max_storage_size: int =
|
|
61
|
+
max_storage_size: int = MAX_STORAGE_SIZE,
|
|
50
62
|
base_state_lifetime: datetime.timedelta = WEEK,
|
|
63
|
+
clear_storage_interval: datetime.timedelta = ONE_MINUTE,
|
|
51
64
|
) -> None:
|
|
52
65
|
self.dispatch = dispatch
|
|
53
66
|
self.max_storage_size = max_storage_size
|
|
54
67
|
self.base_state_lifetime = base_state_lifetime
|
|
55
|
-
self.storage: Storage = {}
|
|
68
|
+
self.storage: Storage[typing.Any, typing.Any] = {}
|
|
69
|
+
|
|
70
|
+
CONTEXT.loop_wrapper.add_task(
|
|
71
|
+
clear_wm_storage_worker(self, clear_storage_interval.total_seconds()),
|
|
72
|
+
)
|
|
56
73
|
|
|
57
74
|
def __repr__(self) -> str:
|
|
58
75
|
return "<{}: with {} storage items and max_storage_size={}, base_state_lifetime={!r}>".format(
|
|
@@ -62,17 +79,12 @@ class WaiterMachine:
|
|
|
62
79
|
self.base_state_lifetime,
|
|
63
80
|
)
|
|
64
81
|
|
|
65
|
-
def create_middleware[Event: BaseCute](self, view: BaseStateView[Event]) -> WaiterMiddleware[Event]:
|
|
66
|
-
hasher = StateViewHasher(view)
|
|
67
|
-
self.storage[hasher] = LimitedDict(maxlimit=self.max_storage_size)
|
|
68
|
-
return WaiterMiddleware(self, hasher)
|
|
69
|
-
|
|
70
82
|
async def drop_all(self) -> None:
|
|
71
83
|
"""Drops all waiters in storage."""
|
|
72
84
|
for hasher in self.storage.copy():
|
|
73
85
|
for ident, short_state in self.storage[hasher].items():
|
|
74
86
|
if short_state.context:
|
|
75
|
-
await self.drop(hasher, ident)
|
|
87
|
+
await self.drop(hasher, data=ident)
|
|
76
88
|
else:
|
|
77
89
|
await short_state.cancel()
|
|
78
90
|
|
|
@@ -101,34 +113,10 @@ class WaiterMachine:
|
|
|
101
113
|
|
|
102
114
|
await short_state.cancel()
|
|
103
115
|
|
|
104
|
-
async def wait_from_event[Event: BaseCute](
|
|
105
|
-
self,
|
|
106
|
-
view: BaseStateView[Event],
|
|
107
|
-
event: Event,
|
|
108
|
-
*,
|
|
109
|
-
filter: ABCRule | None = None,
|
|
110
|
-
release: ABCRule | None = None,
|
|
111
|
-
lifetime: datetime.timedelta | float | None = None,
|
|
112
|
-
lifespan: Lifespan | None = None,
|
|
113
|
-
**actions: typing.Unpack[WaiterActions[Event]],
|
|
114
|
-
) -> ShortStateContext[Event]:
|
|
115
|
-
hasher = StateViewHasher(view)
|
|
116
|
-
return await self.wait(
|
|
117
|
-
hasher=hasher,
|
|
118
|
-
data=hasher.get_data_from_event(event).expect(
|
|
119
|
-
RuntimeError("Hasher couldn't create data from event."),
|
|
120
|
-
),
|
|
121
|
-
filter=filter,
|
|
122
|
-
release=release,
|
|
123
|
-
lifetime=lifetime,
|
|
124
|
-
lifespan=lifespan or Lifespan(),
|
|
125
|
-
**actions,
|
|
126
|
-
)
|
|
127
|
-
|
|
128
116
|
async def wait[Event: BaseCute, HasherData](
|
|
129
117
|
self,
|
|
130
|
-
hasher: Hasher[Event, HasherData],
|
|
131
|
-
data: HasherData,
|
|
118
|
+
hasher: Hasher[Event, HasherData] | HasherWithData[Event, HasherData],
|
|
119
|
+
data: HasherData = _NODEFAULT,
|
|
132
120
|
*,
|
|
133
121
|
filter: ABCRule | None = None,
|
|
134
122
|
release: ABCRule | None = None,
|
|
@@ -141,21 +129,25 @@ class WaiterMachine:
|
|
|
141
129
|
|
|
142
130
|
lifespan = lifespan or Lifespan()
|
|
143
131
|
event = asyncio.Event()
|
|
144
|
-
short_state = ShortState
|
|
132
|
+
short_state = ShortState(
|
|
145
133
|
event,
|
|
146
134
|
actions,
|
|
147
135
|
release=release,
|
|
148
136
|
filter=filter,
|
|
149
137
|
lifetime=lifetime or self.base_state_lifetime,
|
|
150
138
|
)
|
|
139
|
+
|
|
140
|
+
hasher, data = hasher if not isinstance(hasher, Hasher) else (hasher, data)
|
|
141
|
+
assert data is not _NODEFAULT, "Hasher requires data."
|
|
151
142
|
waiter_hash = hasher.get_hash_from_data(data).expect(RuntimeError("Hasher couldn't create hash."))
|
|
152
143
|
|
|
153
144
|
if hasher not in self.storage:
|
|
154
145
|
if self.dispatch:
|
|
155
|
-
view: BaseView
|
|
146
|
+
view: BaseView = self.dispatch.get_view(hasher.view_class).expect(
|
|
156
147
|
RuntimeError(f"View {hasher.view_class.__name__!r} is not defined in dispatch."),
|
|
157
148
|
)
|
|
158
149
|
view.middlewares.insert(0, WaiterMiddleware(self, hasher))
|
|
150
|
+
|
|
159
151
|
self.storage[hasher] = LimitedDict(maxlimit=self.max_storage_size)
|
|
160
152
|
|
|
161
153
|
if (deleted_short_state := self.storage[hasher].set(waiter_hash, short_state)) is not None:
|
|
@@ -170,16 +162,16 @@ class WaiterMachine:
|
|
|
170
162
|
raise LookupError("No context in short_state.")
|
|
171
163
|
return short_state.context
|
|
172
164
|
|
|
173
|
-
async def wait_many[
|
|
165
|
+
async def wait_many[Event: BaseCute[typing.Any], Data, *Ts](
|
|
174
166
|
self,
|
|
175
|
-
*hashers: HasherWithData[
|
|
167
|
+
*hashers: HasherWithData[Event, Data],
|
|
176
168
|
filter: ABCRule | None = None,
|
|
177
169
|
release: ABCRule | None = None,
|
|
178
170
|
lifetime: datetime.timedelta | float | None = None,
|
|
179
171
|
lifespan: Lifespan | None = None,
|
|
180
172
|
unpack: ContextUnpackProto[*Ts] = unpack_to_context,
|
|
181
|
-
**actions: typing.Unpack[WaiterActions[
|
|
182
|
-
) -> tuple[HasherWithData[
|
|
173
|
+
**actions: typing.Unpack[WaiterActions[Event]],
|
|
174
|
+
) -> tuple[HasherWithData[Event, Data], Event, *Ts]:
|
|
183
175
|
if isinstance(lifetime, int | float):
|
|
184
176
|
lifetime = datetime.timedelta(seconds=lifetime)
|
|
185
177
|
|
|
@@ -192,7 +184,7 @@ class WaiterMachine:
|
|
|
192
184
|
filter=filter,
|
|
193
185
|
lifetime=lifetime or self.base_state_lifetime,
|
|
194
186
|
)
|
|
195
|
-
waiter_hashes: dict[Hasher[
|
|
187
|
+
waiter_hashes: dict[Hasher[Event, Data], typing.Hashable] = {}
|
|
196
188
|
|
|
197
189
|
for hasher, data in hashers:
|
|
198
190
|
waiter_hash = hasher.get_hash_from_data(data).expect(RuntimeError("Hasher couldn't create hash."))
|
|
@@ -216,7 +208,7 @@ class WaiterMachine:
|
|
|
216
208
|
if short_state.context is None:
|
|
217
209
|
raise LookupError("No context in short_state.")
|
|
218
210
|
|
|
219
|
-
initiator = short_state.context.context.get(
|
|
211
|
+
initiator = short_state.context.context.get("initiator")
|
|
220
212
|
if initiator is None:
|
|
221
213
|
raise LookupError("Initiator not found in short_state context.")
|
|
222
214
|
|
|
@@ -239,13 +231,4 @@ class WaiterMachine:
|
|
|
239
231
|
await self.drop(hasher, data=ident, force=True)
|
|
240
232
|
|
|
241
233
|
|
|
242
|
-
async def clear_wm_storage_worker(
|
|
243
|
-
wm: WaiterMachine,
|
|
244
|
-
interval_seconds: int = 60,
|
|
245
|
-
) -> typing.NoReturn:
|
|
246
|
-
while True:
|
|
247
|
-
await wm.clear_storage()
|
|
248
|
-
await asyncio.sleep(interval_seconds)
|
|
249
|
-
|
|
250
|
-
|
|
251
234
|
__all__ = ("WaiterMachine",)
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import datetime
|
|
2
2
|
import typing
|
|
3
3
|
|
|
4
|
-
from telegrinder.
|
|
4
|
+
from telegrinder.api.api import API
|
|
5
|
+
from telegrinder.bot.cute_types.update import UpdateCute
|
|
5
6
|
from telegrinder.bot.dispatch.context import Context
|
|
6
7
|
from telegrinder.bot.dispatch.handler.func import FuncHandler
|
|
7
8
|
from telegrinder.bot.dispatch.middleware.abc import ABCMiddleware
|
|
8
9
|
from telegrinder.bot.dispatch.process import check_rule
|
|
9
10
|
from telegrinder.bot.dispatch.waiter_machine.short_state import ShortStateContext
|
|
10
11
|
from telegrinder.modules import logger
|
|
12
|
+
from telegrinder.types.objects import Update
|
|
11
13
|
|
|
12
14
|
from .hasher import Hasher
|
|
13
15
|
|
|
@@ -15,29 +17,29 @@ if typing.TYPE_CHECKING:
|
|
|
15
17
|
from .machine import WaiterMachine
|
|
16
18
|
from .short_state import ShortState
|
|
17
19
|
|
|
20
|
+
type State = ShortState[typing.Any]
|
|
18
21
|
|
|
19
|
-
INITIATOR_CONTEXT_KEY = "initiator"
|
|
20
22
|
|
|
21
|
-
|
|
22
|
-
class WaiterMiddleware[Event: BaseCute](ABCMiddleware[Event]):
|
|
23
|
+
class WaiterMiddleware(ABCMiddleware):
|
|
23
24
|
def __init__(
|
|
24
25
|
self,
|
|
25
26
|
machine: "WaiterMachine",
|
|
26
|
-
hasher: Hasher,
|
|
27
|
+
hasher: Hasher[typing.Any, typing.Any],
|
|
27
28
|
) -> None:
|
|
28
29
|
self.machine = machine
|
|
29
30
|
self.hasher = hasher
|
|
30
31
|
|
|
31
|
-
async def pre(self,
|
|
32
|
+
async def pre(self, update: UpdateCute, raw_update: Update, api: API, ctx: Context) -> bool:
|
|
32
33
|
if self.hasher not in self.machine.storage:
|
|
33
34
|
return True
|
|
34
35
|
|
|
36
|
+
event = update.incoming_update
|
|
35
37
|
key = self.hasher.get_hash_from_data_from_event(event)
|
|
36
38
|
if not key:
|
|
37
39
|
logger.info(f"Unable to get hash from event with hasher {self.hasher!r}")
|
|
38
40
|
return True
|
|
39
41
|
|
|
40
|
-
short_state: "ShortState
|
|
42
|
+
short_state: "ShortState | None" = self.machine.storage[self.hasher].get(key.unwrap())
|
|
41
43
|
if not short_state:
|
|
42
44
|
return True
|
|
43
45
|
|
|
@@ -47,12 +49,12 @@ class WaiterMiddleware[Event: BaseCute](ABCMiddleware[Event]):
|
|
|
47
49
|
|
|
48
50
|
# Run filter rule
|
|
49
51
|
if short_state.filter and not await check_rule(
|
|
50
|
-
|
|
52
|
+
api,
|
|
51
53
|
short_state.filter,
|
|
52
|
-
|
|
54
|
+
raw_update,
|
|
53
55
|
preset_context,
|
|
54
56
|
):
|
|
55
|
-
logger.debug("Filter rule {!r} failed", short_state.filter)
|
|
57
|
+
logger.debug("Filter rule {!r} failed!", short_state.filter)
|
|
56
58
|
return True
|
|
57
59
|
|
|
58
60
|
if short_state.expiration_date is not None and datetime.datetime.now() >= short_state.expiration_date:
|
|
@@ -63,31 +65,25 @@ class WaiterMiddleware[Event: BaseCute](ABCMiddleware[Event]):
|
|
|
63
65
|
)
|
|
64
66
|
return True
|
|
65
67
|
|
|
66
|
-
|
|
68
|
+
result = await FuncHandler(
|
|
67
69
|
self.pass_runtime,
|
|
68
70
|
[short_state.release] if short_state.release else [],
|
|
69
71
|
preset_context=preset_context,
|
|
70
|
-
)
|
|
71
|
-
handler.get_name_event_param = lambda event: "event" # FIXME: HOTFIX
|
|
72
|
-
result = await handler.check(event.ctx_api, ctx.raw_update, ctx)
|
|
73
|
-
|
|
74
|
-
if result is True:
|
|
75
|
-
await handler.run(event.api, event, ctx)
|
|
72
|
+
).run(api, raw_update, ctx)
|
|
76
73
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
await on_miss.run(event.ctx_api, event, ctx)
|
|
74
|
+
if not result and (on_miss := short_state.actions.get("on_miss")):
|
|
75
|
+
await on_miss.run(api, raw_update, ctx)
|
|
80
76
|
|
|
81
77
|
return False
|
|
82
78
|
|
|
83
79
|
async def pass_runtime(
|
|
84
80
|
self,
|
|
85
|
-
event:
|
|
86
|
-
short_state: "ShortState[Event]",
|
|
81
|
+
event: UpdateCute,
|
|
87
82
|
ctx: Context,
|
|
83
|
+
short_state: State,
|
|
88
84
|
) -> None:
|
|
89
85
|
ctx.initiator = self.hasher
|
|
90
|
-
short_state.context = ShortStateContext(event, ctx)
|
|
86
|
+
short_state.context = ShortStateContext(event.incoming_update, ctx) # type: ignore
|
|
91
87
|
short_state.event.set()
|
|
92
88
|
|
|
93
89
|
|
|
@@ -3,22 +3,23 @@ import dataclasses
|
|
|
3
3
|
import datetime
|
|
4
4
|
import typing
|
|
5
5
|
|
|
6
|
-
from telegrinder.bot.cute_types import BaseCute
|
|
7
6
|
from telegrinder.bot.dispatch.context import Context
|
|
8
7
|
from telegrinder.bot.rules.abc import ABCRule
|
|
9
|
-
from telegrinder.tools.
|
|
8
|
+
from telegrinder.tools.aio import cancel_future
|
|
10
9
|
|
|
11
10
|
if typing.TYPE_CHECKING:
|
|
11
|
+
from telegrinder.bot.cute_types.base import BaseCute
|
|
12
|
+
|
|
12
13
|
from .actions import WaiterActions
|
|
13
14
|
|
|
14
15
|
|
|
15
|
-
class ShortStateContext[Event: BaseCute](typing.NamedTuple):
|
|
16
|
+
class ShortStateContext[Event: BaseCute[typing.Any]](typing.NamedTuple):
|
|
16
17
|
event: Event
|
|
17
18
|
context: Context
|
|
18
19
|
|
|
19
20
|
|
|
20
21
|
@dataclasses.dataclass(slots=True)
|
|
21
|
-
class ShortState[Event: BaseCute]:
|
|
22
|
+
class ShortState[Event: BaseCute[typing.Any]]:
|
|
22
23
|
event: asyncio.Event
|
|
23
24
|
actions: "WaiterActions[Event]"
|
|
24
25
|
|
|
@@ -47,7 +48,7 @@ class ShortState[Event: BaseCute]:
|
|
|
47
48
|
async def cancel(self) -> None:
|
|
48
49
|
"""Cancel schedule waiters."""
|
|
49
50
|
waiters = typing.cast(
|
|
50
|
-
typing.Iterable[asyncio.Future[typing.Any]],
|
|
51
|
+
"typing.Iterable[asyncio.Future[typing.Any]]",
|
|
51
52
|
self.event._waiters, # type: ignore
|
|
52
53
|
)
|
|
53
54
|
for future in waiters:
|
|
File without changes
|
telegrinder/bot/polling/abc.py
CHANGED
|
File without changes
|