telegrinder 0.3.1__py3-none-any.whl → 0.3.3__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 +144 -144
- telegrinder/api/__init__.py +8 -8
- telegrinder/api/api.py +93 -93
- telegrinder/api/error.py +16 -16
- telegrinder/api/response.py +20 -20
- telegrinder/api/token.py +36 -36
- telegrinder/bot/__init__.py +66 -66
- telegrinder/bot/bot.py +76 -76
- telegrinder/bot/cute_types/__init__.py +17 -17
- telegrinder/bot/cute_types/base.py +258 -234
- telegrinder/bot/cute_types/callback_query.py +385 -382
- telegrinder/bot/cute_types/chat_join_request.py +61 -61
- telegrinder/bot/cute_types/chat_member_updated.py +160 -160
- telegrinder/bot/cute_types/inline_query.py +43 -53
- telegrinder/bot/cute_types/message.py +2637 -2631
- telegrinder/bot/cute_types/update.py +109 -75
- telegrinder/bot/cute_types/utils.py +95 -95
- telegrinder/bot/dispatch/__init__.py +55 -55
- telegrinder/bot/dispatch/abc.py +77 -77
- telegrinder/bot/dispatch/context.py +98 -92
- telegrinder/bot/dispatch/dispatch.py +202 -201
- telegrinder/bot/dispatch/handler/__init__.py +13 -13
- telegrinder/bot/dispatch/handler/abc.py +24 -24
- telegrinder/bot/dispatch/handler/audio_reply.py +44 -44
- telegrinder/bot/dispatch/handler/base.py +57 -57
- telegrinder/bot/dispatch/handler/document_reply.py +44 -44
- telegrinder/bot/dispatch/handler/func.py +135 -123
- telegrinder/bot/dispatch/handler/media_group_reply.py +43 -43
- telegrinder/bot/dispatch/handler/message_reply.py +36 -36
- telegrinder/bot/dispatch/handler/photo_reply.py +44 -44
- telegrinder/bot/dispatch/handler/sticker_reply.py +37 -37
- telegrinder/bot/dispatch/handler/video_reply.py +44 -44
- telegrinder/bot/dispatch/middleware/__init__.py +3 -3
- telegrinder/bot/dispatch/middleware/abc.py +16 -16
- telegrinder/bot/dispatch/process.py +132 -132
- telegrinder/bot/dispatch/return_manager/__init__.py +13 -13
- telegrinder/bot/dispatch/return_manager/abc.py +108 -108
- telegrinder/bot/dispatch/return_manager/callback_query.py +20 -20
- telegrinder/bot/dispatch/return_manager/inline_query.py +15 -15
- telegrinder/bot/dispatch/return_manager/message.py +36 -36
- telegrinder/bot/dispatch/view/__init__.py +13 -13
- telegrinder/bot/dispatch/view/abc.py +41 -41
- telegrinder/bot/dispatch/view/base.py +200 -211
- telegrinder/bot/dispatch/view/box.py +129 -129
- telegrinder/bot/dispatch/view/callback_query.py +17 -17
- telegrinder/bot/dispatch/view/chat_join_request.py +16 -16
- telegrinder/bot/dispatch/view/chat_member.py +39 -39
- telegrinder/bot/dispatch/view/inline_query.py +17 -17
- telegrinder/bot/dispatch/view/message.py +44 -44
- telegrinder/bot/dispatch/view/raw.py +114 -118
- telegrinder/bot/dispatch/waiter_machine/__init__.py +17 -17
- telegrinder/bot/dispatch/waiter_machine/actions.py +13 -13
- telegrinder/bot/dispatch/waiter_machine/hasher/__init__.py +8 -8
- telegrinder/bot/dispatch/waiter_machine/hasher/callback.py +55 -55
- telegrinder/bot/dispatch/waiter_machine/hasher/hasher.py +57 -57
- telegrinder/bot/dispatch/waiter_machine/hasher/message.py +51 -51
- telegrinder/bot/dispatch/waiter_machine/hasher/state.py +19 -19
- telegrinder/bot/dispatch/waiter_machine/machine.py +167 -170
- telegrinder/bot/dispatch/waiter_machine/middleware.py +89 -89
- telegrinder/bot/dispatch/waiter_machine/short_state.py +68 -65
- telegrinder/bot/polling/__init__.py +4 -4
- telegrinder/bot/polling/abc.py +25 -25
- telegrinder/bot/polling/polling.py +131 -131
- telegrinder/bot/rules/__init__.py +62 -62
- telegrinder/bot/rules/abc.py +213 -238
- telegrinder/bot/rules/adapter/__init__.py +9 -9
- telegrinder/bot/rules/adapter/abc.py +29 -29
- telegrinder/bot/rules/adapter/errors.py +5 -5
- telegrinder/bot/rules/adapter/event.py +67 -76
- telegrinder/bot/rules/adapter/node.py +48 -48
- telegrinder/bot/rules/adapter/raw_update.py +30 -30
- telegrinder/bot/rules/callback_data.py +170 -171
- telegrinder/bot/rules/chat_join.py +46 -48
- telegrinder/bot/rules/command.py +126 -126
- telegrinder/bot/rules/enum_text.py +36 -36
- telegrinder/bot/rules/func.py +26 -26
- telegrinder/bot/rules/fuzzy.py +24 -24
- telegrinder/bot/rules/inline.py +60 -60
- telegrinder/bot/rules/integer.py +20 -20
- telegrinder/bot/rules/is_from.py +127 -127
- telegrinder/bot/rules/markup.py +43 -43
- telegrinder/bot/rules/mention.py +14 -14
- telegrinder/bot/rules/message.py +17 -17
- telegrinder/bot/rules/message_entities.py +35 -35
- telegrinder/bot/rules/node.py +27 -27
- telegrinder/bot/rules/regex.py +37 -37
- telegrinder/bot/rules/rule_enum.py +72 -72
- telegrinder/bot/rules/start.py +42 -42
- telegrinder/bot/rules/state.py +37 -37
- telegrinder/bot/rules/text.py +33 -33
- telegrinder/bot/rules/update.py +15 -15
- telegrinder/bot/scenario/__init__.py +5 -5
- telegrinder/bot/scenario/abc.py +19 -19
- telegrinder/bot/scenario/checkbox.py +167 -147
- telegrinder/bot/scenario/choice.py +46 -44
- telegrinder/client/__init__.py +4 -4
- telegrinder/client/abc.py +75 -75
- telegrinder/client/aiohttp.py +130 -130
- telegrinder/model.py +295 -244
- telegrinder/modules.py +237 -237
- telegrinder/msgspec_json.py +14 -14
- telegrinder/msgspec_utils.py +410 -410
- telegrinder/node/__init__.py +7 -3
- telegrinder/node/attachment.py +87 -87
- telegrinder/node/base.py +166 -144
- telegrinder/node/callback_query.py +53 -14
- telegrinder/node/command.py +33 -33
- telegrinder/node/composer.py +198 -184
- telegrinder/node/container.py +27 -27
- telegrinder/node/event.py +65 -73
- telegrinder/node/me.py +16 -16
- telegrinder/node/message.py +14 -14
- telegrinder/node/polymorphic.py +48 -52
- telegrinder/node/rule.py +76 -76
- telegrinder/node/scope.py +38 -38
- telegrinder/node/source.py +71 -71
- telegrinder/node/text.py +41 -21
- telegrinder/node/tools/__init__.py +3 -3
- telegrinder/node/tools/generator.py +40 -40
- telegrinder/node/update.py +15 -15
- telegrinder/rules.py +0 -0
- telegrinder/tools/__init__.py +74 -74
- telegrinder/tools/buttons.py +79 -79
- telegrinder/tools/error_handler/__init__.py +7 -7
- telegrinder/tools/error_handler/abc.py +33 -33
- telegrinder/tools/error_handler/error.py +9 -9
- telegrinder/tools/error_handler/error_handler.py +193 -193
- telegrinder/tools/formatting/__init__.py +46 -46
- telegrinder/tools/formatting/html.py +283 -283
- telegrinder/tools/formatting/links.py +33 -33
- telegrinder/tools/formatting/spec_html_formats.py +111 -111
- telegrinder/tools/functional.py +12 -12
- telegrinder/tools/global_context/__init__.py +7 -7
- telegrinder/tools/global_context/abc.py +63 -63
- telegrinder/tools/global_context/global_context.py +412 -412
- telegrinder/tools/global_context/telegrinder_ctx.py +27 -27
- telegrinder/tools/i18n/__init__.py +7 -7
- telegrinder/tools/i18n/abc.py +30 -30
- telegrinder/tools/i18n/middleware/__init__.py +3 -3
- telegrinder/tools/i18n/middleware/abc.py +25 -25
- telegrinder/tools/i18n/simple.py +43 -43
- telegrinder/tools/kb_set/__init__.py +4 -4
- telegrinder/tools/kb_set/base.py +15 -15
- telegrinder/tools/kb_set/yaml.py +63 -63
- telegrinder/tools/keyboard.py +132 -132
- telegrinder/tools/limited_dict.py +37 -37
- telegrinder/tools/loop_wrapper/__init__.py +4 -4
- telegrinder/tools/loop_wrapper/abc.py +15 -15
- telegrinder/tools/loop_wrapper/loop_wrapper.py +224 -216
- telegrinder/tools/magic.py +157 -157
- telegrinder/tools/parse_mode.py +6 -6
- telegrinder/tools/state_storage/__init__.py +4 -4
- telegrinder/tools/state_storage/abc.py +35 -35
- telegrinder/tools/state_storage/memory.py +25 -25
- telegrinder/types/__init__.py +260 -260
- telegrinder/types/enums.py +701 -701
- telegrinder/types/methods.py +4633 -4633
- telegrinder/types/objects.py +8561 -6541
- telegrinder/verification_utils.py +32 -32
- {telegrinder-0.3.1.dist-info → telegrinder-0.3.3.dist-info}/LICENSE +22 -22
- {telegrinder-0.3.1.dist-info → telegrinder-0.3.3.dist-info}/METADATA +1 -1
- telegrinder-0.3.3.dist-info/RECORD +164 -0
- telegrinder-0.3.1.dist-info/RECORD +0 -164
- {telegrinder-0.3.1.dist-info → telegrinder-0.3.3.dist-info}/WHEEL +0 -0
|
@@ -1,76 +1,67 @@
|
|
|
1
|
-
import typing
|
|
2
|
-
|
|
3
|
-
from fntypes.result import Error, Ok, Result
|
|
4
|
-
|
|
5
|
-
from telegrinder.api import API
|
|
6
|
-
from telegrinder.bot.cute_types.base import BaseCute
|
|
7
|
-
from telegrinder.bot.dispatch.context import Context
|
|
8
|
-
from telegrinder.bot.rules.adapter.abc import ABCAdapter
|
|
9
|
-
from telegrinder.bot.rules.adapter.errors import AdapterError
|
|
10
|
-
from telegrinder.
|
|
11
|
-
from telegrinder.types.enums import UpdateType
|
|
12
|
-
from telegrinder.types.objects import Model, Update
|
|
13
|
-
|
|
14
|
-
ToCute = typing.TypeVar("ToCute", bound=BaseCute)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class EventAdapter(ABCAdapter[Update, ToCute]):
|
|
18
|
-
ADAPTED_VALUE_KEY: str = "_adapted_cute_event"
|
|
19
|
-
|
|
20
|
-
def __init__(self, event: UpdateType | type[Model], cute_model: type[ToCute]) -> None:
|
|
21
|
-
self.event = event
|
|
22
|
-
self.cute_model = cute_model
|
|
23
|
-
|
|
24
|
-
def __repr__(self) -> str:
|
|
25
|
-
if isinstance(self.event, str):
|
|
26
|
-
raw_update_type = Update.__annotations__.get(self.event, "Unknown")
|
|
27
|
-
raw_update_type = (
|
|
28
|
-
typing.get_args(raw_update_type)[0].__forward_arg__
|
|
29
|
-
if typing.get_args(raw_update_type)
|
|
30
|
-
else raw_update_type
|
|
31
|
-
)
|
|
32
|
-
else:
|
|
33
|
-
raw_update_type = self.event.__name__
|
|
34
|
-
|
|
35
|
-
return "<{}: adapt Update -> {} -> {}>".format(
|
|
36
|
-
self.__class__.__name__,
|
|
37
|
-
raw_update_type,
|
|
38
|
-
self.cute_model.__name__,
|
|
39
|
-
)
|
|
40
|
-
|
|
41
|
-
async def adapt(self, api: API, update: Update, context: Context) -> Result[ToCute, AdapterError]:
|
|
42
|
-
if self.ADAPTED_VALUE_KEY in context:
|
|
43
|
-
return Ok(context[self.ADAPTED_VALUE_KEY])
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
adapted = event
|
|
69
|
-
else:
|
|
70
|
-
adapted = self.cute_model.from_update(event, bound_api=api)
|
|
71
|
-
|
|
72
|
-
context[self.ADAPTED_VALUE_KEY] = adapted
|
|
73
|
-
return Ok(adapted) # type: ignore
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
__all__ = ("EventAdapter",)
|
|
1
|
+
import typing
|
|
2
|
+
|
|
3
|
+
from fntypes.result import Error, Ok, Result
|
|
4
|
+
|
|
5
|
+
from telegrinder.api.api import API
|
|
6
|
+
from telegrinder.bot.cute_types.base import BaseCute
|
|
7
|
+
from telegrinder.bot.dispatch.context import Context
|
|
8
|
+
from telegrinder.bot.rules.adapter.abc import ABCAdapter
|
|
9
|
+
from telegrinder.bot.rules.adapter.errors import AdapterError
|
|
10
|
+
from telegrinder.bot.rules.adapter.raw_update import RawUpdateAdapter
|
|
11
|
+
from telegrinder.types.enums import UpdateType
|
|
12
|
+
from telegrinder.types.objects import Model, Update
|
|
13
|
+
|
|
14
|
+
ToCute = typing.TypeVar("ToCute", bound=BaseCute)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class EventAdapter(ABCAdapter[Update, ToCute]):
|
|
18
|
+
ADAPTED_VALUE_KEY: str = "_adapted_cute_event"
|
|
19
|
+
|
|
20
|
+
def __init__(self, event: UpdateType | type[Model], cute_model: type[ToCute]) -> None:
|
|
21
|
+
self.event = event
|
|
22
|
+
self.cute_model = cute_model
|
|
23
|
+
|
|
24
|
+
def __repr__(self) -> str:
|
|
25
|
+
if isinstance(self.event, str):
|
|
26
|
+
raw_update_type = Update.__annotations__.get(self.event, "Unknown")
|
|
27
|
+
raw_update_type = (
|
|
28
|
+
typing.get_args(raw_update_type)[0].__forward_arg__
|
|
29
|
+
if typing.get_args(raw_update_type)
|
|
30
|
+
else raw_update_type
|
|
31
|
+
)
|
|
32
|
+
else:
|
|
33
|
+
raw_update_type = self.event.__name__
|
|
34
|
+
|
|
35
|
+
return "<{}: adapt Update -> {} -> {}>".format(
|
|
36
|
+
self.__class__.__name__,
|
|
37
|
+
raw_update_type,
|
|
38
|
+
self.cute_model.__name__,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
async def adapt(self, api: API, update: Update, context: Context) -> Result[ToCute, AdapterError]:
|
|
42
|
+
if self.ADAPTED_VALUE_KEY in context:
|
|
43
|
+
return Ok(context[self.ADAPTED_VALUE_KEY])
|
|
44
|
+
|
|
45
|
+
match await RawUpdateAdapter().adapt(api, update, context):
|
|
46
|
+
case Ok(update_cute):
|
|
47
|
+
incoming_update = None
|
|
48
|
+
if isinstance(self.event, UpdateType) and self.event == update_cute.update_type:
|
|
49
|
+
incoming_update = update_cute.incoming_update
|
|
50
|
+
if not isinstance(self.event, UpdateType) and (event := update_cute.get_event(self.event)):
|
|
51
|
+
incoming_update = update_cute.get_event(self.event).unwrap_or_none()
|
|
52
|
+
|
|
53
|
+
if incoming_update is not None:
|
|
54
|
+
adapted = (
|
|
55
|
+
typing.cast(ToCute, incoming_update)
|
|
56
|
+
if isinstance(incoming_update, BaseCute)
|
|
57
|
+
else self.cute_model.from_update(incoming_update, bound_api=api)
|
|
58
|
+
)
|
|
59
|
+
context[self.ADAPTED_VALUE_KEY] = adapted
|
|
60
|
+
return Ok(adapted)
|
|
61
|
+
|
|
62
|
+
return Error(AdapterError(f"Update is not an {self.event!r}."))
|
|
63
|
+
case Error(_) as err:
|
|
64
|
+
return err
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
__all__ = ("EventAdapter",)
|
|
@@ -1,48 +1,48 @@
|
|
|
1
|
-
import typing_extensions as typing
|
|
2
|
-
from fntypes.result import Error, Ok, Result
|
|
3
|
-
|
|
4
|
-
from telegrinder.api import API
|
|
5
|
-
from telegrinder.bot.dispatch.context import Context
|
|
6
|
-
from telegrinder.bot.rules.adapter.abc import ABCAdapter, Event
|
|
7
|
-
from telegrinder.bot.rules.adapter.errors import AdapterError
|
|
8
|
-
from telegrinder.msgspec_utils import repr_type
|
|
9
|
-
from telegrinder.node.composer import NodeSession, compose_nodes
|
|
10
|
-
from telegrinder.types.objects import Update
|
|
11
|
-
|
|
12
|
-
if typing.TYPE_CHECKING:
|
|
13
|
-
from telegrinder.node.base import Node
|
|
14
|
-
|
|
15
|
-
Ts = typing.TypeVarTuple("Ts", default=typing.Unpack[tuple[type["Node"], ...]])
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class NodeAdapter(typing.Generic[*Ts], ABCAdapter[Update, Event[tuple[*Ts]]]):
|
|
19
|
-
def __init__(self, *nodes: *Ts) -> None:
|
|
20
|
-
self.nodes = nodes
|
|
21
|
-
|
|
22
|
-
def __repr__(self) -> str:
|
|
23
|
-
return "<{}: adapt Update -> ({})>".format(
|
|
24
|
-
self.__class__.__name__,
|
|
25
|
-
", ".join(repr_type(node) for node in self.nodes),
|
|
26
|
-
)
|
|
27
|
-
|
|
28
|
-
async def adapt(
|
|
29
|
-
self,
|
|
30
|
-
api: API,
|
|
31
|
-
update: Update,
|
|
32
|
-
context: Context,
|
|
33
|
-
) -> Result[Event[tuple[*Ts]], AdapterError]:
|
|
34
|
-
result = await compose_nodes(
|
|
35
|
-
nodes={str(i): typing.cast(type["Node"], node) for i, node in enumerate(self.nodes)},
|
|
36
|
-
ctx=context,
|
|
37
|
-
data={Update: update, API: api},
|
|
38
|
-
)
|
|
39
|
-
|
|
40
|
-
match result:
|
|
41
|
-
case Ok(collection):
|
|
42
|
-
sessions: list[NodeSession] = list(collection.sessions.values())
|
|
43
|
-
return Ok(Event(tuple(sessions))) # type: ignore
|
|
44
|
-
case Error(err):
|
|
45
|
-
return Error(AdapterError(f"Couldn't compose nodes, error: {err}."))
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
__all__ = ("NodeAdapter",)
|
|
1
|
+
import typing_extensions as typing
|
|
2
|
+
from fntypes.result import Error, Ok, Result
|
|
3
|
+
|
|
4
|
+
from telegrinder.api.api import API
|
|
5
|
+
from telegrinder.bot.dispatch.context import Context
|
|
6
|
+
from telegrinder.bot.rules.adapter.abc import ABCAdapter, Event
|
|
7
|
+
from telegrinder.bot.rules.adapter.errors import AdapterError
|
|
8
|
+
from telegrinder.msgspec_utils import repr_type
|
|
9
|
+
from telegrinder.node.composer import NodeSession, compose_nodes
|
|
10
|
+
from telegrinder.types.objects import Update
|
|
11
|
+
|
|
12
|
+
if typing.TYPE_CHECKING:
|
|
13
|
+
from telegrinder.node.base import Node
|
|
14
|
+
|
|
15
|
+
Ts = typing.TypeVarTuple("Ts", default=typing.Unpack[tuple[type["Node"], ...]])
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class NodeAdapter(typing.Generic[*Ts], ABCAdapter[Update, Event[tuple[*Ts]]]):
|
|
19
|
+
def __init__(self, *nodes: *Ts) -> None:
|
|
20
|
+
self.nodes = nodes
|
|
21
|
+
|
|
22
|
+
def __repr__(self) -> str:
|
|
23
|
+
return "<{}: adapt Update -> ({})>".format(
|
|
24
|
+
self.__class__.__name__,
|
|
25
|
+
", ".join(repr_type(node) for node in self.nodes),
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
async def adapt(
|
|
29
|
+
self,
|
|
30
|
+
api: API,
|
|
31
|
+
update: Update,
|
|
32
|
+
context: Context,
|
|
33
|
+
) -> Result[Event[tuple[*Ts]], AdapterError]:
|
|
34
|
+
result = await compose_nodes(
|
|
35
|
+
nodes={str(i): typing.cast(type["Node"], node) for i, node in enumerate(self.nodes)},
|
|
36
|
+
ctx=context,
|
|
37
|
+
data={Update: update, API: api},
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
match result:
|
|
41
|
+
case Ok(collection):
|
|
42
|
+
sessions: list[NodeSession] = list(collection.sessions.values())
|
|
43
|
+
return Ok(Event(tuple(sessions))) # type: ignore
|
|
44
|
+
case Error(err):
|
|
45
|
+
return Error(AdapterError(f"Couldn't compose nodes, error: {err}."))
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
__all__ = ("NodeAdapter",)
|
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
from fntypes.result import Ok, Result
|
|
2
|
-
|
|
3
|
-
from telegrinder.api import API
|
|
4
|
-
from telegrinder.bot.cute_types.update import UpdateCute
|
|
5
|
-
from telegrinder.bot.dispatch.context import Context
|
|
6
|
-
from telegrinder.bot.rules.adapter.abc import ABCAdapter
|
|
7
|
-
from telegrinder.bot.rules.adapter.errors import AdapterError
|
|
8
|
-
from telegrinder.types.objects import Update
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class RawUpdateAdapter(ABCAdapter[Update, UpdateCute]):
|
|
12
|
-
ADAPTED_VALUE_KEY: str = "_adapted_update_cute"
|
|
13
|
-
|
|
14
|
-
def __repr__(self) -> str:
|
|
15
|
-
return f"<{self.__class__.__name__}: adapt Update -> UpdateCute>"
|
|
16
|
-
|
|
17
|
-
async def adapt(
|
|
18
|
-
self,
|
|
19
|
-
api: API,
|
|
20
|
-
update: Update,
|
|
21
|
-
context: Context,
|
|
22
|
-
) -> Result[UpdateCute, AdapterError]:
|
|
23
|
-
if self.ADAPTED_VALUE_KEY not in context:
|
|
24
|
-
context[self.ADAPTED_VALUE_KEY] = (
|
|
25
|
-
UpdateCute.from_update(update, api) if not isinstance(update, UpdateCute) else update
|
|
26
|
-
)
|
|
27
|
-
return Ok(context[self.ADAPTED_VALUE_KEY])
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
__all__ = ("RawUpdateAdapter",)
|
|
1
|
+
from fntypes.result import Ok, Result
|
|
2
|
+
|
|
3
|
+
from telegrinder.api.api import API
|
|
4
|
+
from telegrinder.bot.cute_types.update import UpdateCute
|
|
5
|
+
from telegrinder.bot.dispatch.context import Context
|
|
6
|
+
from telegrinder.bot.rules.adapter.abc import ABCAdapter
|
|
7
|
+
from telegrinder.bot.rules.adapter.errors import AdapterError
|
|
8
|
+
from telegrinder.types.objects import Update
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class RawUpdateAdapter(ABCAdapter[Update, UpdateCute]):
|
|
12
|
+
ADAPTED_VALUE_KEY: str = "_adapted_update_cute"
|
|
13
|
+
|
|
14
|
+
def __repr__(self) -> str:
|
|
15
|
+
return f"<{self.__class__.__name__}: adapt Update -> UpdateCute>"
|
|
16
|
+
|
|
17
|
+
async def adapt(
|
|
18
|
+
self,
|
|
19
|
+
api: API,
|
|
20
|
+
update: Update,
|
|
21
|
+
context: Context,
|
|
22
|
+
) -> Result[UpdateCute, AdapterError]:
|
|
23
|
+
if self.ADAPTED_VALUE_KEY not in context:
|
|
24
|
+
context[self.ADAPTED_VALUE_KEY] = (
|
|
25
|
+
UpdateCute.from_update(update, api) if not isinstance(update, UpdateCute) else update
|
|
26
|
+
)
|
|
27
|
+
return Ok(context[self.ADAPTED_VALUE_KEY])
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
__all__ = ("RawUpdateAdapter",)
|
|
@@ -1,171 +1,170 @@
|
|
|
1
|
-
import abc
|
|
2
|
-
import inspect
|
|
3
|
-
import typing
|
|
4
|
-
from contextlib import suppress
|
|
5
|
-
|
|
6
|
-
import msgspec
|
|
7
|
-
|
|
8
|
-
from telegrinder.bot.cute_types import CallbackQueryCute
|
|
9
|
-
from telegrinder.bot.dispatch.context import Context
|
|
10
|
-
from telegrinder.bot.rules.adapter import EventAdapter
|
|
11
|
-
from telegrinder.model import decoder
|
|
12
|
-
from telegrinder.tools.buttons import DataclassInstance
|
|
13
|
-
from telegrinder.types.enums import UpdateType
|
|
14
|
-
|
|
15
|
-
from .abc import ABCRule, CheckResult
|
|
16
|
-
from .markup import Markup, PatternLike, check_string
|
|
17
|
-
|
|
18
|
-
CallbackQuery: typing.TypeAlias = CallbackQueryCute
|
|
19
|
-
Validator: typing.TypeAlias = typing.Callable[[typing.Any], bool | typing.Awaitable[bool]]
|
|
20
|
-
MapDict: typing.TypeAlias = dict[str, "typing.Any | type[typing.Any] | Validator | list[MapDict] | MapDict"]
|
|
21
|
-
CallbackMap: typing.TypeAlias = list[tuple[str, "typing.Any | type[typing.Any] | Validator | CallbackMap"]]
|
|
22
|
-
CallbackMapStrict: typing.TypeAlias = list[tuple[str, "Validator | CallbackMapStrict"]]
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
class CallbackQueryRule(ABCRule[CallbackQuery], abc.ABC):
|
|
26
|
-
adapter: EventAdapter[CallbackQuery] = EventAdapter(UpdateType.CALLBACK_QUERY, CallbackQuery)
|
|
27
|
-
|
|
28
|
-
@abc.abstractmethod
|
|
29
|
-
def check(self,
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
callback_data
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
self.
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
"
|
|
164
|
-
"
|
|
165
|
-
"
|
|
166
|
-
"
|
|
167
|
-
"
|
|
168
|
-
"
|
|
169
|
-
"
|
|
170
|
-
|
|
171
|
-
)
|
|
1
|
+
import abc
|
|
2
|
+
import inspect
|
|
3
|
+
import typing
|
|
4
|
+
from contextlib import suppress
|
|
5
|
+
|
|
6
|
+
import msgspec
|
|
7
|
+
|
|
8
|
+
from telegrinder.bot.cute_types import CallbackQueryCute
|
|
9
|
+
from telegrinder.bot.dispatch.context import Context
|
|
10
|
+
from telegrinder.bot.rules.adapter import EventAdapter
|
|
11
|
+
from telegrinder.model import decoder
|
|
12
|
+
from telegrinder.tools.buttons import DataclassInstance
|
|
13
|
+
from telegrinder.types.enums import UpdateType
|
|
14
|
+
|
|
15
|
+
from .abc import ABCRule, CheckResult
|
|
16
|
+
from .markup import Markup, PatternLike, check_string
|
|
17
|
+
|
|
18
|
+
CallbackQuery: typing.TypeAlias = CallbackQueryCute
|
|
19
|
+
Validator: typing.TypeAlias = typing.Callable[[typing.Any], bool | typing.Awaitable[bool]]
|
|
20
|
+
MapDict: typing.TypeAlias = dict[str, "typing.Any | type[typing.Any] | Validator | list[MapDict] | MapDict"]
|
|
21
|
+
CallbackMap: typing.TypeAlias = list[tuple[str, "typing.Any | type[typing.Any] | Validator | CallbackMap"]]
|
|
22
|
+
CallbackMapStrict: typing.TypeAlias = list[tuple[str, "Validator | CallbackMapStrict"]]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class CallbackQueryRule(ABCRule[CallbackQuery], abc.ABC):
|
|
26
|
+
adapter: EventAdapter[CallbackQuery] = EventAdapter(UpdateType.CALLBACK_QUERY, CallbackQuery)
|
|
27
|
+
|
|
28
|
+
@abc.abstractmethod
|
|
29
|
+
def check(self, *args: typing.Any, **kwargs: typing.Any) -> CheckResult: ...
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class HasData(CallbackQueryRule):
|
|
33
|
+
def check(self, event: CallbackQuery) -> bool:
|
|
34
|
+
return bool(event.data.unwrap_or_none())
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class CallbackQueryDataRule(CallbackQueryRule, abc.ABC, requires=[HasData()]):
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class CallbackDataMap(CallbackQueryDataRule):
|
|
42
|
+
def __init__(self, mapping: MapDict, /) -> None:
|
|
43
|
+
self.mapping = self.transform_to_callbacks(
|
|
44
|
+
self.transform_to_map(mapping),
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
@classmethod
|
|
48
|
+
def transform_to_map(cls, mapping: MapDict) -> CallbackMap:
|
|
49
|
+
"""Transforms MapDict to CallbackMap."""
|
|
50
|
+
|
|
51
|
+
callback_map = []
|
|
52
|
+
|
|
53
|
+
for k, v in mapping.items():
|
|
54
|
+
if isinstance(v, dict):
|
|
55
|
+
v = cls.transform_to_map(v)
|
|
56
|
+
callback_map.append((k, v))
|
|
57
|
+
|
|
58
|
+
return callback_map
|
|
59
|
+
|
|
60
|
+
@classmethod
|
|
61
|
+
def transform_to_callbacks(cls, callback_map: CallbackMap) -> CallbackMapStrict:
|
|
62
|
+
"""Transforms `CallbackMap` to `CallbackMapStrict`."""
|
|
63
|
+
|
|
64
|
+
callback_map_result = []
|
|
65
|
+
|
|
66
|
+
for key, value in callback_map:
|
|
67
|
+
if isinstance(value, type):
|
|
68
|
+
validator = (lambda tp: lambda v: isinstance(v, tp))(value)
|
|
69
|
+
elif isinstance(value, list):
|
|
70
|
+
validator = cls.transform_to_callbacks(value)
|
|
71
|
+
elif not callable(value):
|
|
72
|
+
validator = (lambda val: lambda v: val == v)(value)
|
|
73
|
+
else:
|
|
74
|
+
validator = value
|
|
75
|
+
callback_map_result.append((key, validator))
|
|
76
|
+
|
|
77
|
+
return callback_map_result
|
|
78
|
+
|
|
79
|
+
@staticmethod
|
|
80
|
+
async def run_validator(value: typing.Any, validator: Validator) -> bool:
|
|
81
|
+
"""Run async or sync validator."""
|
|
82
|
+
|
|
83
|
+
with suppress(BaseException):
|
|
84
|
+
result = validator(value)
|
|
85
|
+
if inspect.isawaitable(result):
|
|
86
|
+
result = await result
|
|
87
|
+
return result
|
|
88
|
+
|
|
89
|
+
return False
|
|
90
|
+
|
|
91
|
+
@classmethod
|
|
92
|
+
async def match(cls, callback_data: dict[str, typing.Any], callback_map: CallbackMapStrict) -> bool:
|
|
93
|
+
"""Matches callback_data with callback_map recursively."""
|
|
94
|
+
|
|
95
|
+
for key, validator in callback_map:
|
|
96
|
+
if key not in callback_data:
|
|
97
|
+
return False
|
|
98
|
+
|
|
99
|
+
if isinstance(validator, list):
|
|
100
|
+
if not (
|
|
101
|
+
isinstance(callback_data[key], dict) and await cls.match(callback_data[key], validator)
|
|
102
|
+
):
|
|
103
|
+
return False
|
|
104
|
+
|
|
105
|
+
elif not await cls.run_validator(callback_data[key], validator):
|
|
106
|
+
return False
|
|
107
|
+
|
|
108
|
+
return True
|
|
109
|
+
|
|
110
|
+
async def check(self, event: CallbackQuery, ctx: Context) -> bool:
|
|
111
|
+
callback_data = event.decode_callback_data().unwrap_or_none()
|
|
112
|
+
if callback_data is None:
|
|
113
|
+
return False
|
|
114
|
+
if await self.match(callback_data, self.mapping):
|
|
115
|
+
ctx.update(callback_data)
|
|
116
|
+
return True
|
|
117
|
+
return False
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class CallbackDataEq(CallbackQueryDataRule):
|
|
121
|
+
def __init__(self, value: str, /) -> None:
|
|
122
|
+
self.value = value
|
|
123
|
+
|
|
124
|
+
def check(self, event: CallbackQuery) -> bool:
|
|
125
|
+
return event.data.unwrap() == self.value
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
class CallbackDataJsonEq(CallbackQueryDataRule):
|
|
129
|
+
def __init__(self, d: dict[str, typing.Any], /) -> None:
|
|
130
|
+
self.d = d
|
|
131
|
+
|
|
132
|
+
def check(self, event: CallbackQuery) -> bool:
|
|
133
|
+
return event.decode_callback_data().unwrap_or_none() == self.d
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class CallbackDataJsonModel(CallbackQueryDataRule):
|
|
137
|
+
def __init__(
|
|
138
|
+
self,
|
|
139
|
+
model: type[msgspec.Struct] | type[DataclassInstance],
|
|
140
|
+
*,
|
|
141
|
+
alias: str | None = None,
|
|
142
|
+
) -> None:
|
|
143
|
+
self.model = model
|
|
144
|
+
self.alias = alias or "data"
|
|
145
|
+
|
|
146
|
+
def check(self, event: CallbackQuery, ctx: Context) -> bool:
|
|
147
|
+
with suppress(BaseException):
|
|
148
|
+
ctx.set(self.alias, decoder.decode(event.data.unwrap().encode(), type=self.model))
|
|
149
|
+
return True
|
|
150
|
+
return False
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class CallbackDataMarkup(CallbackQueryDataRule):
|
|
154
|
+
def __init__(self, patterns: PatternLike | list[PatternLike], /) -> None:
|
|
155
|
+
self.patterns = Markup(patterns).patterns
|
|
156
|
+
|
|
157
|
+
def check(self, event: CallbackQuery, ctx: Context) -> bool:
|
|
158
|
+
return check_string(self.patterns, event.data.unwrap(), ctx)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
__all__ = (
|
|
162
|
+
"CallbackDataEq",
|
|
163
|
+
"CallbackDataJsonEq",
|
|
164
|
+
"CallbackDataJsonModel",
|
|
165
|
+
"CallbackDataMap",
|
|
166
|
+
"CallbackDataMarkup",
|
|
167
|
+
"CallbackQueryDataRule",
|
|
168
|
+
"CallbackQueryRule",
|
|
169
|
+
"HasData",
|
|
170
|
+
)
|