telegrinder 0.3.4__py3-none-any.whl → 0.4.0__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 +148 -149
- telegrinder/api/__init__.py +9 -8
- telegrinder/api/api.py +101 -93
- telegrinder/api/error.py +20 -16
- telegrinder/api/response.py +20 -20
- telegrinder/api/token.py +36 -36
- telegrinder/bot/__init__.py +72 -66
- telegrinder/bot/bot.py +83 -76
- telegrinder/bot/cute_types/__init__.py +19 -17
- telegrinder/bot/cute_types/base.py +184 -258
- telegrinder/bot/cute_types/callback_query.py +400 -385
- telegrinder/bot/cute_types/chat_join_request.py +62 -61
- telegrinder/bot/cute_types/chat_member_updated.py +157 -160
- telegrinder/bot/cute_types/inline_query.py +44 -43
- telegrinder/bot/cute_types/message.py +2590 -2637
- telegrinder/bot/cute_types/pre_checkout_query.py +42 -0
- telegrinder/bot/cute_types/update.py +112 -104
- telegrinder/bot/cute_types/utils.py +62 -95
- telegrinder/bot/dispatch/__init__.py +59 -55
- telegrinder/bot/dispatch/abc.py +76 -77
- telegrinder/bot/dispatch/context.py +96 -98
- telegrinder/bot/dispatch/dispatch.py +254 -202
- telegrinder/bot/dispatch/handler/__init__.py +13 -13
- telegrinder/bot/dispatch/handler/abc.py +23 -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 +129 -135
- telegrinder/bot/dispatch/handler/media_group_reply.py +44 -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 +97 -22
- telegrinder/bot/dispatch/middleware/global_middleware.py +70 -0
- telegrinder/bot/dispatch/process.py +151 -157
- telegrinder/bot/dispatch/return_manager/__init__.py +15 -13
- telegrinder/bot/dispatch/return_manager/abc.py +104 -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/return_manager/pre_checkout_query.py +20 -0
- telegrinder/bot/dispatch/view/__init__.py +15 -13
- telegrinder/bot/dispatch/view/abc.py +45 -41
- telegrinder/bot/dispatch/view/base.py +231 -200
- telegrinder/bot/dispatch/view/box.py +140 -129
- telegrinder/bot/dispatch/view/callback_query.py +16 -17
- telegrinder/bot/dispatch/view/chat_join_request.py +11 -16
- telegrinder/bot/dispatch/view/chat_member.py +37 -39
- telegrinder/bot/dispatch/view/inline_query.py +16 -17
- telegrinder/bot/dispatch/view/message.py +43 -44
- telegrinder/bot/dispatch/view/pre_checkout_query.py +16 -0
- telegrinder/bot/dispatch/view/raw.py +116 -114
- telegrinder/bot/dispatch/waiter_machine/__init__.py +17 -17
- telegrinder/bot/dispatch/waiter_machine/actions.py +14 -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 +59 -57
- telegrinder/bot/dispatch/waiter_machine/hasher/message.py +51 -51
- telegrinder/bot/dispatch/waiter_machine/hasher/state.py +20 -19
- telegrinder/bot/dispatch/waiter_machine/machine.py +251 -172
- telegrinder/bot/dispatch/waiter_machine/middleware.py +94 -89
- telegrinder/bot/dispatch/waiter_machine/short_state.py +57 -68
- telegrinder/bot/polling/__init__.py +4 -4
- telegrinder/bot/polling/abc.py +25 -25
- telegrinder/bot/polling/polling.py +139 -131
- telegrinder/bot/rules/__init__.py +85 -62
- telegrinder/bot/rules/abc.py +213 -206
- telegrinder/bot/rules/callback_data.py +122 -163
- telegrinder/bot/rules/chat_join.py +45 -43
- telegrinder/bot/rules/command.py +126 -126
- telegrinder/bot/rules/enum_text.py +33 -36
- telegrinder/bot/rules/func.py +28 -26
- telegrinder/bot/rules/fuzzy.py +24 -24
- telegrinder/bot/rules/id.py +24 -0
- telegrinder/bot/rules/inline.py +58 -56
- telegrinder/bot/rules/integer.py +21 -20
- telegrinder/bot/rules/is_from.py +127 -127
- telegrinder/bot/rules/logic.py +18 -0
- telegrinder/bot/rules/markup.py +42 -43
- telegrinder/bot/rules/mention.py +14 -14
- telegrinder/bot/rules/message.py +15 -17
- telegrinder/bot/rules/message_entities.py +33 -35
- telegrinder/bot/rules/node.py +33 -27
- telegrinder/bot/rules/payload.py +81 -0
- telegrinder/bot/rules/payment_invoice.py +29 -0
- telegrinder/bot/rules/regex.py +36 -37
- telegrinder/bot/rules/rule_enum.py +72 -72
- telegrinder/bot/rules/start.py +42 -42
- telegrinder/bot/rules/state.py +35 -37
- telegrinder/bot/rules/text.py +38 -33
- telegrinder/bot/rules/update.py +15 -15
- telegrinder/bot/scenario/__init__.py +5 -5
- telegrinder/bot/scenario/abc.py +17 -19
- telegrinder/bot/scenario/checkbox.py +174 -176
- telegrinder/bot/scenario/choice.py +48 -51
- telegrinder/client/__init__.py +12 -4
- telegrinder/client/abc.py +100 -75
- telegrinder/client/aiohttp.py +134 -130
- telegrinder/client/form_data.py +31 -0
- telegrinder/client/sonic.py +212 -0
- telegrinder/model.py +208 -315
- telegrinder/modules.py +239 -237
- telegrinder/msgspec_json.py +14 -14
- telegrinder/msgspec_utils.py +478 -410
- telegrinder/node/__init__.py +86 -25
- telegrinder/node/attachment.py +163 -87
- telegrinder/node/base.py +288 -160
- telegrinder/node/callback_query.py +54 -53
- telegrinder/node/command.py +34 -33
- telegrinder/node/composer.py +163 -198
- telegrinder/node/container.py +33 -27
- telegrinder/node/either.py +82 -0
- telegrinder/node/event.py +54 -65
- telegrinder/node/file.py +51 -0
- telegrinder/node/me.py +15 -16
- telegrinder/node/payload.py +78 -0
- telegrinder/node/polymorphic.py +67 -48
- telegrinder/node/rule.py +72 -76
- telegrinder/node/scope.py +36 -38
- telegrinder/node/source.py +87 -71
- telegrinder/node/text.py +53 -41
- telegrinder/node/tools/__init__.py +3 -3
- telegrinder/node/tools/generator.py +36 -40
- telegrinder/py.typed +0 -0
- telegrinder/rules.py +1 -62
- telegrinder/tools/__init__.py +152 -93
- telegrinder/tools/adapter/__init__.py +19 -0
- telegrinder/tools/adapter/abc.py +49 -0
- telegrinder/tools/adapter/dataclass.py +56 -0
- telegrinder/{bot/rules → tools}/adapter/errors.py +5 -5
- telegrinder/{bot/rules → tools}/adapter/event.py +63 -65
- telegrinder/{bot/rules → tools}/adapter/node.py +46 -48
- telegrinder/{bot/rules → tools}/adapter/raw_event.py +27 -27
- telegrinder/{bot/rules → tools}/adapter/raw_update.py +30 -30
- telegrinder/tools/buttons.py +106 -80
- telegrinder/tools/callback_data_serilization/__init__.py +5 -0
- telegrinder/tools/callback_data_serilization/abc.py +51 -0
- telegrinder/tools/callback_data_serilization/json_ser.py +60 -0
- telegrinder/tools/callback_data_serilization/msgpack_ser.py +172 -0
- telegrinder/tools/error_handler/__init__.py +7 -7
- telegrinder/tools/error_handler/abc.py +30 -33
- telegrinder/tools/error_handler/error.py +9 -9
- telegrinder/tools/error_handler/error_handler.py +179 -193
- telegrinder/tools/formatting/__init__.py +83 -63
- telegrinder/tools/formatting/deep_links.py +541 -0
- telegrinder/tools/formatting/{html.py → html_formatter.py} +266 -294
- telegrinder/tools/formatting/spec_html_formats.py +71 -117
- telegrinder/tools/functional.py +8 -12
- telegrinder/tools/global_context/__init__.py +7 -7
- telegrinder/tools/global_context/abc.py +63 -63
- telegrinder/tools/global_context/global_context.py +387 -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 +22 -25
- telegrinder/tools/i18n/simple.py +43 -43
- telegrinder/tools/input_file_directory.py +30 -0
- telegrinder/tools/keyboard.py +128 -128
- telegrinder/tools/lifespan.py +105 -0
- telegrinder/tools/limited_dict.py +32 -37
- telegrinder/tools/loop_wrapper/__init__.py +4 -4
- telegrinder/tools/loop_wrapper/abc.py +20 -15
- telegrinder/tools/loop_wrapper/loop_wrapper.py +169 -224
- telegrinder/tools/magic.py +307 -157
- telegrinder/tools/parse_mode.py +6 -6
- telegrinder/tools/state_storage/__init__.py +4 -4
- telegrinder/tools/state_storage/abc.py +31 -35
- telegrinder/tools/state_storage/memory.py +25 -25
- telegrinder/tools/strings.py +13 -0
- telegrinder/types/__init__.py +268 -260
- telegrinder/types/enums.py +711 -701
- telegrinder/types/input_file.py +51 -0
- telegrinder/types/methods.py +5055 -4633
- telegrinder/types/objects.py +7058 -6950
- telegrinder/verification_utils.py +30 -32
- {telegrinder-0.3.4.dist-info → telegrinder-0.4.0.dist-info}/LICENSE +22 -22
- telegrinder-0.4.0.dist-info/METADATA +144 -0
- telegrinder-0.4.0.dist-info/RECORD +182 -0
- {telegrinder-0.3.4.dist-info → telegrinder-0.4.0.dist-info}/WHEEL +1 -1
- telegrinder/bot/rules/adapter/__init__.py +0 -17
- telegrinder/bot/rules/adapter/abc.py +0 -31
- telegrinder/node/message.py +0 -14
- telegrinder/node/update.py +0 -15
- telegrinder/tools/formatting/links.py +0 -38
- telegrinder/tools/kb_set/__init__.py +0 -4
- telegrinder/tools/kb_set/base.py +0 -15
- telegrinder/tools/kb_set/yaml.py +0 -63
- telegrinder-0.3.4.dist-info/METADATA +0 -110
- telegrinder-0.3.4.dist-info/RECORD +0 -165
|
@@ -1,65 +1,63 @@
|
|
|
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.cute_types.update import UpdateCute
|
|
8
|
-
from telegrinder.bot.dispatch.context import Context
|
|
9
|
-
from telegrinder.
|
|
10
|
-
from telegrinder.
|
|
11
|
-
from telegrinder.
|
|
12
|
-
from telegrinder.types.enums import UpdateType
|
|
13
|
-
from telegrinder.types.objects import Model, Update
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
f"Update
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
self.
|
|
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
|
-
return
|
|
59
|
-
case
|
|
60
|
-
return
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
__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.cute_types.update import UpdateCute
|
|
8
|
+
from telegrinder.bot.dispatch.context import Context
|
|
9
|
+
from telegrinder.tools.adapter.abc import ABCAdapter
|
|
10
|
+
from telegrinder.tools.adapter.errors import AdapterError
|
|
11
|
+
from telegrinder.tools.adapter.raw_update import RawUpdateAdapter
|
|
12
|
+
from telegrinder.types.enums import UpdateType
|
|
13
|
+
from telegrinder.types.objects import Model, Update
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class EventAdapter[ToEvent: BaseCute](ABCAdapter[Update, ToEvent]):
|
|
17
|
+
ADAPTED_VALUE_KEY: str = "_adapted_cute_event"
|
|
18
|
+
|
|
19
|
+
def __init__(self, event: UpdateType | type[Model], cute_model: type[ToEvent]) -> None:
|
|
20
|
+
self.event = event
|
|
21
|
+
self.cute_model = cute_model
|
|
22
|
+
|
|
23
|
+
def __repr__(self) -> str:
|
|
24
|
+
raw_update_type = (
|
|
25
|
+
f"Update -> {self.event.__name__}"
|
|
26
|
+
if isinstance(self.event, type)
|
|
27
|
+
else f"Update.{self.event.value}" # type: ignore
|
|
28
|
+
)
|
|
29
|
+
return "<{}: adapt {} -> {}>".format(
|
|
30
|
+
self.__class__.__name__,
|
|
31
|
+
raw_update_type,
|
|
32
|
+
self.cute_model.__name__,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
def get_event(self, update: UpdateCute) -> Model | None:
|
|
36
|
+
if isinstance(self.event, UpdateType) and self.event == update.update_type:
|
|
37
|
+
return update.incoming_update
|
|
38
|
+
|
|
39
|
+
if not isinstance(self.event, UpdateType) and (event := update.get_event(self.event)):
|
|
40
|
+
return event.unwrap()
|
|
41
|
+
|
|
42
|
+
return None
|
|
43
|
+
|
|
44
|
+
def adapt(self, api: API, update: Update, context: Context) -> Result[ToEvent, AdapterError]:
|
|
45
|
+
match RawUpdateAdapter().adapt(api, update, context):
|
|
46
|
+
case Ok(update_cute) if event := self.get_event(update_cute):
|
|
47
|
+
if self.ADAPTED_VALUE_KEY in context:
|
|
48
|
+
return Ok(context[self.ADAPTED_VALUE_KEY])
|
|
49
|
+
|
|
50
|
+
adapted = (
|
|
51
|
+
typing.cast(ToEvent, event)
|
|
52
|
+
if isinstance(event, BaseCute)
|
|
53
|
+
else self.cute_model.from_update(event, bound_api=api)
|
|
54
|
+
)
|
|
55
|
+
context[self.ADAPTED_VALUE_KEY] = adapted
|
|
56
|
+
return Ok(adapted)
|
|
57
|
+
case Error(_) as err:
|
|
58
|
+
return err
|
|
59
|
+
case _:
|
|
60
|
+
return Error(AdapterError(f"Update is not an {self.event!r}."))
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
__all__ = ("EventAdapter",)
|
|
@@ -1,48 +1,46 @@
|
|
|
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.
|
|
7
|
-
from telegrinder.
|
|
8
|
-
from telegrinder.
|
|
9
|
-
from telegrinder.
|
|
10
|
-
from telegrinder.types.objects import Update
|
|
11
|
-
|
|
12
|
-
if typing.TYPE_CHECKING:
|
|
13
|
-
from telegrinder.node.base import
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
return
|
|
44
|
-
|
|
45
|
-
|
|
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.msgspec_utils import repr_type
|
|
7
|
+
from telegrinder.node.composer import NodeSession, compose_nodes
|
|
8
|
+
from telegrinder.tools.adapter.abc import ABCAdapter, Event
|
|
9
|
+
from telegrinder.tools.adapter.errors import AdapterError
|
|
10
|
+
from telegrinder.types.objects import Update
|
|
11
|
+
|
|
12
|
+
if typing.TYPE_CHECKING:
|
|
13
|
+
from telegrinder.node.base import IsNode
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class NodeAdapter(ABCAdapter[Update, Event[tuple["IsNode", ...]]]):
|
|
17
|
+
def __init__(self, *nodes: "IsNode") -> None:
|
|
18
|
+
self.nodes = nodes
|
|
19
|
+
|
|
20
|
+
def __repr__(self) -> str:
|
|
21
|
+
return "<{}: adapt Update -> ({})>".format(
|
|
22
|
+
self.__class__.__name__,
|
|
23
|
+
", ".join(repr_type(node) for node in self.nodes),
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
async def adapt(
|
|
27
|
+
self,
|
|
28
|
+
api: API,
|
|
29
|
+
update: Update,
|
|
30
|
+
context: Context,
|
|
31
|
+
) -> Result[Event[tuple[NodeSession, ...]], AdapterError]:
|
|
32
|
+
result = await compose_nodes(
|
|
33
|
+
nodes={f"node_{i}": typing.cast("IsNode", node) for i, node in enumerate(self.nodes)},
|
|
34
|
+
ctx=context,
|
|
35
|
+
data={Update: update, API: api},
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
match result:
|
|
39
|
+
case Ok(collection):
|
|
40
|
+
sessions: list[NodeSession] = list(collection.sessions.values())
|
|
41
|
+
return Ok(Event(tuple(sessions)))
|
|
42
|
+
case Error(err):
|
|
43
|
+
return Error(AdapterError(f"Couldn't compose nodes, error: {err}."))
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
__all__ = ("NodeAdapter",)
|
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
from fntypes.result import Error, Ok, Result
|
|
2
|
-
|
|
3
|
-
from telegrinder.api.api import API
|
|
4
|
-
from telegrinder.bot.dispatch.context import Context
|
|
5
|
-
from telegrinder.
|
|
6
|
-
from telegrinder.
|
|
7
|
-
from telegrinder.
|
|
8
|
-
from telegrinder.types.objects import Update
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class RawEventAdapter(ABCAdapter[Update, Model]):
|
|
12
|
-
def __init__(self, event_model: type[Model], /) -> None:
|
|
13
|
-
self.event_model = event_model
|
|
14
|
-
|
|
15
|
-
def __repr__(self) -> str:
|
|
16
|
-
return "<{}: adapt Update -> {}>".format(
|
|
17
|
-
self.__class__.__name__,
|
|
18
|
-
self.event_model.__name__,
|
|
19
|
-
)
|
|
20
|
-
|
|
21
|
-
def adapt(self, api: API, update: Update, context: Context) -> Result[Model, AdapterError]:
|
|
22
|
-
if isinstance(update.incoming_update, self.event_model):
|
|
23
|
-
return Ok(update.incoming_update)
|
|
24
|
-
return Error(AdapterError(f"Update is not an {self.event_model.__name__!r}."))
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
__all__ = ("RawEventAdapter",)
|
|
1
|
+
from fntypes.result import Error, Ok, Result
|
|
2
|
+
|
|
3
|
+
from telegrinder.api.api import API
|
|
4
|
+
from telegrinder.bot.dispatch.context import Context
|
|
5
|
+
from telegrinder.model import Model
|
|
6
|
+
from telegrinder.tools.adapter.abc import ABCAdapter
|
|
7
|
+
from telegrinder.tools.adapter.errors import AdapterError
|
|
8
|
+
from telegrinder.types.objects import Update
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class RawEventAdapter(ABCAdapter[Update, Model]):
|
|
12
|
+
def __init__(self, event_model: type[Model], /) -> None:
|
|
13
|
+
self.event_model = event_model
|
|
14
|
+
|
|
15
|
+
def __repr__(self) -> str:
|
|
16
|
+
return "<{}: adapt Update -> {}>".format(
|
|
17
|
+
self.__class__.__name__,
|
|
18
|
+
self.event_model.__name__,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
def adapt(self, api: API, update: Update, context: Context) -> Result[Model, AdapterError]:
|
|
22
|
+
if isinstance(update.incoming_update, self.event_model):
|
|
23
|
+
return Ok(update.incoming_update)
|
|
24
|
+
return Error(AdapterError(f"Update is not an {self.event_model.__name__!r}."))
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
__all__ = ("RawEventAdapter",)
|
|
@@ -1,30 +1,30 @@
|
|
|
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.
|
|
7
|
-
from telegrinder.
|
|
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
|
-
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.tools.adapter.abc import ABCAdapter
|
|
7
|
+
from telegrinder.tools.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
|
+
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",)
|
telegrinder/tools/buttons.py
CHANGED
|
@@ -1,83 +1,109 @@
|
|
|
1
|
-
import dataclasses
|
|
2
|
-
import typing
|
|
3
|
-
|
|
4
|
-
import msgspec
|
|
5
|
-
|
|
6
|
-
from telegrinder.msgspec_utils import
|
|
7
|
-
from telegrinder.types.objects import (
|
|
8
|
-
CallbackGame,
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
1
|
+
import dataclasses
|
|
2
|
+
import typing
|
|
3
|
+
|
|
4
|
+
import msgspec
|
|
5
|
+
|
|
6
|
+
from telegrinder.msgspec_utils import encoder
|
|
7
|
+
from telegrinder.types.objects import (
|
|
8
|
+
CallbackGame,
|
|
9
|
+
CopyTextButton,
|
|
10
|
+
KeyboardButtonPollType,
|
|
11
|
+
KeyboardButtonRequestChat,
|
|
12
|
+
KeyboardButtonRequestUsers,
|
|
13
|
+
LoginUrl,
|
|
14
|
+
SwitchInlineQueryChosenChat,
|
|
15
|
+
WebAppInfo,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
from .callback_data_serilization import ABCDataSerializer, JSONSerializer
|
|
19
|
+
|
|
20
|
+
if typing.TYPE_CHECKING:
|
|
21
|
+
from _typeshed import DataclassInstance
|
|
22
|
+
|
|
23
|
+
type CallbackData = str | bytes | dict[str, typing.Any] | DataclassInstance | msgspec.Struct
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclasses.dataclass
|
|
27
|
+
class BaseButton:
|
|
28
|
+
def get_data(self) -> dict[str, typing.Any]:
|
|
29
|
+
return {k: v for k, v in dataclasses.asdict(self).items() if v is not None}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class RowButtons[KeyboardButton: BaseButton]:
|
|
33
|
+
buttons: list[KeyboardButton]
|
|
34
|
+
auto_row: bool
|
|
35
|
+
|
|
36
|
+
def __init__(self, *buttons: KeyboardButton, auto_row: bool = True) -> None:
|
|
37
|
+
self.buttons = list(buttons)
|
|
38
|
+
self.auto_row = auto_row
|
|
39
|
+
|
|
40
|
+
def get_data(self) -> list[dict[str, typing.Any]]:
|
|
41
|
+
return [b.get_data() for b in self.buttons]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclasses.dataclass
|
|
45
|
+
class Button(BaseButton):
|
|
46
|
+
text: str
|
|
47
|
+
request_contact: bool = dataclasses.field(default=False, kw_only=True)
|
|
48
|
+
request_location: bool = dataclasses.field(default=False, kw_only=True)
|
|
49
|
+
request_chat: KeyboardButtonRequestChat | None = dataclasses.field(
|
|
50
|
+
default=None,
|
|
51
|
+
kw_only=True,
|
|
52
|
+
)
|
|
53
|
+
request_user: KeyboardButtonRequestUsers | None = dataclasses.field(default=None, kw_only=True)
|
|
54
|
+
request_poll: KeyboardButtonPollType | None = dataclasses.field(
|
|
55
|
+
default=None,
|
|
56
|
+
kw_only=True,
|
|
57
|
+
)
|
|
58
|
+
web_app: WebAppInfo | None = dataclasses.field(default=None, kw_only=True)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@dataclasses.dataclass
|
|
62
|
+
class InlineButton(BaseButton):
|
|
63
|
+
text: str
|
|
64
|
+
url: str | None = dataclasses.field(default=None, kw_only=True)
|
|
65
|
+
login_url: LoginUrl | None = dataclasses.field(default=None, kw_only=True)
|
|
66
|
+
pay: bool | None = dataclasses.field(default=None, kw_only=True)
|
|
67
|
+
callback_data: CallbackData | None = dataclasses.field(default=None, kw_only=True)
|
|
68
|
+
callback_data_serializer: dataclasses.InitVar[ABCDataSerializer[typing.Any] | None] = dataclasses.field(
|
|
69
|
+
default=None,
|
|
70
|
+
kw_only=True,
|
|
71
|
+
)
|
|
72
|
+
callback_game: CallbackGame | None = dataclasses.field(default=None, kw_only=True)
|
|
73
|
+
copy_text: str | CopyTextButton | None = dataclasses.field(default=None, kw_only=True)
|
|
74
|
+
switch_inline_query: str | None = dataclasses.field(default=None, kw_only=True)
|
|
75
|
+
switch_inline_query_current_chat: str | None = dataclasses.field(default=None, kw_only=True)
|
|
76
|
+
switch_inline_query_chosen_chat: SwitchInlineQueryChosenChat | None = dataclasses.field(
|
|
77
|
+
default=None,
|
|
78
|
+
kw_only=True,
|
|
79
|
+
)
|
|
80
|
+
web_app: str | WebAppInfo | None = dataclasses.field(default=None, kw_only=True)
|
|
81
|
+
|
|
82
|
+
def __post_init__(self, callback_data_serializer: ABCDataSerializer[typing.Any] | None) -> None:
|
|
83
|
+
if (
|
|
84
|
+
callback_data_serializer is None
|
|
85
|
+
and isinstance(self.callback_data, msgspec.Struct | dict)
|
|
86
|
+
or dataclasses.is_dataclass(self.callback_data)
|
|
87
|
+
):
|
|
88
|
+
callback_data_serializer = callback_data_serializer or JSONSerializer(
|
|
89
|
+
self.callback_data.__class__,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
if callback_data_serializer is not None:
|
|
93
|
+
self.callback_data = callback_data_serializer.serialize(self.callback_data)
|
|
94
|
+
elif self.callback_data is not None and not isinstance(self.callback_data, str | bytes):
|
|
95
|
+
self.callback_data = encoder.encode(self.callback_data)
|
|
96
|
+
|
|
97
|
+
if isinstance(self.copy_text, str):
|
|
98
|
+
self.copy_text = CopyTextButton(text=self.copy_text)
|
|
99
|
+
|
|
100
|
+
if isinstance(self.web_app, str):
|
|
101
|
+
self.web_app = WebAppInfo(url=self.web_app)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
__all__ = (
|
|
78
105
|
"BaseButton",
|
|
79
106
|
"Button",
|
|
80
|
-
"DataclassInstance",
|
|
81
107
|
"InlineButton",
|
|
82
|
-
"RowButtons",
|
|
83
|
-
)
|
|
108
|
+
"RowButtons",
|
|
109
|
+
)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import abc
|
|
4
|
+
import typing
|
|
5
|
+
from functools import cached_property
|
|
6
|
+
|
|
7
|
+
import msgspec
|
|
8
|
+
from fntypes.result import Result
|
|
9
|
+
|
|
10
|
+
if typing.TYPE_CHECKING:
|
|
11
|
+
from dataclasses import Field
|
|
12
|
+
|
|
13
|
+
from _typeshed import DataclassInstance
|
|
14
|
+
|
|
15
|
+
type ModelType = DataclassWithIdentKey | ModelWithIdentKey | msgspec.Struct | DataclassInstance
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@typing.runtime_checkable
|
|
19
|
+
class DataclassWithIdentKey(typing.Protocol):
|
|
20
|
+
__key__: str
|
|
21
|
+
__dataclass_fields__: typing.ClassVar[dict[str, Field[typing.Any]]]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@typing.runtime_checkable
|
|
25
|
+
class ModelWithIdentKey(typing.Protocol):
|
|
26
|
+
__key__: str
|
|
27
|
+
__struct_fields__: typing.ClassVar[tuple[str, ...]]
|
|
28
|
+
__struct_config__: typing.ClassVar[msgspec.structs.StructConfig]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class ABCDataSerializer[Data](abc.ABC):
|
|
32
|
+
ident_key: str | None = None
|
|
33
|
+
|
|
34
|
+
@abc.abstractmethod
|
|
35
|
+
def __init__(self, data_type: type[Data], /) -> None:
|
|
36
|
+
pass
|
|
37
|
+
|
|
38
|
+
@cached_property
|
|
39
|
+
def key(self) -> str:
|
|
40
|
+
return self.ident_key + "_" if self.ident_key else ""
|
|
41
|
+
|
|
42
|
+
@abc.abstractmethod
|
|
43
|
+
def serialize(self, data: Data) -> str:
|
|
44
|
+
pass
|
|
45
|
+
|
|
46
|
+
@abc.abstractmethod
|
|
47
|
+
def deserialize(self, serialized_data: str) -> Result[Data, str]:
|
|
48
|
+
pass
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
__all__ = ("ABCDataSerializer",)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
|
|
3
|
+
import msgspec
|
|
4
|
+
from fntypes.result import Error, Ok, Result
|
|
5
|
+
|
|
6
|
+
from telegrinder.modules import json
|
|
7
|
+
from telegrinder.msgspec_utils import decoder
|
|
8
|
+
|
|
9
|
+
from .abc import ABCDataSerializer, ModelType
|
|
10
|
+
|
|
11
|
+
type Json = dict[str, typing.Any] | ModelType
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class JSONSerializer[JsonT: Json](ABCDataSerializer[JsonT]):
|
|
15
|
+
@typing.overload
|
|
16
|
+
def __init__(self, model_t: type[JsonT]) -> None: ...
|
|
17
|
+
|
|
18
|
+
@typing.overload
|
|
19
|
+
def __init__(self, model_t: type[JsonT], *, ident_key: str | None = ...) -> None: ...
|
|
20
|
+
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
model_t: type[JsonT] = dict[str, typing.Any],
|
|
24
|
+
*,
|
|
25
|
+
ident_key: str | None = None,
|
|
26
|
+
) -> None:
|
|
27
|
+
self.model_t = model_t
|
|
28
|
+
self.ident_key: str | None = ident_key or getattr(model_t, "__key__", None)
|
|
29
|
+
|
|
30
|
+
@classmethod
|
|
31
|
+
def serialize_from_json(cls, data: JsonT, *, ident_key: str | None = None) -> str:
|
|
32
|
+
return cls(data.__class__, ident_key=ident_key).serialize(data)
|
|
33
|
+
|
|
34
|
+
@classmethod
|
|
35
|
+
def deserialize_to_json(cls, serialized_data: str, model_t: type[JsonT]) -> Result[JsonT, str]:
|
|
36
|
+
return cls(model_t).deserialize(serialized_data)
|
|
37
|
+
|
|
38
|
+
def serialize(self, data: JsonT) -> str:
|
|
39
|
+
return self.key + json.dumps(data)
|
|
40
|
+
|
|
41
|
+
def deserialize(self, serialized_data: str) -> Result[JsonT, str]:
|
|
42
|
+
if self.ident_key and not serialized_data.startswith(self.key):
|
|
43
|
+
return Error("Data is not corresponding to key.")
|
|
44
|
+
|
|
45
|
+
data = serialized_data.removeprefix(self.key)
|
|
46
|
+
try:
|
|
47
|
+
data_obj = json.loads(data)
|
|
48
|
+
except (msgspec.ValidationError, msgspec.DecodeError):
|
|
49
|
+
return Error("Cannot decode json.")
|
|
50
|
+
|
|
51
|
+
if not issubclass(self.model_t, dict):
|
|
52
|
+
try:
|
|
53
|
+
return Ok(decoder.convert(data_obj, type=self.model_t))
|
|
54
|
+
except (msgspec.ValidationError, msgspec.DecodeError):
|
|
55
|
+
return Error("Incorrect data.")
|
|
56
|
+
|
|
57
|
+
return Ok(data_obj)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
__all__ = ("JSONSerializer",)
|