telegrinder 0.1.dev166__py3-none-any.whl → 0.1.dev167__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 +0 -2
- telegrinder/bot/__init__.py +0 -2
- telegrinder/bot/bot.py +1 -3
- telegrinder/bot/cute_types/base.py +3 -12
- telegrinder/bot/cute_types/callback_query.py +1 -3
- telegrinder/bot/cute_types/chat_join_request.py +3 -1
- telegrinder/bot/cute_types/chat_member_updated.py +3 -1
- telegrinder/bot/cute_types/message.py +10 -31
- telegrinder/bot/cute_types/utils.py +1 -3
- telegrinder/bot/dispatch/__init__.py +1 -2
- telegrinder/bot/dispatch/composition.py +1 -3
- telegrinder/bot/dispatch/dispatch.py +1 -3
- telegrinder/bot/dispatch/handler/func.py +3 -10
- telegrinder/bot/dispatch/return_manager/abc.py +9 -13
- telegrinder/bot/dispatch/return_manager/message.py +5 -7
- telegrinder/bot/dispatch/view/abc.py +1 -3
- telegrinder/bot/dispatch/view/box.py +3 -11
- telegrinder/bot/dispatch/view/raw.py +2 -6
- telegrinder/bot/dispatch/waiter_machine/__init__.py +1 -2
- telegrinder/bot/dispatch/waiter_machine/machine.py +35 -74
- telegrinder/bot/dispatch/waiter_machine/middleware.py +12 -5
- telegrinder/bot/dispatch/waiter_machine/short_state.py +6 -6
- telegrinder/bot/polling/polling.py +2 -6
- telegrinder/bot/rules/adapter/event.py +1 -3
- telegrinder/bot/rules/callback_data.py +3 -1
- telegrinder/bot/rules/fuzzy.py +1 -2
- telegrinder/bot/rules/is_from.py +6 -4
- telegrinder/bot/rules/markup.py +1 -2
- telegrinder/bot/rules/mention.py +1 -4
- telegrinder/bot/rules/regex.py +1 -2
- telegrinder/bot/rules/rule_enum.py +1 -3
- telegrinder/bot/rules/start.py +1 -3
- telegrinder/bot/scenario/checkbox.py +1 -5
- telegrinder/client/aiohttp.py +1 -3
- telegrinder/model.py +4 -3
- telegrinder/modules.py +1 -3
- telegrinder/msgspec_utils.py +1 -3
- telegrinder/node/attachment.py +18 -14
- telegrinder/node/base.py +4 -11
- telegrinder/node/composer.py +1 -3
- telegrinder/node/message.py +3 -1
- telegrinder/node/source.py +3 -1
- telegrinder/node/text.py +3 -1
- telegrinder/tools/__init__.py +2 -0
- telegrinder/tools/buttons.py +4 -6
- telegrinder/tools/error_handler/abc.py +1 -3
- telegrinder/tools/error_handler/error.py +3 -6
- telegrinder/tools/error_handler/error_handler.py +17 -13
- telegrinder/tools/formatting/html.py +2 -6
- telegrinder/tools/formatting/links.py +1 -3
- telegrinder/tools/global_context/abc.py +1 -3
- telegrinder/tools/global_context/global_context.py +13 -31
- telegrinder/tools/i18n/middleware/base.py +1 -3
- telegrinder/tools/limited_dict.py +27 -0
- telegrinder/tools/loop_wrapper/loop_wrapper.py +3 -7
- telegrinder/types/__init__.py +30 -0
- telegrinder/types/objects.py +4 -4
- telegrinder/verification_utils.py +2 -1
- {telegrinder-0.1.dev166.dist-info → telegrinder-0.1.dev167.dist-info}/METADATA +1 -1
- {telegrinder-0.1.dev166.dist-info → telegrinder-0.1.dev167.dist-info}/RECORD +62 -61
- {telegrinder-0.1.dev166.dist-info → telegrinder-0.1.dev167.dist-info}/LICENSE +0 -0
- {telegrinder-0.1.dev166.dist-info → telegrinder-0.1.dev167.dist-info}/WHEEL +0 -0
telegrinder/__init__.py
CHANGED
|
@@ -69,7 +69,6 @@ from .bot import (
|
|
|
69
69
|
Polling,
|
|
70
70
|
RawEventView,
|
|
71
71
|
ShortState,
|
|
72
|
-
ShortStateStorage,
|
|
73
72
|
SingleChoice,
|
|
74
73
|
Telegrinder,
|
|
75
74
|
UpdateCute,
|
|
@@ -189,7 +188,6 @@ __all__ = (
|
|
|
189
188
|
"RawEventView",
|
|
190
189
|
"RowButtons",
|
|
191
190
|
"ShortState",
|
|
192
|
-
"ShortStateStorage",
|
|
193
191
|
"SimpleI18n",
|
|
194
192
|
"SimpleTranslator",
|
|
195
193
|
"SingleChoice",
|
telegrinder/bot/__init__.py
CHANGED
|
@@ -33,7 +33,6 @@ from .dispatch import (
|
|
|
33
33
|
MessageView,
|
|
34
34
|
RawEventView,
|
|
35
35
|
ShortState,
|
|
36
|
-
ShortStateStorage,
|
|
37
36
|
ViewBox,
|
|
38
37
|
WaiterMachine,
|
|
39
38
|
register_manager,
|
|
@@ -80,7 +79,6 @@ __all__ = (
|
|
|
80
79
|
"Polling",
|
|
81
80
|
"RawEventView",
|
|
82
81
|
"ShortState",
|
|
83
|
-
"ShortStateStorage",
|
|
84
82
|
"SingleChoice",
|
|
85
83
|
"Telegrinder",
|
|
86
84
|
"UpdateCute",
|
telegrinder/bot/bot.py
CHANGED
|
@@ -57,9 +57,7 @@ class Telegrinder(typing.Generic[DispatchT, PollingT, LoopWrapperT]):
|
|
|
57
57
|
|
|
58
58
|
def run_forever(self, *, offset: int = 0, skip_updates: bool = False) -> None:
|
|
59
59
|
logger.debug("Running blocking polling (id={})", self.api.id)
|
|
60
|
-
self.loop_wrapper.add_task(
|
|
61
|
-
self.run_polling(offset=offset, skip_updates=skip_updates)
|
|
62
|
-
)
|
|
60
|
+
self.loop_wrapper.add_task(self.run_polling(offset=offset, skip_updates=skip_updates))
|
|
63
61
|
self.loop_wrapper.run_event_loop()
|
|
64
62
|
|
|
65
63
|
|
|
@@ -74,9 +74,7 @@ def compose_method_params(
|
|
|
74
74
|
if param_name not in params:
|
|
75
75
|
if param_name in validators and not validators[param_name](update):
|
|
76
76
|
continue
|
|
77
|
-
params[param_name] = getattr(
|
|
78
|
-
update, param if isinstance(param, str) else param[1]
|
|
79
|
-
)
|
|
77
|
+
params[param_name] = getattr(update, param if isinstance(param, str) else param[1])
|
|
80
78
|
|
|
81
79
|
return params
|
|
82
80
|
|
|
@@ -105,21 +103,14 @@ def shortcut(
|
|
|
105
103
|
index = 0
|
|
106
104
|
|
|
107
105
|
for k, p in signature_params.items():
|
|
108
|
-
if (
|
|
109
|
-
p.kind in (p.POSITIONAL_OR_KEYWORD, p.POSITIONAL_ONLY)
|
|
110
|
-
and len(args) > index
|
|
111
|
-
):
|
|
106
|
+
if p.kind in (p.POSITIONAL_OR_KEYWORD, p.POSITIONAL_ONLY) and len(args) > index:
|
|
112
107
|
params[k] = args[index]
|
|
113
108
|
index += 1
|
|
114
109
|
continue
|
|
115
110
|
if p.kind in (p.VAR_KEYWORD, p.VAR_POSITIONAL):
|
|
116
111
|
params[k] = kwargs.copy() if p.kind is p.VAR_KEYWORD else args[index:]
|
|
117
112
|
continue
|
|
118
|
-
params[k] = (
|
|
119
|
-
kwargs.pop(k, p.default)
|
|
120
|
-
if p.default is not p.empty
|
|
121
|
-
else kwargs.pop(k)
|
|
122
|
-
)
|
|
113
|
+
params[k] = kwargs.pop(k, p.default) if p.default is not p.empty else kwargs.pop(k)
|
|
123
114
|
|
|
124
115
|
return await executor(self, method_name, get_params(params))
|
|
125
116
|
|
|
@@ -44,9 +44,7 @@ class CallbackQueryCute(BaseCute[CallbackQuery], CallbackQuery, kw_only=True, di
|
|
|
44
44
|
by the bot with the callback button that originated the query."""
|
|
45
45
|
|
|
46
46
|
return self.message.map(
|
|
47
|
-
lambda m: m.only()
|
|
48
|
-
.map(lambda m: m.is_topic_message.unwrap_or(False))
|
|
49
|
-
.unwrap_or(False)
|
|
47
|
+
lambda m: m.only().map(lambda m: m.is_topic_message.unwrap_or(False)).unwrap_or(False)
|
|
50
48
|
)
|
|
51
49
|
|
|
52
50
|
@property
|
|
@@ -9,7 +9,9 @@ from .base import BaseCute, shortcut
|
|
|
9
9
|
from .chat_member_updated import ChatMemberShortcuts, chat_member_interaction
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
class ChatJoinRequestCute(
|
|
12
|
+
class ChatJoinRequestCute(
|
|
13
|
+
BaseCute[ChatJoinRequest], ChatJoinRequest, ChatMemberShortcuts, kw_only=True
|
|
14
|
+
):
|
|
13
15
|
api: ABCAPI
|
|
14
16
|
|
|
15
17
|
@property
|
|
@@ -232,7 +232,9 @@ class ChatMemberShortcuts:
|
|
|
232
232
|
...
|
|
233
233
|
|
|
234
234
|
|
|
235
|
-
class ChatMemberUpdatedCute(
|
|
235
|
+
class ChatMemberUpdatedCute(
|
|
236
|
+
BaseCute[ChatMemberUpdated], ChatMemberUpdated, ChatMemberShortcuts, kw_only=True
|
|
237
|
+
):
|
|
236
238
|
api: ABCAPI
|
|
237
239
|
|
|
238
240
|
@property
|
|
@@ -71,16 +71,12 @@ async def execute_method_answer(
|
|
|
71
71
|
link_preview_options = params.get("link_preview_options")
|
|
72
72
|
|
|
73
73
|
if reply_parameters is not None and isinstance(reply_parameters, dict):
|
|
74
|
-
reply_parameters.setdefault(
|
|
75
|
-
"message_id", params.get("message_id", message.message_id)
|
|
76
|
-
)
|
|
74
|
+
reply_parameters.setdefault("message_id", params.get("message_id", message.message_id))
|
|
77
75
|
reply_parameters.setdefault("chat_id", params.get("chat_id"))
|
|
78
76
|
params["reply_parameters"] = compose_reply_params(**reply_parameters)
|
|
79
77
|
|
|
80
78
|
if link_preview_options is not None and isinstance(link_preview_options, dict):
|
|
81
|
-
params["link_preview_options"] = compose_link_preview_options(
|
|
82
|
-
**link_preview_options
|
|
83
|
-
)
|
|
79
|
+
params["link_preview_options"] = compose_link_preview_options(**link_preview_options)
|
|
84
80
|
|
|
85
81
|
result = await getattr(message.ctx_api, method_name)(**params)
|
|
86
82
|
return result.map(
|
|
@@ -124,8 +120,7 @@ async def execute_method_edit(
|
|
|
124
120
|
"message_thread_id": lambda x: (
|
|
125
121
|
x.is_topic_message.unwrap_or(False)
|
|
126
122
|
if isinstance(x, MessageCute)
|
|
127
|
-
else bool(x.message)
|
|
128
|
-
and getattr(x.message.unwrap().v, "is_topic_message", False)
|
|
123
|
+
else bool(x.message) and getattr(x.message.unwrap().v, "is_topic_message", False)
|
|
129
124
|
),
|
|
130
125
|
},
|
|
131
126
|
)
|
|
@@ -330,9 +325,7 @@ class MessageCute(BaseCute[Message], Message, kw_only=True):
|
|
|
330
325
|
params=get_params(locals()),
|
|
331
326
|
update=self,
|
|
332
327
|
default_params={"chat_id", "message_id", "message_thread_id"},
|
|
333
|
-
validators={
|
|
334
|
-
"message_thread_id": lambda x: x.is_topic_message.unwrap_or(False)
|
|
335
|
-
},
|
|
328
|
+
validators={"message_thread_id": lambda x: x.is_topic_message.unwrap_or(False)},
|
|
336
329
|
)
|
|
337
330
|
return await self.ctx_api.delete_message(**params)
|
|
338
331
|
|
|
@@ -450,9 +443,7 @@ class MessageCute(BaseCute[Message], Message, kw_only=True):
|
|
|
450
443
|
("from_chat_id", "chat_id"),
|
|
451
444
|
"message_thread_id",
|
|
452
445
|
},
|
|
453
|
-
validators={
|
|
454
|
-
"message_thread_id": lambda x: x.is_topic_message.unwrap_or(False)
|
|
455
|
-
},
|
|
446
|
+
validators={"message_thread_id": lambda x: x.is_topic_message.unwrap_or(False)},
|
|
456
447
|
)
|
|
457
448
|
if isinstance(reply_parameters, dict):
|
|
458
449
|
reply_parameters.setdefault("message_id", params.get("message_id"))
|
|
@@ -467,11 +458,7 @@ class MessageCute(BaseCute[Message], Message, kw_only=True):
|
|
|
467
458
|
async def react(
|
|
468
459
|
self,
|
|
469
460
|
reaction: (
|
|
470
|
-
str
|
|
471
|
-
| ReactionEmoji
|
|
472
|
-
| ReactionType
|
|
473
|
-
| list[str | ReactionEmoji | ReactionType]
|
|
474
|
-
| None
|
|
461
|
+
str | ReactionEmoji | ReactionType | list[str | ReactionEmoji | ReactionType] | None
|
|
475
462
|
) = None,
|
|
476
463
|
chat_id: int | str | None = None,
|
|
477
464
|
message_thread_id: int | None = None,
|
|
@@ -507,9 +494,7 @@ class MessageCute(BaseCute[Message], Message, kw_only=True):
|
|
|
507
494
|
params=get_params(locals()),
|
|
508
495
|
update=self,
|
|
509
496
|
default_params={"chat_id", "message_id", "message_thread_id"},
|
|
510
|
-
validators={
|
|
511
|
-
"message_thread_id": lambda x: x.is_topic_message.unwrap_or(False)
|
|
512
|
-
},
|
|
497
|
+
validators={"message_thread_id": lambda x: x.is_topic_message.unwrap_or(False)},
|
|
513
498
|
)
|
|
514
499
|
if reaction:
|
|
515
500
|
params["reaction"] = compose_reactions(
|
|
@@ -557,9 +542,7 @@ class MessageCute(BaseCute[Message], Message, kw_only=True):
|
|
|
557
542
|
"message_id",
|
|
558
543
|
"message_thread_id",
|
|
559
544
|
},
|
|
560
|
-
validators={
|
|
561
|
-
"message_thread_id": lambda x: x.is_topic_message.unwrap_or(False)
|
|
562
|
-
},
|
|
545
|
+
validators={"message_thread_id": lambda x: x.is_topic_message.unwrap_or(False)},
|
|
563
546
|
)
|
|
564
547
|
return (await self.ctx_api.forward_message(**params)).map(
|
|
565
548
|
lambda message: MessageCute.from_update(message, bound_api=self.api),
|
|
@@ -599,9 +582,7 @@ class MessageCute(BaseCute[Message], Message, kw_only=True):
|
|
|
599
582
|
params=get_params(locals()),
|
|
600
583
|
update=self,
|
|
601
584
|
default_params={"chat_id", "message_id", "message_thread_id"},
|
|
602
|
-
validators={
|
|
603
|
-
"message_thread_id": lambda x: x.is_topic_message.unwrap_or(False)
|
|
604
|
-
},
|
|
585
|
+
validators={"message_thread_id": lambda x: x.is_topic_message.unwrap_or(False)},
|
|
605
586
|
)
|
|
606
587
|
return await self.ctx_api.pin_chat_message(**params)
|
|
607
588
|
|
|
@@ -632,9 +613,7 @@ class MessageCute(BaseCute[Message], Message, kw_only=True):
|
|
|
632
613
|
params=get_params(locals()),
|
|
633
614
|
update=self,
|
|
634
615
|
default_params={"chat_id", "message_id", "message_thread_id"},
|
|
635
|
-
validators={
|
|
636
|
-
"message_thread_id": lambda x: x.is_topic_message.unwrap_or(False)
|
|
637
|
-
},
|
|
616
|
+
validators={"message_thread_id": lambda x: x.is_topic_message.unwrap_or(False)},
|
|
638
617
|
)
|
|
639
618
|
return await self.ctx_api.pin_chat_message(**params)
|
|
640
619
|
|
|
@@ -69,9 +69,7 @@ INPUT_MEDIA_TYPES: typing.Final[dict[str, type[InputMedia]]] = {
|
|
|
69
69
|
|
|
70
70
|
|
|
71
71
|
def compose_reactions(
|
|
72
|
-
reactions: (
|
|
73
|
-
str | ReactionEmoji | ReactionType | list[str | ReactionEmoji | ReactionType]
|
|
74
|
-
),
|
|
72
|
+
reactions: (str | ReactionEmoji | ReactionType | list[str | ReactionEmoji | ReactionType]),
|
|
75
73
|
/,
|
|
76
74
|
) -> list[ReactionType]:
|
|
77
75
|
if not isinstance(reactions, list):
|
|
@@ -27,7 +27,7 @@ from .view import (
|
|
|
27
27
|
RawEventView,
|
|
28
28
|
ViewBox,
|
|
29
29
|
)
|
|
30
|
-
from .waiter_machine import ShortState,
|
|
30
|
+
from .waiter_machine import ShortState, WaiterMachine
|
|
31
31
|
|
|
32
32
|
__all__ = (
|
|
33
33
|
"ABCDispatch",
|
|
@@ -56,7 +56,6 @@ __all__ = (
|
|
|
56
56
|
"MessageView",
|
|
57
57
|
"RawEventView",
|
|
58
58
|
"ShortState",
|
|
59
|
-
"ShortStateStorage",
|
|
60
59
|
"TelegrinderCtx",
|
|
61
60
|
"ViewBox",
|
|
62
61
|
"WaiterMachine",
|
|
@@ -78,9 +78,7 @@ class CompositionDispatch(ABCDispatch):
|
|
|
78
78
|
def wrapper(func: typing.Callable):
|
|
79
79
|
composition = Composition(func, is_blocking)
|
|
80
80
|
if container_nodes:
|
|
81
|
-
composition.nodes["container"] = ContainerNode.link_nodes(
|
|
82
|
-
list(container_nodes)
|
|
83
|
-
)
|
|
81
|
+
composition.nodes["container"] = ContainerNode.link_nodes(list(container_nodes))
|
|
84
82
|
self.compositions.append(composition)
|
|
85
83
|
return func
|
|
86
84
|
|
|
@@ -160,9 +160,7 @@ class Dispatch(
|
|
|
160
160
|
def load(self, external: typing.Self) -> None:
|
|
161
161
|
view_external = external.get_views()
|
|
162
162
|
for name, view in self.get_views().items():
|
|
163
|
-
assert
|
|
164
|
-
name in view_external
|
|
165
|
-
), f"View {name!r} is undefined in external dispatch."
|
|
163
|
+
assert name in view_external, f"View {name!r} is undefined in external dispatch."
|
|
166
164
|
view.load(view_external[name])
|
|
167
165
|
setattr(external, name, view)
|
|
168
166
|
|
|
@@ -18,14 +18,10 @@ if typing.TYPE_CHECKING:
|
|
|
18
18
|
|
|
19
19
|
F = typing.TypeVar(
|
|
20
20
|
"F",
|
|
21
|
-
bound=typing.Callable[
|
|
22
|
-
typing.Concatenate[typing.Any, ...], typing.Awaitable[typing.Any]
|
|
23
|
-
],
|
|
21
|
+
bound=typing.Callable[typing.Concatenate[typing.Any, ...], typing.Awaitable[typing.Any]],
|
|
24
22
|
)
|
|
25
23
|
EventT = typing.TypeVar("EventT", bound=BaseCute)
|
|
26
|
-
ErrorHandlerT = typing.TypeVar(
|
|
27
|
-
"ErrorHandlerT", bound=ABCErrorHandler, default=ErrorHandler
|
|
28
|
-
)
|
|
24
|
+
ErrorHandlerT = typing.TypeVar("ErrorHandlerT", bound=ABCErrorHandler, default=ErrorHandler)
|
|
29
25
|
|
|
30
26
|
|
|
31
27
|
@dataclasses.dataclass(repr=False)
|
|
@@ -52,10 +48,7 @@ class FuncHandler(ABCHandler[EventT], typing.Generic[EventT, F, ErrorHandlerT]):
|
|
|
52
48
|
)
|
|
53
49
|
|
|
54
50
|
async def check(self, api: ABCAPI, event: Update, ctx: Context | None = None) -> bool:
|
|
55
|
-
if (
|
|
56
|
-
self.update_type is not None
|
|
57
|
-
and self.update_type != event.update_type.unwrap_or_none()
|
|
58
|
-
):
|
|
51
|
+
if self.update_type is not None and self.update_type != event.update_type.unwrap_or_none():
|
|
59
52
|
return False
|
|
60
53
|
ctx = ctx or Context()
|
|
61
54
|
temp_ctx = ctx.copy()
|
|
@@ -66,9 +66,7 @@ class BaseReturnManager(ABCReturnManager[EventT]):
|
|
|
66
66
|
|
|
67
67
|
async def run(self, response: typing.Any, event: EventT, ctx: Context) -> None:
|
|
68
68
|
for manager in self.managers:
|
|
69
|
-
if typing.Any in manager.types or any(
|
|
70
|
-
type(response) is x for x in manager.types
|
|
71
|
-
):
|
|
69
|
+
if typing.Any in manager.types or any(type(response) is x for x in manager.types):
|
|
72
70
|
await manager(response, event, ctx)
|
|
73
71
|
|
|
74
72
|
@typing.overload
|
|
@@ -79,24 +77,22 @@ class BaseReturnManager(ABCReturnManager[EventT]):
|
|
|
79
77
|
]: ...
|
|
80
78
|
|
|
81
79
|
@typing.overload
|
|
82
|
-
def register_manager(
|
|
80
|
+
def register_manager(
|
|
81
|
+
self,
|
|
82
|
+
return_type: tuple[type[T], ...],
|
|
83
|
+
) -> typing.Callable[
|
|
83
84
|
[typing.Callable[[tuple[T, ...], EventT, Context], typing.Awaitable[typing.Any]]],
|
|
84
85
|
Manager,
|
|
85
86
|
]: ...
|
|
86
87
|
|
|
87
88
|
def register_manager(
|
|
88
|
-
self,
|
|
89
|
+
self,
|
|
90
|
+
return_type: type[T] | tuple[type[T], ...],
|
|
89
91
|
) -> typing.Callable[
|
|
90
|
-
[
|
|
91
|
-
typing.Callable[
|
|
92
|
-
[T | tuple[T, ...], EventT, Context], typing.Awaitable[typing.Any]
|
|
93
|
-
]
|
|
94
|
-
],
|
|
92
|
+
[typing.Callable[[T | tuple[T, ...], EventT, Context], typing.Awaitable[typing.Any]]],
|
|
95
93
|
Manager,
|
|
96
94
|
]:
|
|
97
|
-
def wrapper(
|
|
98
|
-
func: typing.Callable[[T, EventT, Context], typing.Awaitable]
|
|
99
|
-
) -> Manager:
|
|
95
|
+
def wrapper(func: typing.Callable[[T, EventT, Context], typing.Awaitable]) -> Manager:
|
|
100
96
|
manager = Manager(get_union_types(return_type) or (return_type,), func) # type: ignore
|
|
101
97
|
setattr(self.__class__, func.__name__, manager)
|
|
102
98
|
return manager
|
|
@@ -16,23 +16,21 @@ class MessageReturnManager(BaseReturnManager[MessageCute]):
|
|
|
16
16
|
@register_manager(list | tuple)
|
|
17
17
|
@staticmethod
|
|
18
18
|
async def seq_manager(
|
|
19
|
-
value: list[str] | tuple[str, ...],
|
|
19
|
+
value: list[str] | tuple[str, ...],
|
|
20
|
+
event: MessageCute,
|
|
21
|
+
ctx: Context,
|
|
20
22
|
) -> None:
|
|
21
23
|
for message in value:
|
|
22
24
|
await event.answer(message)
|
|
23
25
|
|
|
24
26
|
@register_manager(dict)
|
|
25
27
|
@staticmethod
|
|
26
|
-
async def dict_manager(
|
|
27
|
-
value: dict[str, typing.Any], event: MessageCute, ctx: Context
|
|
28
|
-
) -> None:
|
|
28
|
+
async def dict_manager(value: dict[str, typing.Any], event: MessageCute, ctx: Context) -> None:
|
|
29
29
|
await event.answer(**value)
|
|
30
30
|
|
|
31
31
|
@register_manager(HTMLFormatter)
|
|
32
32
|
@staticmethod
|
|
33
|
-
async def htmlformatter_manager(
|
|
34
|
-
value: HTMLFormatter, event: MessageCute, ctx: Context
|
|
35
|
-
) -> None:
|
|
33
|
+
async def htmlformatter_manager(value: HTMLFormatter, event: MessageCute, ctx: Context) -> None:
|
|
36
34
|
await event.answer(value, parse_mode=HTMLFormatter.PARSE_MODE)
|
|
37
35
|
|
|
38
36
|
|
|
@@ -63,9 +63,7 @@ class BaseView(ABCView, typing.Generic[EventType]):
|
|
|
63
63
|
for base in cls.__dict__.get("__orig_bases__", ()):
|
|
64
64
|
if issubclass(typing.get_origin(base) or base, ABCView):
|
|
65
65
|
for generic_type in typing.get_args(base):
|
|
66
|
-
if issubclass(
|
|
67
|
-
typing.get_origin(generic_type) or generic_type, BaseCute
|
|
68
|
-
):
|
|
66
|
+
if issubclass(typing.get_origin(generic_type) or generic_type, BaseCute):
|
|
69
67
|
return Some(generic_type)
|
|
70
68
|
return Nothing()
|
|
71
69
|
|
|
@@ -12,16 +12,12 @@ from .inline_query import InlineQueryView
|
|
|
12
12
|
from .message import MessageView
|
|
13
13
|
from .raw import RawEventView
|
|
14
14
|
|
|
15
|
-
CallbackQueryViewT = typing.TypeVar(
|
|
16
|
-
"CallbackQueryViewT", bound=ABCView, default=CallbackQueryView
|
|
17
|
-
)
|
|
15
|
+
CallbackQueryViewT = typing.TypeVar("CallbackQueryViewT", bound=ABCView, default=CallbackQueryView)
|
|
18
16
|
ChatJoinRequestViewT = typing.TypeVar(
|
|
19
17
|
"ChatJoinRequestViewT", bound=ABCView, default=ChatJoinRequestView
|
|
20
18
|
)
|
|
21
19
|
ChatMemberViewT = typing.TypeVar("ChatMemberViewT", bound=ABCView, default=ChatMemberView)
|
|
22
|
-
InlineQueryViewT = typing.TypeVar(
|
|
23
|
-
"InlineQueryViewT", bound=ABCView, default=InlineQueryView
|
|
24
|
-
)
|
|
20
|
+
InlineQueryViewT = typing.TypeVar("InlineQueryViewT", bound=ABCView, default=InlineQueryView)
|
|
25
21
|
MessageViewT = typing.TypeVar("MessageViewT", bound=ABCView, default=MessageView)
|
|
26
22
|
RawEventViewT = typing.TypeVar("RawEventViewT", bound=ABCView, default=RawEventView)
|
|
27
23
|
|
|
@@ -100,11 +96,7 @@ class ViewBox(
|
|
|
100
96
|
def get_views(self) -> dict[str, ABCView]:
|
|
101
97
|
"""Get all views."""
|
|
102
98
|
|
|
103
|
-
return {
|
|
104
|
-
name: view
|
|
105
|
-
for name, view in self.__dict__.items()
|
|
106
|
-
if isinstance(view, ABCView)
|
|
107
|
-
}
|
|
99
|
+
return {name: view for name, view in self.__dict__.items() if isinstance(view, ABCView)}
|
|
108
100
|
|
|
109
101
|
|
|
110
102
|
__all__ = ("ViewBox",)
|
|
@@ -41,9 +41,7 @@ class RawEventView(BaseView[UpdateCute]):
|
|
|
41
41
|
update_type: UpdateType,
|
|
42
42
|
*rules: ABCRule[UpdateCute],
|
|
43
43
|
dataclass: type[T],
|
|
44
|
-
) -> typing.Callable[
|
|
45
|
-
[FuncType[T]], FuncHandler[UpdateCute, FuncType[T], ErrorHandler[T]]
|
|
46
|
-
]: ...
|
|
44
|
+
) -> typing.Callable[[FuncType[T]], FuncHandler[UpdateCute, FuncType[T], ErrorHandler[T]]]: ...
|
|
47
45
|
|
|
48
46
|
@typing.overload
|
|
49
47
|
def __call__(
|
|
@@ -64,9 +62,7 @@ class RawEventView(BaseView[UpdateCute]):
|
|
|
64
62
|
dataclass: type[T],
|
|
65
63
|
error_handler: ErrorHandlerT,
|
|
66
64
|
is_blocking: bool = True,
|
|
67
|
-
) -> typing.Callable[
|
|
68
|
-
[FuncType[T]], FuncHandler[UpdateCute, FuncType[T], ErrorHandlerT]
|
|
69
|
-
]: ...
|
|
65
|
+
) -> typing.Callable[[FuncType[T]], FuncHandler[UpdateCute, FuncType[T], ErrorHandlerT]]: ...
|
|
70
66
|
|
|
71
67
|
@typing.overload
|
|
72
68
|
def __call__(
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
from .machine import
|
|
1
|
+
from .machine import WaiterMachine
|
|
2
2
|
from .middleware import WaiterMiddleware
|
|
3
3
|
from .short_state import ShortState
|
|
4
4
|
|
|
5
5
|
__all__ = (
|
|
6
6
|
"ShortState",
|
|
7
|
-
"ShortStateStorage",
|
|
8
7
|
"WaiterMachine",
|
|
9
8
|
"WaiterMiddleware",
|
|
10
9
|
)
|
|
@@ -1,79 +1,23 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import datetime
|
|
3
3
|
import typing
|
|
4
|
-
from collections import deque
|
|
5
4
|
|
|
6
5
|
from telegrinder.api.abc import ABCAPI
|
|
7
6
|
from telegrinder.bot.dispatch.context import Context
|
|
8
7
|
from telegrinder.bot.rules.abc import ABCRule
|
|
8
|
+
from telegrinder.tools.limited_dict import LimitedDict
|
|
9
|
+
from telegrinder.types import Update
|
|
9
10
|
|
|
10
11
|
from .middleware import WaiterMiddleware
|
|
11
12
|
from .short_state import Behaviour, EventModel, ShortState
|
|
12
13
|
|
|
13
|
-
T = typing.TypeVar("T")
|
|
14
|
-
|
|
15
|
-
Storage: typing.TypeAlias = dict[str, "ShortStateStorage"]
|
|
16
|
-
Identificator: typing.TypeAlias = str | int
|
|
17
|
-
|
|
18
14
|
if typing.TYPE_CHECKING:
|
|
19
15
|
from telegrinder.bot.dispatch.view.abc import ABCStateView, BaseStateView
|
|
20
16
|
|
|
17
|
+
T = typing.TypeVar("T")
|
|
21
18
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
super().__init__()
|
|
25
|
-
self.maxlimit = maxlimit
|
|
26
|
-
self.queue: deque[Identificator] = deque(maxlen=maxlimit)
|
|
27
|
-
|
|
28
|
-
def __repr__(self) -> str:
|
|
29
|
-
return "<{}: {}, (current={} | maxlimit={})>".format(
|
|
30
|
-
self.__class__.__name__,
|
|
31
|
-
super().__repr__(),
|
|
32
|
-
len(self.queue),
|
|
33
|
-
self.maxlimit,
|
|
34
|
-
)
|
|
35
|
-
|
|
36
|
-
def __setitem__(self, key: Identificator, value: ShortState[EventModel], /) -> None:
|
|
37
|
-
self.add(key, value)
|
|
38
|
-
|
|
39
|
-
def __delitem__(self, key: Identificator, /) -> None:
|
|
40
|
-
self.pop(key, None)
|
|
41
|
-
|
|
42
|
-
def add(self, id: Identificator, short_state: ShortState[EventModel]) -> None:
|
|
43
|
-
if len(self.queue) >= self.maxlimit:
|
|
44
|
-
self.pop(self.queue.popleft(), None)
|
|
45
|
-
if id not in self.queue:
|
|
46
|
-
self.queue.append(id)
|
|
47
|
-
super().__setitem__(id, short_state)
|
|
48
|
-
|
|
49
|
-
def clear(self) -> None:
|
|
50
|
-
self.queue.clear()
|
|
51
|
-
super().clear()
|
|
52
|
-
|
|
53
|
-
def setdefault(self, id: Identificator, default: ShortState[EventModel]) -> ShortState[EventModel]:
|
|
54
|
-
if id in self:
|
|
55
|
-
return self[id]
|
|
56
|
-
self.add(id, default)
|
|
57
|
-
return default
|
|
58
|
-
|
|
59
|
-
def pop(self, id: Identificator, default: T): # type: ignore
|
|
60
|
-
if id in self.queue:
|
|
61
|
-
self.queue.remove(id)
|
|
62
|
-
return super().pop(id, default)
|
|
63
|
-
|
|
64
|
-
def popitem(self) -> tuple[Identificator, ShortState[EventModel]]:
|
|
65
|
-
item = super().popitem()
|
|
66
|
-
self.queue.remove(item[0])
|
|
67
|
-
return item
|
|
68
|
-
|
|
69
|
-
def update(
|
|
70
|
-
self,
|
|
71
|
-
mapping: typing.Mapping[Identificator, ShortState[EventModel]] | None = None,
|
|
72
|
-
/,
|
|
73
|
-
**kwargs: ShortState[EventModel],
|
|
74
|
-
) -> None:
|
|
75
|
-
for key, value in (mapping if mapping is not None else kwargs).items():
|
|
76
|
-
self.add(key, value)
|
|
19
|
+
Identificator: typing.TypeAlias = str | int
|
|
20
|
+
Storage: typing.TypeAlias = dict[str, LimitedDict[Identificator, ShortState[EventModel]]]
|
|
77
21
|
|
|
78
22
|
|
|
79
23
|
class WaiterMachine:
|
|
@@ -90,6 +34,7 @@ class WaiterMachine:
|
|
|
90
34
|
self,
|
|
91
35
|
state_view: "ABCStateView[EventModel]",
|
|
92
36
|
id: Identificator,
|
|
37
|
+
update: Update,
|
|
93
38
|
**context: typing.Any,
|
|
94
39
|
) -> None:
|
|
95
40
|
view_name = state_view.__class__.__name__
|
|
@@ -100,8 +45,7 @@ class WaiterMachine:
|
|
|
100
45
|
if short_state is None:
|
|
101
46
|
raise LookupError(
|
|
102
47
|
"Waiter with identificator {} is not found for view {!r}".format(
|
|
103
|
-
id,
|
|
104
|
-
view_name,
|
|
48
|
+
id, view_name
|
|
105
49
|
)
|
|
106
50
|
)
|
|
107
51
|
|
|
@@ -114,8 +58,9 @@ class WaiterMachine:
|
|
|
114
58
|
|
|
115
59
|
await self.call_behaviour(
|
|
116
60
|
state_view,
|
|
117
|
-
short_state.on_drop_behaviour,
|
|
118
61
|
short_state.event,
|
|
62
|
+
update,
|
|
63
|
+
behaviour=short_state.on_drop_behaviour,
|
|
119
64
|
**context,
|
|
120
65
|
)
|
|
121
66
|
|
|
@@ -126,14 +71,18 @@ class WaiterMachine:
|
|
|
126
71
|
*rules: ABCRule[EventModel],
|
|
127
72
|
default: Behaviour = None,
|
|
128
73
|
on_drop: Behaviour = None,
|
|
129
|
-
expiration: datetime.timedelta | int |
|
|
130
|
-
short_state_storage: ShortStateStorage[EventModel] | None = None,
|
|
74
|
+
expiration: datetime.timedelta | int | None = None,
|
|
131
75
|
) -> tuple[EventModel, Context]:
|
|
132
76
|
if isinstance(expiration, int | float):
|
|
133
77
|
expiration = datetime.timedelta(seconds=expiration)
|
|
134
78
|
|
|
135
|
-
api: ABCAPI
|
|
136
|
-
|
|
79
|
+
api: ABCAPI
|
|
80
|
+
key: Identificator
|
|
81
|
+
api, key = (
|
|
82
|
+
linked
|
|
83
|
+
if isinstance(linked, tuple)
|
|
84
|
+
else (linked.ctx_api, state_view.get_state_key(linked))
|
|
85
|
+
) # type: ignore
|
|
137
86
|
if not key:
|
|
138
87
|
raise RuntimeError("Unable to get state key.")
|
|
139
88
|
|
|
@@ -150,9 +99,9 @@ class WaiterMachine:
|
|
|
150
99
|
view_name = state_view.__class__.__name__
|
|
151
100
|
if view_name not in self.storage:
|
|
152
101
|
state_view.middlewares.insert(0, WaiterMiddleware(self, state_view))
|
|
153
|
-
self.storage[view_name] =
|
|
102
|
+
self.storage[view_name] = LimitedDict()
|
|
154
103
|
|
|
155
|
-
self.storage[view_name]
|
|
104
|
+
self.storage[view_name][key] = short_state
|
|
156
105
|
await event.wait()
|
|
157
106
|
|
|
158
107
|
e, ctx = getattr(event, "context")
|
|
@@ -162,15 +111,27 @@ class WaiterMachine:
|
|
|
162
111
|
async def call_behaviour(
|
|
163
112
|
self,
|
|
164
113
|
view: "ABCStateView[EventModel]",
|
|
165
|
-
behaviour: Behaviour,
|
|
166
114
|
event: asyncio.Event | EventModel,
|
|
115
|
+
update: Update,
|
|
116
|
+
behaviour: Behaviour[EventModel] | None = None,
|
|
167
117
|
**context: typing.Any,
|
|
168
118
|
) -> None:
|
|
119
|
+
# TODO: support param view as a behaviour
|
|
120
|
+
|
|
121
|
+
ctx = Context(**context)
|
|
122
|
+
|
|
123
|
+
if isinstance(event, asyncio.Event):
|
|
124
|
+
event, preset_ctx = typing.cast(
|
|
125
|
+
tuple[EventModel, Context],
|
|
126
|
+
getattr(event, "context"),
|
|
127
|
+
)
|
|
128
|
+
ctx.update(preset_ctx)
|
|
129
|
+
|
|
169
130
|
if behaviour is None:
|
|
170
131
|
return
|
|
171
|
-
# TODO: add behaviour check
|
|
172
|
-
# TODO: support view as a behaviour
|
|
173
|
-
await behaviour.run(event, context) # type: ignore
|
|
174
132
|
|
|
133
|
+
if await behaviour.check(event.api, update, ctx):
|
|
134
|
+
await behaviour.run(event, ctx)
|
|
135
|
+
|
|
175
136
|
|
|
176
137
|
__all__ = ("WaiterMachine",)
|