telegrinder 0.1.dev168__py3-none-any.whl → 0.1.dev170__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 +9 -3
- telegrinder/bot/__init__.py +7 -5
- telegrinder/bot/cute_types/base.py +12 -14
- telegrinder/bot/cute_types/callback_query.py +54 -43
- telegrinder/bot/cute_types/chat_join_request.py +8 -7
- telegrinder/bot/cute_types/chat_member_updated.py +23 -17
- telegrinder/bot/cute_types/inline_query.py +1 -1
- telegrinder/bot/cute_types/message.py +331 -183
- telegrinder/bot/cute_types/update.py +4 -8
- telegrinder/bot/cute_types/utils.py +1 -5
- telegrinder/bot/dispatch/__init__.py +2 -3
- telegrinder/bot/dispatch/abc.py +4 -0
- telegrinder/bot/dispatch/context.py +9 -4
- telegrinder/bot/dispatch/dispatch.py +35 -33
- telegrinder/bot/dispatch/handler/func.py +34 -13
- telegrinder/bot/dispatch/handler/message_reply.py +6 -3
- telegrinder/bot/dispatch/middleware/abc.py +4 -4
- telegrinder/bot/dispatch/process.py +40 -13
- telegrinder/bot/dispatch/return_manager/abc.py +12 -12
- telegrinder/bot/dispatch/return_manager/callback_query.py +1 -3
- telegrinder/bot/dispatch/return_manager/inline_query.py +1 -3
- telegrinder/bot/dispatch/view/abc.py +37 -45
- telegrinder/bot/dispatch/view/box.py +66 -50
- telegrinder/bot/dispatch/view/message.py +1 -5
- telegrinder/bot/dispatch/view/raw.py +6 -6
- telegrinder/bot/dispatch/waiter_machine/__init__.py +2 -1
- telegrinder/bot/dispatch/waiter_machine/machine.py +77 -35
- telegrinder/bot/dispatch/waiter_machine/middleware.py +31 -7
- telegrinder/bot/dispatch/waiter_machine/short_state.py +17 -8
- telegrinder/bot/polling/polling.py +4 -4
- telegrinder/bot/rules/__init__.py +9 -6
- telegrinder/bot/rules/abc.py +99 -22
- telegrinder/bot/rules/adapter/__init__.py +4 -1
- telegrinder/bot/rules/adapter/abc.py +11 -6
- telegrinder/bot/rules/adapter/errors.py +1 -2
- telegrinder/bot/rules/adapter/event.py +14 -9
- telegrinder/bot/rules/adapter/node.py +42 -0
- telegrinder/bot/rules/callback_data.py +4 -4
- telegrinder/bot/rules/chat_join.py +3 -2
- telegrinder/bot/rules/command.py +26 -14
- telegrinder/bot/rules/enum_text.py +5 -5
- telegrinder/bot/rules/func.py +6 -6
- telegrinder/bot/rules/fuzzy.py +5 -7
- telegrinder/bot/rules/inline.py +4 -5
- telegrinder/bot/rules/integer.py +10 -8
- telegrinder/bot/rules/is_from.py +63 -91
- telegrinder/bot/rules/markup.py +5 -5
- telegrinder/bot/rules/mention.py +4 -4
- telegrinder/bot/rules/message.py +1 -1
- telegrinder/bot/rules/node.py +27 -0
- telegrinder/bot/rules/regex.py +5 -5
- telegrinder/bot/rules/rule_enum.py +4 -4
- telegrinder/bot/rules/start.py +5 -5
- telegrinder/bot/rules/text.py +9 -13
- telegrinder/bot/rules/update.py +4 -4
- telegrinder/bot/scenario/__init__.py +3 -3
- telegrinder/bot/scenario/choice.py +2 -3
- telegrinder/model.py +51 -16
- telegrinder/modules.py +14 -6
- telegrinder/msgspec_utils.py +67 -23
- telegrinder/node/__init__.py +26 -8
- telegrinder/node/attachment.py +13 -9
- telegrinder/node/base.py +27 -14
- telegrinder/node/callback_query.py +18 -0
- telegrinder/node/command.py +29 -0
- telegrinder/node/composer.py +119 -30
- telegrinder/node/me.py +14 -0
- telegrinder/node/message.py +2 -4
- telegrinder/node/polymorphic.py +44 -0
- telegrinder/node/rule.py +26 -22
- telegrinder/node/scope.py +36 -0
- telegrinder/node/source.py +37 -10
- telegrinder/node/text.py +11 -5
- telegrinder/node/tools/__init__.py +2 -2
- telegrinder/node/tools/generator.py +6 -6
- telegrinder/tools/__init__.py +8 -13
- telegrinder/tools/buttons.py +23 -17
- telegrinder/tools/error_handler/error_handler.py +11 -14
- telegrinder/tools/formatting/__init__.py +0 -6
- telegrinder/tools/formatting/html.py +10 -12
- telegrinder/tools/formatting/links.py +0 -5
- telegrinder/tools/formatting/spec_html_formats.py +0 -11
- telegrinder/tools/global_context/abc.py +1 -3
- telegrinder/tools/global_context/global_context.py +6 -16
- telegrinder/tools/i18n/simple.py +1 -3
- telegrinder/tools/kb_set/yaml.py +1 -2
- telegrinder/tools/keyboard.py +7 -8
- telegrinder/tools/limited_dict.py +1 -1
- telegrinder/tools/loop_wrapper/loop_wrapper.py +6 -5
- telegrinder/tools/magic.py +27 -5
- telegrinder/types/__init__.py +20 -0
- telegrinder/types/enums.py +37 -31
- telegrinder/types/methods.py +608 -327
- telegrinder/types/objects.py +1139 -716
- {telegrinder-0.1.dev168.dist-info → telegrinder-0.1.dev170.dist-info}/LICENSE +1 -1
- {telegrinder-0.1.dev168.dist-info → telegrinder-0.1.dev170.dist-info}/METADATA +6 -5
- telegrinder-0.1.dev170.dist-info/RECORD +143 -0
- telegrinder/bot/dispatch/composition.py +0 -88
- telegrinder-0.1.dev168.dist-info/RECORD +0 -137
- {telegrinder-0.1.dev168.dist-info → telegrinder-0.1.dev170.dist-info}/WHEEL +0 -0
|
@@ -16,12 +16,12 @@ from telegrinder.msgspec_utils import Option
|
|
|
16
16
|
from telegrinder.tools.error_handler.error_handler import ABCErrorHandler, ErrorHandler
|
|
17
17
|
from telegrinder.types.objects import Update
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
Event = typing.TypeVar("Event", bound=BaseCute)
|
|
20
20
|
ErrorHandlerT = typing.TypeVar("ErrorHandlerT", bound=ABCErrorHandler)
|
|
21
21
|
MiddlewareT = typing.TypeVar("MiddlewareT", bound=ABCMiddleware)
|
|
22
22
|
|
|
23
23
|
FuncType: typing.TypeAlias = typing.Callable[
|
|
24
|
-
typing.Concatenate[
|
|
24
|
+
typing.Concatenate[Event, ...],
|
|
25
25
|
typing.Coroutine[typing.Any, typing.Any, typing.Any],
|
|
26
26
|
]
|
|
27
27
|
|
|
@@ -46,20 +46,24 @@ class ABCView(ABC):
|
|
|
46
46
|
pass
|
|
47
47
|
|
|
48
48
|
|
|
49
|
-
class ABCStateView(ABCView, typing.Generic[
|
|
49
|
+
class ABCStateView(ABCView, typing.Generic[Event]):
|
|
50
50
|
@abstractmethod
|
|
51
|
-
def get_state_key(self, event:
|
|
51
|
+
def get_state_key(self, event: Event) -> int | None:
|
|
52
52
|
pass
|
|
53
53
|
|
|
54
54
|
|
|
55
|
-
class BaseView(ABCView, typing.Generic[
|
|
56
|
-
auto_rules: list[ABCRule
|
|
57
|
-
handlers: list[ABCHandler[
|
|
58
|
-
middlewares: list[ABCMiddleware[
|
|
59
|
-
return_manager: ABCReturnManager[
|
|
55
|
+
class BaseView(ABCView, typing.Generic[Event]):
|
|
56
|
+
auto_rules: list[ABCRule]
|
|
57
|
+
handlers: list[ABCHandler[Event]]
|
|
58
|
+
middlewares: list[ABCMiddleware[Event]]
|
|
59
|
+
return_manager: ABCReturnManager[Event] | None = None
|
|
60
|
+
|
|
61
|
+
@staticmethod
|
|
62
|
+
def get_raw_event(update: Update) -> Option[Model]:
|
|
63
|
+
return getattr(update, update.update_type.value)
|
|
60
64
|
|
|
61
65
|
@classmethod
|
|
62
|
-
def get_event_type(cls) -> Option[type[
|
|
66
|
+
def get_event_type(cls) -> Option[type[Event]]:
|
|
63
67
|
for base in cls.__dict__.get("__orig_bases__", ()):
|
|
64
68
|
if issubclass(typing.get_origin(base) or base, ABCView):
|
|
65
69
|
for generic_type in typing.get_args(base):
|
|
@@ -67,55 +71,45 @@ class BaseView(ABCView, typing.Generic[EventType]):
|
|
|
67
71
|
return Some(generic_type)
|
|
68
72
|
return Nothing()
|
|
69
73
|
|
|
70
|
-
@staticmethod
|
|
71
|
-
def get_raw_event(update: Update) -> Option[Model]:
|
|
72
|
-
match update.update_type:
|
|
73
|
-
case Some(update_type):
|
|
74
|
-
return getattr(update, update_type.value)
|
|
75
|
-
case _:
|
|
76
|
-
return Nothing()
|
|
77
|
-
|
|
78
74
|
@typing.overload
|
|
79
75
|
@classmethod
|
|
80
76
|
def to_handler(
|
|
81
77
|
cls,
|
|
82
|
-
*rules: ABCRule
|
|
78
|
+
*rules: ABCRule,
|
|
83
79
|
) -> typing.Callable[
|
|
84
|
-
[FuncType[
|
|
85
|
-
FuncHandler[
|
|
80
|
+
[FuncType[Event]],
|
|
81
|
+
FuncHandler[Event, FuncType[Event], ErrorHandler[Event]],
|
|
86
82
|
]: ...
|
|
87
83
|
|
|
88
84
|
@typing.overload
|
|
89
85
|
@classmethod
|
|
90
86
|
def to_handler(
|
|
91
87
|
cls,
|
|
92
|
-
*rules: ABCRule
|
|
88
|
+
*rules: ABCRule,
|
|
93
89
|
error_handler: ErrorHandlerT,
|
|
94
90
|
is_blocking: bool = True,
|
|
95
|
-
) -> typing.Callable[
|
|
96
|
-
[FuncType[EventType]], FuncHandler[EventType, FuncType[EventType], ErrorHandlerT]
|
|
97
|
-
]: ...
|
|
91
|
+
) -> typing.Callable[[FuncType[Event]], FuncHandler[Event, FuncType[Event], ErrorHandlerT]]: ...
|
|
98
92
|
|
|
99
93
|
@typing.overload
|
|
100
94
|
@classmethod
|
|
101
95
|
def to_handler(
|
|
102
96
|
cls,
|
|
103
|
-
*rules: ABCRule
|
|
97
|
+
*rules: ABCRule,
|
|
104
98
|
error_handler: typing.Literal[None] = None,
|
|
105
99
|
is_blocking: bool = True,
|
|
106
100
|
) -> typing.Callable[
|
|
107
|
-
[FuncType[
|
|
108
|
-
FuncHandler[
|
|
101
|
+
[FuncType[Event]],
|
|
102
|
+
FuncHandler[Event, FuncType[Event], ErrorHandler[Event]],
|
|
109
103
|
]: ...
|
|
110
104
|
|
|
111
105
|
@classmethod
|
|
112
106
|
def to_handler( # type: ignore
|
|
113
107
|
cls,
|
|
114
|
-
*rules: ABCRule
|
|
108
|
+
*rules: ABCRule,
|
|
115
109
|
error_handler: ABCErrorHandler | None = None,
|
|
116
110
|
is_blocking: bool = True,
|
|
117
111
|
):
|
|
118
|
-
def wrapper(func: FuncType[
|
|
112
|
+
def wrapper(func: FuncType[Event]):
|
|
119
113
|
return FuncHandler(
|
|
120
114
|
func,
|
|
121
115
|
list(rules),
|
|
@@ -129,40 +123,38 @@ class BaseView(ABCView, typing.Generic[EventType]):
|
|
|
129
123
|
@typing.overload
|
|
130
124
|
def __call__(
|
|
131
125
|
self,
|
|
132
|
-
*rules: ABCRule
|
|
126
|
+
*rules: ABCRule,
|
|
133
127
|
) -> typing.Callable[
|
|
134
|
-
[FuncType[
|
|
135
|
-
FuncHandler[
|
|
128
|
+
[FuncType[Event]],
|
|
129
|
+
FuncHandler[Event, FuncType[Event], ErrorHandler[Event]],
|
|
136
130
|
]: ...
|
|
137
131
|
|
|
138
132
|
@typing.overload
|
|
139
|
-
def __call__(
|
|
133
|
+
def __call__( # type: ignore
|
|
140
134
|
self,
|
|
141
|
-
*rules: ABCRule
|
|
135
|
+
*rules: ABCRule,
|
|
142
136
|
error_handler: ErrorHandlerT,
|
|
143
137
|
is_blocking: bool = True,
|
|
144
|
-
) -> typing.Callable[
|
|
145
|
-
[FuncType[EventType]], FuncHandler[EventType, FuncType[EventType], ErrorHandlerT]
|
|
146
|
-
]: ...
|
|
138
|
+
) -> typing.Callable[[FuncType[Event]], FuncHandler[Event, FuncType[Event], ErrorHandlerT]]: ...
|
|
147
139
|
|
|
148
140
|
@typing.overload
|
|
149
141
|
def __call__(
|
|
150
142
|
self,
|
|
151
|
-
*rules: ABCRule
|
|
143
|
+
*rules: ABCRule,
|
|
152
144
|
error_handler: typing.Literal[None] = None,
|
|
153
145
|
is_blocking: bool = True,
|
|
154
146
|
) -> typing.Callable[
|
|
155
|
-
[FuncType[
|
|
156
|
-
FuncHandler[
|
|
147
|
+
[FuncType[Event]],
|
|
148
|
+
FuncHandler[Event, FuncType[Event], ErrorHandler[Event]],
|
|
157
149
|
]: ...
|
|
158
150
|
|
|
159
151
|
def __call__( # type: ignore
|
|
160
152
|
self,
|
|
161
|
-
*rules: ABCRule
|
|
153
|
+
*rules: ABCRule,
|
|
162
154
|
error_handler: ABCErrorHandler | None = None,
|
|
163
155
|
is_blocking: bool = True,
|
|
164
156
|
):
|
|
165
|
-
def wrapper(func: FuncType[
|
|
157
|
+
def wrapper(func: FuncType[Event]):
|
|
166
158
|
func_handler = FuncHandler(
|
|
167
159
|
func,
|
|
168
160
|
[*self.auto_rules, *rules],
|
|
@@ -214,9 +206,9 @@ class BaseView(ABCView, typing.Generic[EventType]):
|
|
|
214
206
|
self.middlewares.extend(external.middlewares)
|
|
215
207
|
|
|
216
208
|
|
|
217
|
-
class BaseStateView(ABCStateView[
|
|
209
|
+
class BaseStateView(ABCStateView[Event], BaseView[Event], ABC, typing.Generic[Event]):
|
|
218
210
|
@abstractmethod
|
|
219
|
-
def get_state_key(self, event:
|
|
211
|
+
def get_state_key(self, event: Event) -> int | None:
|
|
220
212
|
pass
|
|
221
213
|
|
|
222
214
|
|
|
@@ -2,95 +2,111 @@ import dataclasses
|
|
|
2
2
|
|
|
3
3
|
import typing_extensions as typing
|
|
4
4
|
|
|
5
|
+
from telegrinder.bot.dispatch.view import (
|
|
6
|
+
callback_query,
|
|
7
|
+
chat_join_request,
|
|
8
|
+
chat_member,
|
|
9
|
+
inline_query,
|
|
10
|
+
message,
|
|
11
|
+
raw,
|
|
12
|
+
)
|
|
13
|
+
from telegrinder.bot.dispatch.view.abc import ABCView
|
|
5
14
|
from telegrinder.types.enums import UpdateType
|
|
6
15
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
from .chat_join_request import ChatJoinRequestView
|
|
10
|
-
from .chat_member import ChatMemberView
|
|
11
|
-
from .inline_query import InlineQueryView
|
|
12
|
-
from .message import MessageView
|
|
13
|
-
from .raw import RawEventView
|
|
14
|
-
|
|
15
|
-
CallbackQueryViewT = typing.TypeVar("CallbackQueryViewT", bound=ABCView, default=CallbackQueryView)
|
|
16
|
-
ChatJoinRequestViewT = typing.TypeVar(
|
|
17
|
-
"ChatJoinRequestViewT", bound=ABCView, default=ChatJoinRequestView
|
|
16
|
+
CallbackQueryView = typing.TypeVar(
|
|
17
|
+
"CallbackQueryView", bound=ABCView, default=callback_query.CallbackQueryView
|
|
18
18
|
)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
ChatJoinRequestView = typing.TypeVar(
|
|
20
|
+
"ChatJoinRequestView", bound=ABCView, default=chat_join_request.ChatJoinRequestView
|
|
21
|
+
)
|
|
22
|
+
ChatMemberView = typing.TypeVar("ChatMemberView", bound=ABCView, default=chat_member.ChatMemberView)
|
|
23
|
+
InlineQueryView = typing.TypeVar("InlineQueryView", bound=ABCView, default=inline_query.InlineQueryView)
|
|
24
|
+
MessageView = typing.TypeVar("MessageView", bound=ABCView, default=message.MessageView)
|
|
25
|
+
RawEventView = typing.TypeVar("RawEventView", bound=ABCView, default=raw.RawEventView)
|
|
23
26
|
|
|
24
27
|
|
|
25
28
|
@dataclasses.dataclass(kw_only=True)
|
|
26
29
|
class ViewBox(
|
|
27
30
|
typing.Generic[
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
CallbackQueryView,
|
|
32
|
+
ChatJoinRequestView,
|
|
33
|
+
ChatMemberView,
|
|
34
|
+
InlineQueryView,
|
|
35
|
+
MessageView,
|
|
36
|
+
RawEventView,
|
|
34
37
|
],
|
|
35
38
|
):
|
|
36
|
-
callback_query:
|
|
37
|
-
default_factory=lambda: typing.cast(
|
|
39
|
+
callback_query: CallbackQueryView = dataclasses.field(
|
|
40
|
+
default_factory=lambda: typing.cast(
|
|
41
|
+
CallbackQueryView,
|
|
42
|
+
callback_query.CallbackQueryView(),
|
|
43
|
+
),
|
|
38
44
|
)
|
|
39
|
-
chat_join_request:
|
|
40
|
-
default_factory=lambda: typing.cast(
|
|
45
|
+
chat_join_request: ChatJoinRequestView = dataclasses.field(
|
|
46
|
+
default_factory=lambda: typing.cast(
|
|
47
|
+
ChatJoinRequestView,
|
|
48
|
+
chat_join_request.ChatJoinRequestView(),
|
|
49
|
+
),
|
|
41
50
|
)
|
|
42
|
-
chat_member:
|
|
51
|
+
chat_member: ChatMemberView = dataclasses.field(
|
|
43
52
|
default_factory=lambda: typing.cast(
|
|
44
|
-
|
|
53
|
+
ChatMemberView,
|
|
54
|
+
chat_member.ChatMemberView(update_type=UpdateType.CHAT_MEMBER),
|
|
45
55
|
),
|
|
46
56
|
)
|
|
47
|
-
my_chat_member:
|
|
57
|
+
my_chat_member: ChatMemberView = dataclasses.field(
|
|
48
58
|
default_factory=lambda: typing.cast(
|
|
49
|
-
|
|
59
|
+
ChatMemberView,
|
|
60
|
+
chat_member.ChatMemberView(update_type=UpdateType.MY_CHAT_MEMBER),
|
|
50
61
|
),
|
|
51
62
|
)
|
|
52
|
-
inline_query:
|
|
53
|
-
default_factory=lambda: typing.cast(
|
|
63
|
+
inline_query: InlineQueryView = dataclasses.field(
|
|
64
|
+
default_factory=lambda: typing.cast(InlineQueryView, inline_query.InlineQueryView()),
|
|
54
65
|
)
|
|
55
|
-
message:
|
|
66
|
+
message: MessageView = dataclasses.field(
|
|
56
67
|
default_factory=lambda: typing.cast(
|
|
57
|
-
|
|
68
|
+
MessageView,
|
|
69
|
+
message.MessageView(update_type=UpdateType.MESSAGE),
|
|
58
70
|
),
|
|
59
71
|
)
|
|
60
|
-
business_message:
|
|
72
|
+
business_message: MessageView = dataclasses.field(
|
|
61
73
|
default_factory=lambda: typing.cast(
|
|
62
|
-
|
|
74
|
+
MessageView,
|
|
75
|
+
message.MessageView(update_type=UpdateType.BUSINESS_MESSAGE),
|
|
63
76
|
),
|
|
64
77
|
)
|
|
65
|
-
channel_post:
|
|
78
|
+
channel_post: MessageView = dataclasses.field(
|
|
66
79
|
default_factory=lambda: typing.cast(
|
|
67
|
-
|
|
80
|
+
MessageView,
|
|
81
|
+
message.MessageView(update_type=UpdateType.CHANNEL_POST),
|
|
68
82
|
),
|
|
69
83
|
)
|
|
70
|
-
edited_message:
|
|
84
|
+
edited_message: MessageView = dataclasses.field(
|
|
71
85
|
default_factory=lambda: typing.cast(
|
|
72
|
-
|
|
86
|
+
MessageView,
|
|
87
|
+
message.MessageView(update_type=UpdateType.EDITED_MESSAGE),
|
|
73
88
|
),
|
|
74
89
|
)
|
|
75
|
-
edited_business_message:
|
|
90
|
+
edited_business_message: MessageView = dataclasses.field(
|
|
76
91
|
default_factory=lambda: typing.cast(
|
|
77
|
-
|
|
78
|
-
MessageView(update_type=UpdateType.EDITED_BUSINESS_MESSAGE),
|
|
92
|
+
MessageView,
|
|
93
|
+
message.MessageView(update_type=UpdateType.EDITED_BUSINESS_MESSAGE),
|
|
79
94
|
),
|
|
80
95
|
)
|
|
81
|
-
edited_channel_post:
|
|
96
|
+
edited_channel_post: MessageView = dataclasses.field(
|
|
82
97
|
default_factory=lambda: typing.cast(
|
|
83
|
-
|
|
98
|
+
MessageView,
|
|
99
|
+
message.MessageView(update_type=UpdateType.EDITED_CHANNEL_POST),
|
|
84
100
|
),
|
|
85
101
|
)
|
|
86
|
-
any_message:
|
|
87
|
-
default_factory=lambda: typing.cast(
|
|
102
|
+
any_message: MessageView = dataclasses.field(
|
|
103
|
+
default_factory=lambda: typing.cast(MessageView, message.MessageView()),
|
|
88
104
|
)
|
|
89
|
-
chat_member_updated:
|
|
90
|
-
default_factory=lambda: typing.cast(
|
|
105
|
+
chat_member_updated: ChatMemberView = dataclasses.field(
|
|
106
|
+
default_factory=lambda: typing.cast(ChatMemberView, chat_member.ChatMemberView()),
|
|
91
107
|
)
|
|
92
|
-
raw_event:
|
|
93
|
-
default_factory=lambda: typing.cast(
|
|
108
|
+
raw_event: RawEventView = dataclasses.field(
|
|
109
|
+
default_factory=lambda: typing.cast(RawEventView, raw.RawEventView()),
|
|
94
110
|
)
|
|
95
111
|
|
|
96
112
|
def get_views(self) -> dict[str, ABCView]:
|
|
@@ -30,11 +30,7 @@ class MessageView(BaseStateView[MessageCute]):
|
|
|
30
30
|
async def check(self, event: Update) -> bool:
|
|
31
31
|
if not await super().check(event):
|
|
32
32
|
return False
|
|
33
|
-
return
|
|
34
|
-
True
|
|
35
|
-
if self.update_type is None
|
|
36
|
-
else self.update_type == event.update_type.unwrap_or_none()
|
|
37
|
-
)
|
|
33
|
+
return True if self.update_type is None else self.update_type == event.update_type
|
|
38
34
|
|
|
39
35
|
|
|
40
36
|
__all__ = ("MessageView",)
|
|
@@ -29,7 +29,7 @@ class RawEventView(BaseView[UpdateCute]):
|
|
|
29
29
|
def __call__(
|
|
30
30
|
self,
|
|
31
31
|
update_type: UpdateType,
|
|
32
|
-
*rules: ABCRule
|
|
32
|
+
*rules: ABCRule,
|
|
33
33
|
) -> typing.Callable[
|
|
34
34
|
[FuncType[UpdateCute]],
|
|
35
35
|
FuncHandler[UpdateCute, FuncType[UpdateCute], ErrorHandler[UpdateCute]],
|
|
@@ -39,7 +39,7 @@ class RawEventView(BaseView[UpdateCute]):
|
|
|
39
39
|
def __call__(
|
|
40
40
|
self,
|
|
41
41
|
update_type: UpdateType,
|
|
42
|
-
*rules: ABCRule
|
|
42
|
+
*rules: ABCRule,
|
|
43
43
|
dataclass: type[T],
|
|
44
44
|
) -> typing.Callable[[FuncType[T]], FuncHandler[UpdateCute, FuncType[T], ErrorHandler[T]]]: ...
|
|
45
45
|
|
|
@@ -47,7 +47,7 @@ class RawEventView(BaseView[UpdateCute]):
|
|
|
47
47
|
def __call__(
|
|
48
48
|
self,
|
|
49
49
|
update_type: UpdateType,
|
|
50
|
-
*rules: ABCRule
|
|
50
|
+
*rules: ABCRule,
|
|
51
51
|
error_handler: ErrorHandlerT,
|
|
52
52
|
) -> typing.Callable[
|
|
53
53
|
[FuncType[UpdateCute]],
|
|
@@ -58,7 +58,7 @@ class RawEventView(BaseView[UpdateCute]):
|
|
|
58
58
|
def __call__(
|
|
59
59
|
self,
|
|
60
60
|
update_type: UpdateType,
|
|
61
|
-
*rules: ABCRule
|
|
61
|
+
*rules: ABCRule,
|
|
62
62
|
dataclass: type[T],
|
|
63
63
|
error_handler: ErrorHandlerT,
|
|
64
64
|
is_blocking: bool = True,
|
|
@@ -68,7 +68,7 @@ class RawEventView(BaseView[UpdateCute]):
|
|
|
68
68
|
def __call__(
|
|
69
69
|
self,
|
|
70
70
|
update_type: UpdateType,
|
|
71
|
-
*rules: ABCRule
|
|
71
|
+
*rules: ABCRule,
|
|
72
72
|
dataclass: typing.Literal[None] = None,
|
|
73
73
|
error_handler: typing.Literal[None] = None,
|
|
74
74
|
is_blocking: bool = True,
|
|
@@ -80,7 +80,7 @@ class RawEventView(BaseView[UpdateCute]):
|
|
|
80
80
|
def __call__( # type: ignore
|
|
81
81
|
self,
|
|
82
82
|
update_type: UpdateType,
|
|
83
|
-
*rules: ABCRule
|
|
83
|
+
*rules: ABCRule,
|
|
84
84
|
dataclass: type[typing.Any] | None = None,
|
|
85
85
|
error_handler: ABCErrorHandler | None = None,
|
|
86
86
|
is_blocking: bool = True,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from .machine import WaiterMachine
|
|
1
|
+
from .machine import WaiterMachine, clear_wm_storage_worker
|
|
2
2
|
from .middleware import WaiterMiddleware
|
|
3
3
|
from .short_state import ShortState
|
|
4
4
|
|
|
@@ -6,4 +6,5 @@ __all__ = (
|
|
|
6
6
|
"ShortState",
|
|
7
7
|
"WaiterMachine",
|
|
8
8
|
"WaiterMiddleware",
|
|
9
|
+
"clear_wm_storage_worker",
|
|
9
10
|
)
|
|
@@ -4,36 +4,46 @@ import typing
|
|
|
4
4
|
|
|
5
5
|
from telegrinder.api.abc import ABCAPI
|
|
6
6
|
from telegrinder.bot.dispatch.context import Context
|
|
7
|
+
from telegrinder.bot.dispatch.view.abc import ABCStateView, BaseStateView
|
|
7
8
|
from telegrinder.bot.rules.abc import ABCRule
|
|
8
9
|
from telegrinder.tools.limited_dict import LimitedDict
|
|
9
10
|
from telegrinder.types import Update
|
|
10
11
|
|
|
11
12
|
from .middleware import WaiterMiddleware
|
|
12
|
-
from .short_state import Behaviour, EventModel, ShortState
|
|
13
|
+
from .short_state import Behaviour, EventModel, ShortState, ShortStateContext
|
|
13
14
|
|
|
14
15
|
if typing.TYPE_CHECKING:
|
|
15
|
-
from telegrinder.bot.dispatch
|
|
16
|
+
from telegrinder.bot.dispatch import Dispatch
|
|
16
17
|
|
|
17
18
|
T = typing.TypeVar("T")
|
|
18
19
|
|
|
19
20
|
Identificator: typing.TypeAlias = str | int
|
|
20
21
|
Storage: typing.TypeAlias = dict[str, LimitedDict[Identificator, ShortState[EventModel]]]
|
|
21
22
|
|
|
23
|
+
WEEK: typing.Final[datetime.timedelta] = datetime.timedelta(days=7)
|
|
24
|
+
|
|
22
25
|
|
|
23
26
|
class WaiterMachine:
|
|
24
|
-
def __init__(self) -> None:
|
|
27
|
+
def __init__(self, *, max_storage_size: int = 1000) -> None:
|
|
28
|
+
self.max_storage_size = max_storage_size
|
|
25
29
|
self.storage: Storage = {}
|
|
26
30
|
|
|
27
31
|
def __repr__(self) -> str:
|
|
28
|
-
return "<{}:
|
|
32
|
+
return "<{}: max_storage_size={}, {}>".format(
|
|
29
33
|
self.__class__.__name__,
|
|
30
|
-
self.
|
|
34
|
+
self.max_storage_size,
|
|
35
|
+
", ".join(
|
|
36
|
+
f"{view_name}: {len(self.storage[view_name].values())} shortstates"
|
|
37
|
+
for view_name in self.storage
|
|
38
|
+
)
|
|
39
|
+
or "empty",
|
|
31
40
|
)
|
|
32
41
|
|
|
33
42
|
async def drop(
|
|
34
43
|
self,
|
|
35
44
|
state_view: "ABCStateView[EventModel]",
|
|
36
45
|
id: Identificator,
|
|
46
|
+
event: EventModel,
|
|
37
47
|
update: Update,
|
|
38
48
|
**context: typing.Any,
|
|
39
49
|
) -> None:
|
|
@@ -43,16 +53,12 @@ class WaiterMachine:
|
|
|
43
53
|
|
|
44
54
|
short_state = self.storage[view_name].pop(id, None)
|
|
45
55
|
if short_state is None:
|
|
46
|
-
raise LookupError(
|
|
47
|
-
"Waiter with identificator {} is not found for view {!r}".format(
|
|
48
|
-
id, view_name
|
|
49
|
-
)
|
|
50
|
-
)
|
|
56
|
+
raise LookupError("Waiter with identificator {} is not found for view {!r}".format(id, view_name))
|
|
51
57
|
|
|
52
58
|
short_state.cancel()
|
|
53
59
|
await self.call_behaviour(
|
|
54
60
|
state_view,
|
|
55
|
-
|
|
61
|
+
event,
|
|
56
62
|
update,
|
|
57
63
|
behaviour=short_state.on_drop_behaviour,
|
|
58
64
|
**context,
|
|
@@ -62,27 +68,24 @@ class WaiterMachine:
|
|
|
62
68
|
self,
|
|
63
69
|
state_view: "BaseStateView[EventModel]",
|
|
64
70
|
linked: EventModel | tuple[ABCAPI, Identificator],
|
|
65
|
-
*rules: ABCRule
|
|
71
|
+
*rules: ABCRule,
|
|
66
72
|
default: Behaviour[EventModel] | None = None,
|
|
67
73
|
on_drop: Behaviour[EventModel] | None = None,
|
|
74
|
+
exit: Behaviour[EventModel] | None = None,
|
|
68
75
|
expiration: datetime.timedelta | float | None = None,
|
|
69
|
-
) ->
|
|
76
|
+
) -> ShortStateContext[EventModel]:
|
|
70
77
|
if isinstance(expiration, int | float):
|
|
71
78
|
expiration = datetime.timedelta(seconds=expiration)
|
|
72
79
|
|
|
73
80
|
api: ABCAPI
|
|
74
81
|
key: Identificator
|
|
75
|
-
api, key = (
|
|
76
|
-
linked
|
|
77
|
-
if isinstance(linked, tuple)
|
|
78
|
-
else (linked.ctx_api, state_view.get_state_key(linked))
|
|
79
|
-
) # type: ignore
|
|
82
|
+
api, key = linked if isinstance(linked, tuple) else (linked.ctx_api, state_view.get_state_key(linked)) # type: ignore
|
|
80
83
|
if not key:
|
|
81
84
|
raise RuntimeError("Unable to get state key.")
|
|
82
85
|
|
|
83
86
|
view_name = state_view.__class__.__name__
|
|
84
87
|
event = asyncio.Event()
|
|
85
|
-
short_state = ShortState(
|
|
88
|
+
short_state = ShortState[EventModel](
|
|
86
89
|
key,
|
|
87
90
|
api,
|
|
88
91
|
event,
|
|
@@ -90,42 +93,81 @@ class WaiterMachine:
|
|
|
90
93
|
expiration=expiration,
|
|
91
94
|
default_behaviour=default,
|
|
92
95
|
on_drop_behaviour=on_drop,
|
|
96
|
+
exit_behaviour=exit,
|
|
93
97
|
)
|
|
94
|
-
|
|
98
|
+
|
|
95
99
|
if view_name not in self.storage:
|
|
96
100
|
state_view.middlewares.insert(0, WaiterMiddleware(self, state_view))
|
|
97
|
-
self.storage[view_name] = LimitedDict()
|
|
101
|
+
self.storage[view_name] = LimitedDict(maxlimit=self.max_storage_size)
|
|
98
102
|
|
|
99
103
|
if (deleted_short_state := self.storage[view_name].set(key, short_state)) is not None:
|
|
100
104
|
deleted_short_state.cancel()
|
|
101
|
-
|
|
105
|
+
|
|
102
106
|
await event.wait()
|
|
103
107
|
self.storage[view_name].pop(key, None)
|
|
104
|
-
|
|
108
|
+
assert short_state.context is not None
|
|
109
|
+
return short_state.context
|
|
105
110
|
|
|
106
111
|
async def call_behaviour(
|
|
107
112
|
self,
|
|
108
113
|
view: "ABCStateView[EventModel]",
|
|
109
|
-
event:
|
|
114
|
+
event: EventModel,
|
|
110
115
|
update: Update,
|
|
111
116
|
behaviour: Behaviour[EventModel] | None = None,
|
|
112
117
|
**context: typing.Any,
|
|
113
|
-
) ->
|
|
114
|
-
# TODO: support
|
|
118
|
+
) -> bool:
|
|
119
|
+
# TODO: support view as a behaviour
|
|
115
120
|
|
|
116
121
|
if behaviour is None:
|
|
117
|
-
return
|
|
122
|
+
return False
|
|
118
123
|
|
|
119
124
|
ctx = Context(**context)
|
|
120
|
-
if isinstance(event, asyncio.Event):
|
|
121
|
-
event, preset_ctx = typing.cast(
|
|
122
|
-
tuple[EventModel, Context],
|
|
123
|
-
getattr(event, "context"),
|
|
124
|
-
)
|
|
125
|
-
ctx.update(preset_ctx)
|
|
126
|
-
|
|
127
125
|
if await behaviour.check(event.api, update, ctx):
|
|
128
126
|
await behaviour.run(event, ctx)
|
|
129
|
-
|
|
127
|
+
return True
|
|
128
|
+
|
|
129
|
+
return False
|
|
130
|
+
|
|
131
|
+
async def clear_storage(
|
|
132
|
+
self,
|
|
133
|
+
views: typing.Iterable[ABCStateView[EventModel]],
|
|
134
|
+
absolutely_dead_time: datetime.timedelta = WEEK,
|
|
135
|
+
):
|
|
136
|
+
"""Clears storage.
|
|
137
|
+
|
|
138
|
+
:param absolutely_dead_time: timedelta when state can be forgotten.
|
|
139
|
+
"""
|
|
140
|
+
|
|
141
|
+
for view in views:
|
|
142
|
+
view_name = view.__class__.__name__
|
|
143
|
+
now = datetime.datetime.now()
|
|
144
|
+
for ident, short_state in self.storage.get(view_name, {}).copy().items():
|
|
145
|
+
if short_state.expiration_date is not None and now > short_state.expiration_date:
|
|
146
|
+
assert short_state.context
|
|
147
|
+
await self.drop(
|
|
148
|
+
view,
|
|
149
|
+
ident,
|
|
150
|
+
event=short_state.context.event,
|
|
151
|
+
update=short_state.context.context.raw_update,
|
|
152
|
+
force=True,
|
|
153
|
+
)
|
|
154
|
+
elif short_state.creation_date + absolutely_dead_time < now:
|
|
155
|
+
short_state.cancel()
|
|
156
|
+
del self.storage[view_name][short_state.key]
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
async def clear_wm_storage_worker(
|
|
160
|
+
wm: WaiterMachine,
|
|
161
|
+
dp: "Dispatch",
|
|
162
|
+
interval_seconds: int = 60,
|
|
163
|
+
absolutely_dead_time: datetime.timedelta = WEEK,
|
|
164
|
+
) -> typing.NoReturn:
|
|
165
|
+
while True:
|
|
166
|
+
await wm.clear_storage(
|
|
167
|
+
views=[view for view in dp.get_views().values() if isinstance(view, ABCStateView)],
|
|
168
|
+
absolutely_dead_time=absolutely_dead_time,
|
|
169
|
+
)
|
|
170
|
+
await asyncio.sleep(interval_seconds)
|
|
171
|
+
|
|
130
172
|
|
|
131
173
|
__all__ = ("WaiterMachine",)
|