telegrinder 0.1.dev20__py3-none-any.whl → 0.1.dev158__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of telegrinder might be problematic. Click here for more details.
- telegrinder/__init__.py +129 -22
- telegrinder/api/__init__.py +11 -2
- telegrinder/api/abc.py +25 -9
- telegrinder/api/api.py +29 -24
- telegrinder/api/error.py +14 -4
- telegrinder/api/response.py +11 -7
- telegrinder/bot/__init__.py +68 -7
- telegrinder/bot/bot.py +30 -24
- telegrinder/bot/cute_types/__init__.py +11 -1
- telegrinder/bot/cute_types/base.py +47 -0
- telegrinder/bot/cute_types/callback_query.py +64 -14
- telegrinder/bot/cute_types/inline_query.py +22 -16
- telegrinder/bot/cute_types/message.py +145 -53
- telegrinder/bot/cute_types/update.py +23 -0
- telegrinder/bot/dispatch/__init__.py +56 -3
- telegrinder/bot/dispatch/abc.py +9 -7
- telegrinder/bot/dispatch/composition.py +74 -0
- telegrinder/bot/dispatch/context.py +71 -0
- telegrinder/bot/dispatch/dispatch.py +86 -49
- telegrinder/bot/dispatch/handler/__init__.py +3 -0
- telegrinder/bot/dispatch/handler/abc.py +11 -5
- telegrinder/bot/dispatch/handler/func.py +41 -32
- telegrinder/bot/dispatch/handler/message_reply.py +46 -0
- telegrinder/bot/dispatch/middleware/__init__.py +2 -0
- telegrinder/bot/dispatch/middleware/abc.py +10 -4
- telegrinder/bot/dispatch/process.py +53 -49
- telegrinder/bot/dispatch/return_manager/__init__.py +19 -0
- telegrinder/bot/dispatch/return_manager/abc.py +95 -0
- telegrinder/bot/dispatch/return_manager/callback_query.py +19 -0
- telegrinder/bot/dispatch/return_manager/inline_query.py +14 -0
- telegrinder/bot/dispatch/return_manager/message.py +25 -0
- telegrinder/bot/dispatch/view/__init__.py +14 -2
- telegrinder/bot/dispatch/view/abc.py +121 -2
- telegrinder/bot/dispatch/view/box.py +38 -0
- telegrinder/bot/dispatch/view/callback_query.py +13 -39
- telegrinder/bot/dispatch/view/inline_query.py +11 -39
- telegrinder/bot/dispatch/view/message.py +11 -47
- telegrinder/bot/dispatch/waiter_machine/__init__.py +9 -0
- telegrinder/bot/dispatch/waiter_machine/machine.py +116 -0
- telegrinder/bot/dispatch/waiter_machine/middleware.py +76 -0
- telegrinder/bot/dispatch/waiter_machine/short_state.py +37 -0
- telegrinder/bot/polling/__init__.py +2 -0
- telegrinder/bot/polling/abc.py +11 -4
- telegrinder/bot/polling/polling.py +89 -40
- telegrinder/bot/rules/__init__.py +91 -5
- telegrinder/bot/rules/abc.py +81 -63
- telegrinder/bot/rules/adapter/__init__.py +11 -0
- telegrinder/bot/rules/adapter/abc.py +21 -0
- telegrinder/bot/rules/adapter/errors.py +5 -0
- telegrinder/bot/rules/adapter/event.py +43 -0
- telegrinder/bot/rules/adapter/raw_update.py +24 -0
- telegrinder/bot/rules/callback_data.py +159 -38
- telegrinder/bot/rules/command.py +116 -0
- telegrinder/bot/rules/enum_text.py +28 -0
- telegrinder/bot/rules/func.py +17 -17
- telegrinder/bot/rules/fuzzy.py +13 -10
- telegrinder/bot/rules/inline.py +61 -0
- telegrinder/bot/rules/integer.py +12 -7
- telegrinder/bot/rules/is_from.py +148 -7
- telegrinder/bot/rules/markup.py +21 -18
- telegrinder/bot/rules/mention.py +17 -0
- telegrinder/bot/rules/message_entities.py +33 -0
- telegrinder/bot/rules/regex.py +27 -19
- telegrinder/bot/rules/rule_enum.py +74 -0
- telegrinder/bot/rules/start.py +25 -13
- telegrinder/bot/rules/text.py +23 -14
- telegrinder/bot/scenario/__init__.py +2 -0
- telegrinder/bot/scenario/abc.py +12 -5
- telegrinder/bot/scenario/checkbox.py +48 -30
- telegrinder/bot/scenario/choice.py +16 -10
- telegrinder/client/__init__.py +2 -0
- telegrinder/client/abc.py +8 -21
- telegrinder/client/aiohttp.py +30 -21
- telegrinder/model.py +68 -37
- telegrinder/modules.py +189 -21
- telegrinder/msgspec_json.py +14 -0
- telegrinder/msgspec_utils.py +207 -0
- telegrinder/node/__init__.py +31 -0
- telegrinder/node/attachment.py +71 -0
- telegrinder/node/base.py +93 -0
- telegrinder/node/composer.py +71 -0
- telegrinder/node/container.py +22 -0
- telegrinder/node/message.py +18 -0
- telegrinder/node/rule.py +56 -0
- telegrinder/node/source.py +31 -0
- telegrinder/node/text.py +13 -0
- telegrinder/node/tools/__init__.py +3 -0
- telegrinder/node/tools/generator.py +40 -0
- telegrinder/node/update.py +12 -0
- telegrinder/rules.py +1 -1
- telegrinder/tools/__init__.py +165 -4
- telegrinder/tools/buttons.py +75 -51
- telegrinder/tools/error_handler/__init__.py +8 -0
- telegrinder/tools/error_handler/abc.py +30 -0
- telegrinder/tools/error_handler/error_handler.py +156 -0
- telegrinder/tools/formatting/__init__.py +81 -3
- telegrinder/tools/formatting/html.py +283 -37
- telegrinder/tools/formatting/links.py +32 -0
- telegrinder/tools/formatting/spec_html_formats.py +121 -0
- telegrinder/tools/global_context/__init__.py +12 -0
- telegrinder/tools/global_context/abc.py +66 -0
- telegrinder/tools/global_context/global_context.py +451 -0
- telegrinder/tools/global_context/telegrinder_ctx.py +25 -0
- telegrinder/tools/i18n/__init__.py +12 -0
- telegrinder/tools/i18n/base.py +31 -0
- telegrinder/tools/i18n/middleware/__init__.py +3 -0
- telegrinder/tools/i18n/middleware/base.py +26 -0
- telegrinder/tools/i18n/simple.py +48 -0
- telegrinder/tools/inline_query.py +684 -0
- telegrinder/tools/kb_set/__init__.py +2 -0
- telegrinder/tools/kb_set/base.py +3 -0
- telegrinder/tools/kb_set/yaml.py +28 -17
- telegrinder/tools/keyboard.py +84 -62
- telegrinder/tools/loop_wrapper/__init__.py +4 -0
- telegrinder/tools/loop_wrapper/abc.py +18 -0
- telegrinder/tools/loop_wrapper/loop_wrapper.py +132 -0
- telegrinder/tools/magic.py +48 -23
- telegrinder/tools/parse_mode.py +1 -2
- telegrinder/types/__init__.py +1 -0
- telegrinder/types/enums.py +651 -0
- telegrinder/types/methods.py +3920 -1251
- telegrinder/types/objects.py +4702 -1718
- {telegrinder-0.1.dev20.dist-info → telegrinder-0.1.dev158.dist-info}/LICENSE +2 -1
- telegrinder-0.1.dev158.dist-info/METADATA +108 -0
- telegrinder-0.1.dev158.dist-info/RECORD +126 -0
- {telegrinder-0.1.dev20.dist-info → telegrinder-0.1.dev158.dist-info}/WHEEL +1 -1
- telegrinder/bot/dispatch/waiter.py +0 -38
- telegrinder/result.py +0 -38
- telegrinder/tools/formatting/abc.py +0 -52
- telegrinder/tools/formatting/markdown.py +0 -57
- telegrinder-0.1.dev20.dist-info/METADATA +0 -22
- telegrinder-0.1.dev20.dist-info/RECORD +0 -71
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from telegrinder.bot.cute_types import MessageCute
|
|
2
|
+
from telegrinder.bot.dispatch.context import Context
|
|
3
|
+
|
|
4
|
+
from .abc import BaseReturnManager, register_manager
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class MessageReturnManager(BaseReturnManager[MessageCute]):
|
|
8
|
+
@register_manager(str)
|
|
9
|
+
@staticmethod
|
|
10
|
+
async def str_manager(value: str, event: MessageCute, ctx: Context) -> None:
|
|
11
|
+
await event.answer(value)
|
|
12
|
+
|
|
13
|
+
@register_manager(list | tuple)
|
|
14
|
+
@staticmethod
|
|
15
|
+
async def seq_manager(value: list[str] | tuple[str, ...], event: MessageCute, ctx: Context) -> None:
|
|
16
|
+
for message in value:
|
|
17
|
+
await event.answer(message)
|
|
18
|
+
|
|
19
|
+
@register_manager(dict)
|
|
20
|
+
@staticmethod
|
|
21
|
+
async def dict_manager(value: dict, event: MessageCute, ctx: Context) -> None:
|
|
22
|
+
await event.answer(**value)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
__all__ = ("MessageReturnManager",)
|
|
@@ -1,4 +1,16 @@
|
|
|
1
|
-
from .abc import ABCView
|
|
2
|
-
from .
|
|
1
|
+
from .abc import ABCStateView, ABCView, BaseStateView, BaseView
|
|
2
|
+
from .box import ViewBox
|
|
3
3
|
from .callback_query import CallbackQueryView
|
|
4
4
|
from .inline_query import InlineQueryView
|
|
5
|
+
from .message import MessageView
|
|
6
|
+
|
|
7
|
+
__all__ = (
|
|
8
|
+
"ABCView",
|
|
9
|
+
"ABCStateView",
|
|
10
|
+
"BaseView",
|
|
11
|
+
"BaseStateView",
|
|
12
|
+
"CallbackQueryView",
|
|
13
|
+
"InlineQueryView",
|
|
14
|
+
"MessageView",
|
|
15
|
+
"ViewBox",
|
|
16
|
+
)
|
|
@@ -1,6 +1,21 @@
|
|
|
1
|
+
import typing
|
|
1
2
|
from abc import ABC, abstractmethod
|
|
3
|
+
|
|
4
|
+
from fntypes.co import Nothing, Some
|
|
5
|
+
|
|
2
6
|
from telegrinder.api.abc import ABCAPI
|
|
3
|
-
from telegrinder.
|
|
7
|
+
from telegrinder.bot.cute_types.base import BaseCute
|
|
8
|
+
from telegrinder.bot.dispatch.handler.abc import ABCHandler
|
|
9
|
+
from telegrinder.bot.dispatch.handler.func import ErrorHandlerT, FuncHandler
|
|
10
|
+
from telegrinder.bot.dispatch.middleware.abc import ABCMiddleware
|
|
11
|
+
from telegrinder.bot.dispatch.process import process_inner
|
|
12
|
+
from telegrinder.bot.dispatch.return_manager.abc import ABCReturnManager
|
|
13
|
+
from telegrinder.bot.rules.abc import ABCRule
|
|
14
|
+
from telegrinder.model import Model
|
|
15
|
+
from telegrinder.msgspec_utils import Option
|
|
16
|
+
from telegrinder.types.objects import Update
|
|
17
|
+
|
|
18
|
+
EventType = typing.TypeVar("EventType", bound=BaseCute)
|
|
4
19
|
|
|
5
20
|
|
|
6
21
|
class ABCView(ABC):
|
|
@@ -13,5 +28,109 @@ class ABCView(ABC):
|
|
|
13
28
|
pass
|
|
14
29
|
|
|
15
30
|
@abstractmethod
|
|
16
|
-
|
|
31
|
+
def load(self, external: typing.Self) -> None:
|
|
17
32
|
pass
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class ABCStateView(ABCView, typing.Generic[EventType]):
|
|
36
|
+
@abstractmethod
|
|
37
|
+
def get_state_key(self, event: EventType) -> int | None:
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
def __repr__(self) -> str:
|
|
41
|
+
return "<{!r}: {}>".format(
|
|
42
|
+
self.__class__.__name__,
|
|
43
|
+
", ".join(f"{k}={v!r}" for k, v in self.__dict__.items())
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class BaseView(ABCView, typing.Generic[EventType]):
|
|
48
|
+
auto_rules: list[ABCRule[EventType]]
|
|
49
|
+
handlers: list[ABCHandler[EventType]]
|
|
50
|
+
middlewares: list[ABCMiddleware[EventType]]
|
|
51
|
+
return_manager: ABCReturnManager[EventType]
|
|
52
|
+
|
|
53
|
+
@classmethod
|
|
54
|
+
def get_event_type(cls) -> Option[type[EventType]]:
|
|
55
|
+
for base in cls.__dict__.get("__orig_bases__", ()):
|
|
56
|
+
if issubclass(typing.get_origin(base) or base, ABCView):
|
|
57
|
+
for generic_type in typing.get_args(base):
|
|
58
|
+
if issubclass(typing.get_origin(generic_type) or generic_type, BaseCute):
|
|
59
|
+
return Some(generic_type)
|
|
60
|
+
return Nothing()
|
|
61
|
+
|
|
62
|
+
@classmethod
|
|
63
|
+
def get_event_raw(cls, update: Update) -> Option[Model]:
|
|
64
|
+
match cls.get_event_type():
|
|
65
|
+
case Some(event_type):
|
|
66
|
+
for field in update.__struct_fields__:
|
|
67
|
+
event_raw = getattr(update, field)
|
|
68
|
+
if isinstance(event_raw, Some | Nothing):
|
|
69
|
+
event_raw = event_raw.unwrap_or_none()
|
|
70
|
+
if event_raw is not None and issubclass(event_type, event_raw.__class__):
|
|
71
|
+
return Some(event_raw)
|
|
72
|
+
return Nothing()
|
|
73
|
+
|
|
74
|
+
def __call__(
|
|
75
|
+
self,
|
|
76
|
+
*rules: ABCRule[EventType],
|
|
77
|
+
is_blocking: bool = True,
|
|
78
|
+
error_handler: ErrorHandlerT | None = None,
|
|
79
|
+
):
|
|
80
|
+
def wrapper(
|
|
81
|
+
func: typing.Callable[
|
|
82
|
+
typing.Concatenate[EventType, ...],
|
|
83
|
+
typing.Coroutine,
|
|
84
|
+
]
|
|
85
|
+
):
|
|
86
|
+
func_handler = FuncHandler(
|
|
87
|
+
func,
|
|
88
|
+
[*self.auto_rules, *rules],
|
|
89
|
+
is_blocking,
|
|
90
|
+
dataclass=None,
|
|
91
|
+
error_handler=error_handler,
|
|
92
|
+
)
|
|
93
|
+
self.handlers.append(func_handler)
|
|
94
|
+
return func_handler
|
|
95
|
+
|
|
96
|
+
return wrapper
|
|
97
|
+
|
|
98
|
+
def register_middleware(self, *args: typing.Any, **kwargs: typing.Any):
|
|
99
|
+
def wrapper(cls: type[ABCMiddleware[EventType]]):
|
|
100
|
+
self.middlewares.append(cls(*args, **kwargs))
|
|
101
|
+
return cls
|
|
102
|
+
|
|
103
|
+
return wrapper
|
|
104
|
+
|
|
105
|
+
async def check(self, event: Update) -> bool:
|
|
106
|
+
return bool(self.get_event_raw(event))
|
|
107
|
+
|
|
108
|
+
async def process(self, event: Update, api: ABCAPI) -> bool:
|
|
109
|
+
event_raw = self.get_event_raw(event).unwrap()
|
|
110
|
+
event_type = self.get_event_type().unwrap()
|
|
111
|
+
return await process_inner(
|
|
112
|
+
event_type(**event_raw.to_dict(), api=api),
|
|
113
|
+
event,
|
|
114
|
+
self.middlewares,
|
|
115
|
+
self.handlers,
|
|
116
|
+
self.return_manager,
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
def load(self, external: typing.Self) -> None:
|
|
120
|
+
self.auto_rules.extend(external.auto_rules)
|
|
121
|
+
self.handlers.extend(external.handlers)
|
|
122
|
+
self.middlewares.extend(external.middlewares)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class BaseStateView(ABCStateView[EventType], BaseView[EventType], ABC, typing.Generic[EventType]):
|
|
126
|
+
@abstractmethod
|
|
127
|
+
def get_state_key(self, event: EventType) -> int | None:
|
|
128
|
+
pass
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
__all__ = (
|
|
132
|
+
"ABCView",
|
|
133
|
+
"ABCStateView",
|
|
134
|
+
"BaseView",
|
|
135
|
+
"BaseStateView",
|
|
136
|
+
)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
|
|
3
|
+
import typing_extensions as typing
|
|
4
|
+
|
|
5
|
+
from .abc import ABCView
|
|
6
|
+
from .callback_query import CallbackQueryView
|
|
7
|
+
from .inline_query import InlineQueryView
|
|
8
|
+
from .message import MessageView
|
|
9
|
+
|
|
10
|
+
CallbackQueryViewT = typing.TypeVar(
|
|
11
|
+
"CallbackQueryViewT", bound=ABCView, default=CallbackQueryView
|
|
12
|
+
)
|
|
13
|
+
InlineQueryViewT = typing.TypeVar(
|
|
14
|
+
"InlineQueryViewT", bound=ABCView, default=InlineQueryView
|
|
15
|
+
)
|
|
16
|
+
MessageViewT = typing.TypeVar("MessageViewT", bound=ABCView, default=MessageView)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclasses.dataclass(kw_only=True)
|
|
20
|
+
class ViewBox(typing.Generic[CallbackQueryViewT, InlineQueryViewT, MessageViewT]):
|
|
21
|
+
callback_query: CallbackQueryViewT = dataclasses.field( # type: ignore
|
|
22
|
+
default_factory=lambda: CallbackQueryView(),
|
|
23
|
+
)
|
|
24
|
+
inline_query: InlineQueryViewT = dataclasses.field( # type: ignore
|
|
25
|
+
default_factory=lambda: InlineQueryView(),
|
|
26
|
+
)
|
|
27
|
+
message: MessageViewT = dataclasses.field( # type: ignore
|
|
28
|
+
default_factory=lambda: MessageView(),
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
def get_views(self) -> dict[str, ABCView]:
|
|
32
|
+
return {
|
|
33
|
+
name: view for name, view in self.__dict__.items()
|
|
34
|
+
if isinstance(view, ABCView)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
__all__ = ("ViewBox",)
|
|
@@ -1,46 +1,20 @@
|
|
|
1
|
-
from .
|
|
2
|
-
from telegrinder.bot.dispatch.handler import ABCHandler, FuncHandler
|
|
3
|
-
from telegrinder.bot.dispatch.waiter import Waiter
|
|
4
|
-
from telegrinder.bot.dispatch.middleware import ABCMiddleware
|
|
5
|
-
from telegrinder.bot.rules import ABCRule
|
|
6
|
-
from telegrinder.bot.cute_types import CallbackQueryCute
|
|
7
|
-
from telegrinder.api.abc import ABCAPI
|
|
8
|
-
from telegrinder.bot.dispatch.waiter import WithWaiter
|
|
9
|
-
from telegrinder.bot.dispatch.process import process_waiters, process_inner
|
|
10
|
-
from telegrinder.types import Update
|
|
11
|
-
import typing
|
|
12
|
-
|
|
1
|
+
from fntypes.option import Some
|
|
13
2
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
self.auto_rules: list[ABCRule] = []
|
|
17
|
-
self.handlers: typing.List[ABCHandler[CallbackQueryCute]] = []
|
|
18
|
-
self.middlewares: typing.List[ABCMiddleware[CallbackQueryCute]] = []
|
|
19
|
-
self.short_waiters: typing.Dict[int, Waiter] = {}
|
|
20
|
-
|
|
21
|
-
def __call__(self, *rules: ABCRule, is_blocking: bool = True):
|
|
22
|
-
def wrapper(func: typing.Callable[..., typing.Coroutine]):
|
|
23
|
-
self.handlers.append(
|
|
24
|
-
FuncHandler(func, [*self.auto_rules, *rules], is_blocking, dataclass=None)
|
|
25
|
-
)
|
|
26
|
-
return func
|
|
3
|
+
from telegrinder.bot.cute_types import CallbackQueryCute
|
|
4
|
+
from telegrinder.bot.dispatch.return_manager import CallbackQueryReturnManager
|
|
27
5
|
|
|
28
|
-
|
|
6
|
+
from .abc import BaseStateView
|
|
29
7
|
|
|
30
|
-
async def check(self, event: Update) -> bool:
|
|
31
|
-
return bool(event.callback_query)
|
|
32
8
|
|
|
33
|
-
|
|
34
|
-
|
|
9
|
+
class CallbackQueryView(BaseStateView[CallbackQueryCute]):
|
|
10
|
+
def __init__(self) -> None:
|
|
11
|
+
self.auto_rules = []
|
|
12
|
+
self.handlers = []
|
|
13
|
+
self.middlewares = []
|
|
14
|
+
self.return_manager = CallbackQueryReturnManager()
|
|
35
15
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
):
|
|
39
|
-
return
|
|
16
|
+
def get_state_key(self, event: CallbackQueryCute) -> int | None:
|
|
17
|
+
return event.message.map(lambda variative: variative.v.message_id).unwrap_or_none()
|
|
40
18
|
|
|
41
|
-
return await process_inner(query, event, self.middlewares, self.handlers)
|
|
42
19
|
|
|
43
|
-
|
|
44
|
-
self.handlers.extend(external.handlers)
|
|
45
|
-
self.middlewares.extend(external.middlewares)
|
|
46
|
-
external.short_waiters = self.short_waiters
|
|
20
|
+
__all__ = ("CallbackQueryView",)
|
|
@@ -1,46 +1,18 @@
|
|
|
1
|
-
from .abc import ABCView
|
|
2
|
-
from telegrinder.bot.dispatch.handler import ABCHandler, FuncHandler
|
|
3
|
-
from telegrinder.bot.dispatch.waiter import Waiter
|
|
4
|
-
from telegrinder.bot.dispatch.middleware import ABCMiddleware
|
|
5
|
-
from telegrinder.bot.rules import ABCRule
|
|
6
1
|
from telegrinder.bot.cute_types import InlineQueryCute
|
|
7
|
-
from telegrinder.
|
|
8
|
-
from telegrinder.bot.dispatch.waiter import WithWaiter
|
|
9
|
-
from telegrinder.bot.dispatch.process import process_waiters, process_inner
|
|
10
|
-
from telegrinder.types import Update
|
|
11
|
-
import typing
|
|
2
|
+
from telegrinder.bot.dispatch.return_manager import InlineQueryReturnManager
|
|
12
3
|
|
|
4
|
+
from .abc import BaseStateView
|
|
13
5
|
|
|
14
|
-
class InlineQueryView(ABCView, WithWaiter[str, InlineQueryCute]):
|
|
15
|
-
def __init__(self):
|
|
16
|
-
self.auto_rules: list[ABCRule] = []
|
|
17
|
-
self.handlers: typing.List[ABCHandler[InlineQueryCute]] = []
|
|
18
|
-
self.middlewares: typing.List[ABCMiddleware[InlineQueryCute]] = []
|
|
19
|
-
self.short_waiters: typing.Dict[str, Waiter] = {}
|
|
20
6
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
7
|
+
class InlineQueryView(BaseStateView[InlineQueryCute]):
|
|
8
|
+
def __init__(self) -> None:
|
|
9
|
+
self.auto_rules = []
|
|
10
|
+
self.handlers = []
|
|
11
|
+
self.middlewares = []
|
|
12
|
+
self.return_manager = InlineQueryReturnManager()
|
|
27
13
|
|
|
28
|
-
|
|
14
|
+
def get_state_key(self, event: InlineQueryCute) -> int | None:
|
|
15
|
+
return event.from_.id
|
|
29
16
|
|
|
30
|
-
def load(self, external: "InlineQueryView"):
|
|
31
|
-
self.handlers.extend(external.handlers)
|
|
32
|
-
self.middlewares.extend(external.middlewares)
|
|
33
|
-
external.short_waiters = self.short_waiters
|
|
34
17
|
|
|
35
|
-
|
|
36
|
-
return bool(event.inline_query)
|
|
37
|
-
|
|
38
|
-
async def process(self, event: Update, api: ABCAPI):
|
|
39
|
-
query = InlineQueryCute(**event.inline_query.to_dict(), api=api)
|
|
40
|
-
|
|
41
|
-
if await process_waiters(
|
|
42
|
-
self.short_waiters, query.id, query, event, query.answer
|
|
43
|
-
):
|
|
44
|
-
return
|
|
45
|
-
|
|
46
|
-
return await process_inner(query, event, self.middlewares, self.handlers)
|
|
18
|
+
__all__ = ("InlineQueryView",)
|
|
@@ -1,54 +1,18 @@
|
|
|
1
|
-
from .abc import ABCView
|
|
2
|
-
from telegrinder.bot.dispatch.handler import ABCHandler, FuncHandler
|
|
3
|
-
from telegrinder.bot.dispatch.waiter import Waiter
|
|
4
|
-
from telegrinder.bot.dispatch.middleware import ABCMiddleware
|
|
5
|
-
from telegrinder.bot.rules import ABCRule
|
|
6
1
|
from telegrinder.bot.cute_types import MessageCute
|
|
7
|
-
from telegrinder.
|
|
8
|
-
from telegrinder.bot.dispatch.waiter import WithWaiter, DefaultWaiterHandler
|
|
9
|
-
from telegrinder.bot.dispatch.process import process_waiters, process_inner
|
|
10
|
-
from telegrinder.types import Update
|
|
11
|
-
import typing
|
|
2
|
+
from telegrinder.bot.dispatch.return_manager import MessageReturnManager
|
|
12
3
|
|
|
4
|
+
from .abc import BaseStateView
|
|
13
5
|
|
|
14
|
-
class MessageView(ABCView, WithWaiter[int, MessageCute]):
|
|
15
|
-
def __init__(self):
|
|
16
|
-
self.auto_rules: list[ABCRule] = []
|
|
17
|
-
self.handlers: typing.List[ABCHandler[MessageCute]] = []
|
|
18
|
-
self.middlewares: typing.List[ABCMiddleware[MessageCute]] = []
|
|
19
|
-
self.short_waiters: typing.Dict[int, Waiter] = {}
|
|
20
6
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
7
|
+
class MessageView(BaseStateView[MessageCute]):
|
|
8
|
+
def __init__(self) -> None:
|
|
9
|
+
self.auto_rules = []
|
|
10
|
+
self.handlers = []
|
|
11
|
+
self.middlewares = []
|
|
12
|
+
self.return_manager = MessageReturnManager()
|
|
27
13
|
|
|
28
|
-
|
|
14
|
+
def get_state_key(self, event: MessageCute) -> int | None:
|
|
15
|
+
return event.chat.id
|
|
29
16
|
|
|
30
|
-
def load(self, external: "MessageView"):
|
|
31
|
-
self.handlers.extend(external.handlers)
|
|
32
|
-
self.middlewares.extend(external.middlewares)
|
|
33
|
-
external.short_waiters = self.short_waiters
|
|
34
17
|
|
|
35
|
-
|
|
36
|
-
return bool(event.message)
|
|
37
|
-
|
|
38
|
-
async def process(self, event: Update, api: ABCAPI):
|
|
39
|
-
msg = MessageCute(**event.message.to_dict(), api=api)
|
|
40
|
-
|
|
41
|
-
if await process_waiters(
|
|
42
|
-
self.short_waiters, msg.chat.id, msg, event, msg.answer
|
|
43
|
-
):
|
|
44
|
-
return
|
|
45
|
-
|
|
46
|
-
return await process_inner(msg, event, self.middlewares, self.handlers)
|
|
47
|
-
|
|
48
|
-
async def wait_for_message(
|
|
49
|
-
self,
|
|
50
|
-
chat_id: int,
|
|
51
|
-
*rules: ABCRule,
|
|
52
|
-
default: typing.Optional[typing.Union[DefaultWaiterHandler, str]] = None
|
|
53
|
-
) -> typing.Tuple["MessageCute", dict]:
|
|
54
|
-
return await self.wait_for_answer(chat_id, *self.auto_rules, *rules, default=default)
|
|
18
|
+
__all__ = ("MessageView",)
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import datetime
|
|
3
|
+
import typing
|
|
4
|
+
|
|
5
|
+
from telegrinder.api.abc import ABCAPI
|
|
6
|
+
from telegrinder.bot.dispatch.context import Context
|
|
7
|
+
from telegrinder.bot.rules.abc import ABCRule
|
|
8
|
+
|
|
9
|
+
from .middleware import WaiterMiddleware
|
|
10
|
+
from .short_state import Behaviour, EventModel, ShortState
|
|
11
|
+
|
|
12
|
+
Identificator: typing.TypeAlias = str | int
|
|
13
|
+
Storage: typing.TypeAlias = dict[str, dict[Identificator, "ShortState"]]
|
|
14
|
+
|
|
15
|
+
if typing.TYPE_CHECKING:
|
|
16
|
+
from telegrinder.bot.dispatch.view.abc import ABCStateView, BaseStateView
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class WaiterMachine:
|
|
20
|
+
def __init__(self) -> None:
|
|
21
|
+
self.storage: Storage = {}
|
|
22
|
+
|
|
23
|
+
async def drop(
|
|
24
|
+
self,
|
|
25
|
+
state_view: "ABCStateView[EventModel]",
|
|
26
|
+
id: Identificator,
|
|
27
|
+
**context,
|
|
28
|
+
) -> None:
|
|
29
|
+
view_name = state_view.__class__.__name__
|
|
30
|
+
if view_name not in self.storage:
|
|
31
|
+
raise LookupError("No record of view {!r} found".format(view_name))
|
|
32
|
+
|
|
33
|
+
short_state = self.storage[view_name].pop(id, None)
|
|
34
|
+
if not short_state:
|
|
35
|
+
raise LookupError(
|
|
36
|
+
"Waiter with identificator {} is not found for view {!r}".format(
|
|
37
|
+
id,
|
|
38
|
+
view_name,
|
|
39
|
+
)
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
waiters = typing.cast(
|
|
43
|
+
typing.Iterable[asyncio.Future[typing.Any]],
|
|
44
|
+
short_state.event._waiters # type: ignore
|
|
45
|
+
)
|
|
46
|
+
for future in waiters:
|
|
47
|
+
future.cancel()
|
|
48
|
+
|
|
49
|
+
await self.call_behaviour(
|
|
50
|
+
state_view,
|
|
51
|
+
short_state.on_drop_behaviour,
|
|
52
|
+
short_state.event,
|
|
53
|
+
**context,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
async def wait(
|
|
57
|
+
self,
|
|
58
|
+
state_view: "BaseStateView[EventModel]",
|
|
59
|
+
linked: EventModel | tuple[ABCAPI, Identificator],
|
|
60
|
+
*rules: ABCRule[EventModel],
|
|
61
|
+
default: Behaviour = None,
|
|
62
|
+
on_drop: Behaviour = None,
|
|
63
|
+
expiration: datetime.timedelta | int | None = None,
|
|
64
|
+
) -> tuple[EventModel, Context]:
|
|
65
|
+
if isinstance(expiration, int):
|
|
66
|
+
expiration = datetime.timedelta(seconds=expiration)
|
|
67
|
+
|
|
68
|
+
api: ABCAPI
|
|
69
|
+
key: Identificator
|
|
70
|
+
event = asyncio.Event()
|
|
71
|
+
if isinstance(linked, tuple):
|
|
72
|
+
api, key = linked
|
|
73
|
+
else:
|
|
74
|
+
api, key = linked.ctx_api, state_view.get_state_key(linked) # type: ignore
|
|
75
|
+
if not key:
|
|
76
|
+
raise RuntimeError("Unable to get state key.")
|
|
77
|
+
|
|
78
|
+
short_state = ShortState(
|
|
79
|
+
key,
|
|
80
|
+
ctx_api=api,
|
|
81
|
+
event=event,
|
|
82
|
+
rules=rules,
|
|
83
|
+
expiration=expiration,
|
|
84
|
+
default_behaviour=default,
|
|
85
|
+
on_drop_behaviour=on_drop,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
view_name = state_view.__class__.__name__
|
|
89
|
+
if view_name not in self.storage:
|
|
90
|
+
state_view.middlewares.insert(0, WaiterMiddleware(self, state_view))
|
|
91
|
+
self.storage[view_name] = {}
|
|
92
|
+
|
|
93
|
+
self.storage[view_name][key] = short_state
|
|
94
|
+
|
|
95
|
+
await event.wait()
|
|
96
|
+
|
|
97
|
+
e, ctx = getattr(event, "context")
|
|
98
|
+
self.storage[view_name].pop(key)
|
|
99
|
+
|
|
100
|
+
return e, ctx
|
|
101
|
+
|
|
102
|
+
async def call_behaviour(
|
|
103
|
+
self,
|
|
104
|
+
view: "ABCStateView[EventModel]",
|
|
105
|
+
behaviour: Behaviour,
|
|
106
|
+
event: asyncio.Event | EventModel,
|
|
107
|
+
**context,
|
|
108
|
+
) -> None:
|
|
109
|
+
if behaviour is None:
|
|
110
|
+
return
|
|
111
|
+
# TODO: add behaviour check
|
|
112
|
+
# TODO: support view as a behaviour
|
|
113
|
+
await behaviour.run(event)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
__all__ = ("WaiterMachine",)
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import typing
|
|
3
|
+
|
|
4
|
+
from telegrinder.bot.cute_types.base import BaseCute
|
|
5
|
+
from telegrinder.bot.dispatch.context import Context
|
|
6
|
+
from telegrinder.bot.dispatch.handler.func import FuncHandler
|
|
7
|
+
from telegrinder.bot.dispatch.middleware.abc import ABCMiddleware
|
|
8
|
+
from telegrinder.bot.dispatch.view.abc import ABCStateView
|
|
9
|
+
|
|
10
|
+
if typing.TYPE_CHECKING:
|
|
11
|
+
from .machine import WaiterMachine
|
|
12
|
+
from .short_state import ShortState
|
|
13
|
+
|
|
14
|
+
EventType = typing.TypeVar("EventType", bound=BaseCute)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class WaiterMiddleware(ABCMiddleware[EventType]):
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
machine: "WaiterMachine",
|
|
21
|
+
view: ABCStateView[EventType],
|
|
22
|
+
) -> None:
|
|
23
|
+
self.machine = machine
|
|
24
|
+
self.view = view
|
|
25
|
+
|
|
26
|
+
async def pre(self, event: EventType, ctx: Context) -> bool:
|
|
27
|
+
if not self.view or not hasattr(self.view, "get_state_key"):
|
|
28
|
+
raise RuntimeError(
|
|
29
|
+
"WaiterMiddleware cannot be used inside a view which doesn't "
|
|
30
|
+
"provide get_state_key (ABCStateView Protocol)."
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
view_name = self.view.__class__.__name__
|
|
34
|
+
if view_name not in self.machine.storage:
|
|
35
|
+
return True
|
|
36
|
+
|
|
37
|
+
key = self.view.get_state_key(event)
|
|
38
|
+
if key is None:
|
|
39
|
+
raise RuntimeError("Unable to get state key.")
|
|
40
|
+
|
|
41
|
+
short_state: typing.Optional["ShortState"] = self.machine.storage[view_name].get(key)
|
|
42
|
+
if not short_state:
|
|
43
|
+
return True
|
|
44
|
+
|
|
45
|
+
if (
|
|
46
|
+
short_state.expiration is not None
|
|
47
|
+
and datetime.datetime.now() >= short_state.expiration
|
|
48
|
+
):
|
|
49
|
+
await self.machine.drop(self.view, short_state.key)
|
|
50
|
+
return True
|
|
51
|
+
|
|
52
|
+
handler = FuncHandler(
|
|
53
|
+
self.pass_runtime, list(short_state.rules), dataclass=None
|
|
54
|
+
)
|
|
55
|
+
handler.ctx.set("short_state", short_state)
|
|
56
|
+
result = await handler.check(event.ctx_api, ctx.raw_update, ctx)
|
|
57
|
+
|
|
58
|
+
if result is True:
|
|
59
|
+
await handler.run(event)
|
|
60
|
+
|
|
61
|
+
elif short_state.default_behaviour is not None:
|
|
62
|
+
await self.machine.call_behaviour(
|
|
63
|
+
self.view,
|
|
64
|
+
short_state.default_behaviour,
|
|
65
|
+
event,
|
|
66
|
+
**handler.ctx,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
return False
|
|
70
|
+
|
|
71
|
+
async def pass_runtime(self, event: EventType, short_state: "ShortState[EventType]", ctx: Context) -> None:
|
|
72
|
+
setattr(short_state.event, "context", (event, ctx))
|
|
73
|
+
short_state.event.set()
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
__all__ = ("WaiterMiddleware",)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import datetime
|
|
3
|
+
import typing
|
|
4
|
+
|
|
5
|
+
from telegrinder.api.abc import ABCAPI
|
|
6
|
+
from telegrinder.bot.cute_types import BaseCute
|
|
7
|
+
from telegrinder.bot.dispatch.handler.abc import ABCHandler
|
|
8
|
+
from telegrinder.bot.rules.abc import ABCRule
|
|
9
|
+
|
|
10
|
+
if typing.TYPE_CHECKING:
|
|
11
|
+
from .machine import Identificator
|
|
12
|
+
|
|
13
|
+
EventModel = typing.TypeVar("EventModel", bound=BaseCute)
|
|
14
|
+
Behaviour = ABCHandler | None
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ShortState(typing.Generic[EventModel]):
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
key: "Identificator",
|
|
21
|
+
ctx_api: ABCAPI,
|
|
22
|
+
event: asyncio.Event,
|
|
23
|
+
rules: tuple[ABCRule[EventModel], ...],
|
|
24
|
+
expiration: datetime.timedelta | None = None,
|
|
25
|
+
default_behaviour: Behaviour | None = None,
|
|
26
|
+
on_drop_behaviour: Behaviour | None = None,
|
|
27
|
+
) -> None:
|
|
28
|
+
self.key = key
|
|
29
|
+
self.ctx_api = ctx_api
|
|
30
|
+
self.event = event
|
|
31
|
+
self.rules = rules
|
|
32
|
+
self.default_behaviour = default_behaviour
|
|
33
|
+
self.expiration = (datetime.datetime.now() + expiration) if expiration else None
|
|
34
|
+
self.on_drop_behaviour = on_drop_behaviour
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
__all__ = ("ShortState",)
|
telegrinder/bot/polling/abc.py
CHANGED
|
@@ -1,18 +1,25 @@
|
|
|
1
1
|
import typing
|
|
2
2
|
from abc import ABC, abstractmethod
|
|
3
|
+
|
|
4
|
+
import msgspec
|
|
5
|
+
|
|
3
6
|
from telegrinder.types import Update
|
|
4
|
-
from telegrinder.model import Raw
|
|
5
7
|
|
|
6
8
|
|
|
7
9
|
class ABCPolling(ABC):
|
|
10
|
+
offset: int
|
|
11
|
+
|
|
8
12
|
@abstractmethod
|
|
9
|
-
async def get_updates(self) ->
|
|
13
|
+
async def get_updates(self) -> list[msgspec.Raw]:
|
|
10
14
|
pass
|
|
11
15
|
|
|
12
16
|
@abstractmethod
|
|
13
|
-
async def listen(self) -> typing.
|
|
14
|
-
|
|
17
|
+
async def listen(self) -> typing.AsyncGenerator[list[Update], None]:
|
|
18
|
+
yield []
|
|
15
19
|
|
|
16
20
|
@abstractmethod
|
|
17
21
|
def stop(self) -> None:
|
|
18
22
|
pass
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
__all__ = ("ABCPolling",)
|