telegrinder 0.3.4.post1__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 +30 -31
- telegrinder/api/__init__.py +2 -1
- telegrinder/api/api.py +28 -20
- telegrinder/api/error.py +8 -4
- telegrinder/api/response.py +2 -2
- telegrinder/api/token.py +2 -2
- telegrinder/bot/__init__.py +6 -0
- telegrinder/bot/bot.py +38 -31
- telegrinder/bot/cute_types/__init__.py +2 -0
- telegrinder/bot/cute_types/base.py +54 -128
- telegrinder/bot/cute_types/callback_query.py +76 -61
- telegrinder/bot/cute_types/chat_join_request.py +4 -3
- telegrinder/bot/cute_types/chat_member_updated.py +28 -31
- telegrinder/bot/cute_types/inline_query.py +5 -4
- telegrinder/bot/cute_types/message.py +555 -602
- telegrinder/bot/cute_types/pre_checkout_query.py +42 -0
- telegrinder/bot/cute_types/update.py +20 -12
- telegrinder/bot/cute_types/utils.py +3 -36
- telegrinder/bot/dispatch/__init__.py +4 -0
- telegrinder/bot/dispatch/abc.py +8 -9
- telegrinder/bot/dispatch/context.py +5 -7
- telegrinder/bot/dispatch/dispatch.py +85 -33
- telegrinder/bot/dispatch/handler/abc.py +5 -6
- telegrinder/bot/dispatch/handler/audio_reply.py +2 -2
- telegrinder/bot/dispatch/handler/base.py +3 -3
- telegrinder/bot/dispatch/handler/document_reply.py +2 -2
- telegrinder/bot/dispatch/handler/func.py +36 -42
- telegrinder/bot/dispatch/handler/media_group_reply.py +5 -4
- telegrinder/bot/dispatch/handler/message_reply.py +2 -2
- telegrinder/bot/dispatch/handler/photo_reply.py +2 -2
- telegrinder/bot/dispatch/handler/sticker_reply.py +2 -2
- telegrinder/bot/dispatch/handler/video_reply.py +2 -2
- telegrinder/bot/dispatch/middleware/abc.py +83 -8
- telegrinder/bot/dispatch/middleware/global_middleware.py +70 -0
- telegrinder/bot/dispatch/process.py +44 -50
- telegrinder/bot/dispatch/return_manager/__init__.py +2 -0
- telegrinder/bot/dispatch/return_manager/abc.py +6 -10
- telegrinder/bot/dispatch/return_manager/pre_checkout_query.py +20 -0
- telegrinder/bot/dispatch/view/__init__.py +2 -0
- telegrinder/bot/dispatch/view/abc.py +10 -6
- telegrinder/bot/dispatch/view/base.py +81 -50
- telegrinder/bot/dispatch/view/box.py +20 -9
- telegrinder/bot/dispatch/view/callback_query.py +3 -4
- telegrinder/bot/dispatch/view/chat_join_request.py +2 -7
- telegrinder/bot/dispatch/view/chat_member.py +3 -5
- telegrinder/bot/dispatch/view/inline_query.py +3 -4
- telegrinder/bot/dispatch/view/message.py +3 -4
- telegrinder/bot/dispatch/view/pre_checkout_query.py +16 -0
- telegrinder/bot/dispatch/view/raw.py +42 -40
- telegrinder/bot/dispatch/waiter_machine/actions.py +5 -4
- telegrinder/bot/dispatch/waiter_machine/hasher/__init__.py +0 -0
- telegrinder/bot/dispatch/waiter_machine/hasher/callback.py +0 -0
- telegrinder/bot/dispatch/waiter_machine/hasher/hasher.py +9 -7
- telegrinder/bot/dispatch/waiter_machine/hasher/message.py +0 -0
- telegrinder/bot/dispatch/waiter_machine/hasher/state.py +3 -2
- telegrinder/bot/dispatch/waiter_machine/machine.py +113 -34
- telegrinder/bot/dispatch/waiter_machine/middleware.py +15 -10
- telegrinder/bot/dispatch/waiter_machine/short_state.py +7 -18
- telegrinder/bot/polling/polling.py +62 -54
- telegrinder/bot/rules/__init__.py +24 -1
- telegrinder/bot/rules/abc.py +17 -10
- telegrinder/bot/rules/callback_data.py +20 -61
- telegrinder/bot/rules/chat_join.py +6 -4
- telegrinder/bot/rules/command.py +4 -4
- telegrinder/bot/rules/enum_text.py +1 -4
- telegrinder/bot/rules/func.py +5 -3
- telegrinder/bot/rules/fuzzy.py +1 -1
- telegrinder/bot/rules/id.py +24 -0
- telegrinder/bot/rules/inline.py +6 -4
- telegrinder/bot/rules/integer.py +2 -1
- telegrinder/bot/rules/logic.py +18 -0
- telegrinder/bot/rules/markup.py +5 -6
- telegrinder/bot/rules/message.py +2 -4
- telegrinder/bot/rules/message_entities.py +1 -3
- telegrinder/bot/rules/node.py +15 -9
- telegrinder/bot/rules/payload.py +81 -0
- telegrinder/bot/rules/payment_invoice.py +29 -0
- telegrinder/bot/rules/regex.py +5 -6
- telegrinder/bot/rules/state.py +1 -3
- telegrinder/bot/rules/text.py +10 -5
- telegrinder/bot/rules/update.py +0 -0
- telegrinder/bot/scenario/abc.py +2 -4
- telegrinder/bot/scenario/checkbox.py +12 -14
- telegrinder/bot/scenario/choice.py +6 -9
- telegrinder/client/__init__.py +9 -1
- telegrinder/client/abc.py +35 -10
- telegrinder/client/aiohttp.py +28 -24
- telegrinder/client/form_data.py +31 -0
- telegrinder/client/sonic.py +212 -0
- telegrinder/model.py +38 -145
- telegrinder/modules.py +3 -1
- telegrinder/msgspec_utils.py +136 -68
- telegrinder/node/__init__.py +74 -13
- telegrinder/node/attachment.py +92 -16
- telegrinder/node/base.py +196 -68
- telegrinder/node/callback_query.py +17 -16
- telegrinder/node/command.py +3 -2
- telegrinder/node/composer.py +40 -75
- telegrinder/node/container.py +13 -7
- telegrinder/node/either.py +82 -0
- telegrinder/node/event.py +20 -31
- telegrinder/node/file.py +51 -0
- telegrinder/node/me.py +4 -5
- telegrinder/node/payload.py +78 -0
- telegrinder/node/polymorphic.py +27 -8
- telegrinder/node/rule.py +2 -6
- telegrinder/node/scope.py +4 -6
- telegrinder/node/source.py +37 -21
- telegrinder/node/text.py +20 -8
- telegrinder/node/tools/generator.py +7 -11
- telegrinder/py.typed +0 -0
- telegrinder/rules.py +0 -61
- telegrinder/tools/__init__.py +97 -38
- 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/event.py +8 -10
- telegrinder/{bot/rules → tools}/adapter/node.py +8 -10
- telegrinder/{bot/rules → tools}/adapter/raw_event.py +2 -2
- telegrinder/{bot/rules → tools}/adapter/raw_update.py +2 -2
- telegrinder/tools/buttons.py +52 -26
- 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/abc.py +4 -7
- telegrinder/tools/error_handler/error.py +0 -0
- telegrinder/tools/error_handler/error_handler.py +34 -48
- telegrinder/tools/formatting/__init__.py +57 -37
- telegrinder/tools/formatting/deep_links.py +541 -0
- telegrinder/tools/formatting/{html.py → html_formatter.py} +51 -79
- telegrinder/tools/formatting/spec_html_formats.py +14 -60
- telegrinder/tools/functional.py +1 -5
- telegrinder/tools/global_context/global_context.py +26 -51
- telegrinder/tools/global_context/telegrinder_ctx.py +3 -3
- telegrinder/tools/i18n/abc.py +0 -0
- telegrinder/tools/i18n/middleware/abc.py +3 -6
- telegrinder/tools/input_file_directory.py +30 -0
- telegrinder/tools/keyboard.py +9 -9
- telegrinder/tools/lifespan.py +105 -0
- telegrinder/tools/limited_dict.py +5 -10
- telegrinder/tools/loop_wrapper/abc.py +7 -2
- telegrinder/tools/loop_wrapper/loop_wrapper.py +40 -95
- telegrinder/tools/magic.py +184 -34
- telegrinder/tools/state_storage/__init__.py +0 -0
- telegrinder/tools/state_storage/abc.py +5 -9
- telegrinder/tools/state_storage/memory.py +1 -1
- telegrinder/tools/strings.py +13 -0
- telegrinder/types/__init__.py +8 -0
- telegrinder/types/enums.py +31 -21
- telegrinder/types/input_file.py +51 -0
- telegrinder/types/methods.py +531 -109
- telegrinder/types/objects.py +934 -826
- telegrinder/verification_utils.py +0 -2
- {telegrinder-0.3.4.post1.dist-info → telegrinder-0.4.0.dist-info}/LICENSE +2 -2
- telegrinder-0.4.0.dist-info/METADATA +144 -0
- telegrinder-0.4.0.dist-info/RECORD +182 -0
- {telegrinder-0.3.4.post1.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.post1.dist-info/METADATA +0 -110
- telegrinder-0.3.4.post1.dist-info/RECORD +0 -165
- /telegrinder/{bot/rules → tools}/adapter/errors.py +0 -0
telegrinder/node/source.py
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
import dataclasses
|
|
2
2
|
import typing
|
|
3
3
|
|
|
4
|
-
from fntypes.option import Nothing, Option
|
|
4
|
+
from fntypes.option import Nothing, Option, Some
|
|
5
5
|
|
|
6
6
|
from telegrinder.api.api import API
|
|
7
|
-
from telegrinder.bot.cute_types import ChatJoinRequestCute
|
|
8
|
-
from telegrinder.node.base import ComposeError, DataNode,
|
|
9
|
-
from telegrinder.node.callback_query import CallbackQueryNode
|
|
10
|
-
from telegrinder.node.event import EventNode
|
|
11
|
-
from telegrinder.node.message import MessageNode
|
|
7
|
+
from telegrinder.bot.cute_types import CallbackQueryCute, ChatJoinRequestCute, MessageCute, PreCheckoutQueryCute
|
|
8
|
+
from telegrinder.node.base import ComposeError, DataNode, scalar_node
|
|
12
9
|
from telegrinder.node.polymorphic import Polymorphic, impl
|
|
13
10
|
from telegrinder.types.objects import Chat, Message, User
|
|
14
11
|
|
|
@@ -16,56 +13,75 @@ from telegrinder.types.objects import Chat, Message, User
|
|
|
16
13
|
@dataclasses.dataclass(kw_only=True, slots=True)
|
|
17
14
|
class Source(Polymorphic, DataNode):
|
|
18
15
|
api: API
|
|
19
|
-
chat: Chat
|
|
20
16
|
from_user: User
|
|
21
|
-
|
|
17
|
+
chat: Option[Chat] = dataclasses.field(default_factory=Nothing)
|
|
18
|
+
thread_id: Option[int] = dataclasses.field(default_factory=Nothing)
|
|
22
19
|
|
|
23
20
|
@impl
|
|
24
|
-
def compose_message(cls, message:
|
|
21
|
+
def compose_message(cls, message: MessageCute) -> typing.Self:
|
|
25
22
|
return cls(
|
|
26
23
|
api=message.ctx_api,
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
from_user=message.from_user,
|
|
25
|
+
chat=Some(message.chat),
|
|
29
26
|
thread_id=message.message_thread_id,
|
|
30
27
|
)
|
|
31
28
|
|
|
32
29
|
@impl
|
|
33
|
-
def compose_callback_query(cls, callback_query:
|
|
30
|
+
def compose_callback_query(cls, callback_query: CallbackQueryCute) -> typing.Self:
|
|
34
31
|
return cls(
|
|
35
32
|
api=callback_query.ctx_api,
|
|
36
|
-
chat=callback_query.chat.expect(ComposeError("CallbackQueryNode has no chat")),
|
|
37
33
|
from_user=callback_query.from_user,
|
|
34
|
+
chat=callback_query.chat,
|
|
38
35
|
thread_id=callback_query.message_thread_id,
|
|
39
36
|
)
|
|
40
37
|
|
|
41
38
|
@impl
|
|
42
|
-
def compose_chat_join_request(cls, chat_join_request:
|
|
39
|
+
def compose_chat_join_request(cls, chat_join_request: ChatJoinRequestCute) -> typing.Self:
|
|
43
40
|
return cls(
|
|
44
41
|
api=chat_join_request.ctx_api,
|
|
45
|
-
chat=chat_join_request.chat,
|
|
46
42
|
from_user=chat_join_request.from_user,
|
|
43
|
+
chat=Some(chat_join_request.chat),
|
|
47
44
|
thread_id=Nothing(),
|
|
48
45
|
)
|
|
49
46
|
|
|
50
|
-
|
|
47
|
+
@impl
|
|
48
|
+
def compose_pre_checkout_query(cls, pre_checkout_query: PreCheckoutQueryCute) -> typing.Self:
|
|
49
|
+
return cls(
|
|
50
|
+
api=pre_checkout_query.ctx_api,
|
|
51
|
+
from_user=pre_checkout_query.from_user,
|
|
52
|
+
chat=Nothing(),
|
|
53
|
+
thread_id=Nothing(),
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
async def send(self, text: str, **kwargs: typing.Any) -> Message:
|
|
51
57
|
result = await self.api.send_message(
|
|
52
|
-
chat_id=self.chat.id,
|
|
58
|
+
chat_id=self.chat.map_or(self.from_user.id, lambda chat: chat.id).unwrap(),
|
|
53
59
|
message_thread_id=self.thread_id.unwrap_or_none(),
|
|
54
60
|
text=text,
|
|
61
|
+
**kwargs,
|
|
55
62
|
)
|
|
56
63
|
return result.unwrap()
|
|
57
64
|
|
|
58
65
|
|
|
59
|
-
|
|
66
|
+
@scalar_node
|
|
67
|
+
class ChatSource:
|
|
60
68
|
@classmethod
|
|
61
69
|
def compose(cls, source: Source) -> Chat:
|
|
62
|
-
return source.chat
|
|
70
|
+
return source.chat.expect(ComposeError("Source has no chat."))
|
|
63
71
|
|
|
64
72
|
|
|
65
|
-
|
|
73
|
+
@scalar_node
|
|
74
|
+
class UserSource:
|
|
66
75
|
@classmethod
|
|
67
76
|
def compose(cls, source: Source) -> User:
|
|
68
77
|
return source.from_user
|
|
69
78
|
|
|
70
79
|
|
|
71
|
-
|
|
80
|
+
@scalar_node
|
|
81
|
+
class UserId:
|
|
82
|
+
@classmethod
|
|
83
|
+
def compose(cls, user: UserSource) -> int:
|
|
84
|
+
return user.id
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
__all__ = ("ChatSource", "Source", "UserId", "UserSource")
|
telegrinder/node/text.py
CHANGED
|
@@ -1,20 +1,32 @@
|
|
|
1
1
|
import typing
|
|
2
2
|
|
|
3
|
-
from telegrinder.
|
|
4
|
-
from telegrinder.node.
|
|
3
|
+
from telegrinder.bot.cute_types.message import MessageCute
|
|
4
|
+
from telegrinder.node.base import ComposeError, FactoryNode, scalar_node
|
|
5
|
+
from telegrinder.node.either import Either
|
|
5
6
|
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
@scalar_node
|
|
9
|
+
class Caption:
|
|
8
10
|
@classmethod
|
|
9
|
-
def compose(cls, message:
|
|
11
|
+
def compose(cls, message: MessageCute) -> str:
|
|
12
|
+
if not message.caption:
|
|
13
|
+
raise ComposeError("Message has no caption.")
|
|
14
|
+
return message.caption.unwrap()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@scalar_node
|
|
18
|
+
class Text:
|
|
19
|
+
@classmethod
|
|
20
|
+
def compose(cls, message: MessageCute) -> str:
|
|
10
21
|
if not message.text:
|
|
11
22
|
raise ComposeError("Message has no text.")
|
|
12
23
|
return message.text.unwrap()
|
|
13
24
|
|
|
14
25
|
|
|
15
|
-
|
|
26
|
+
@scalar_node
|
|
27
|
+
class TextInteger:
|
|
16
28
|
@classmethod
|
|
17
|
-
def compose(cls, text: Text) -> int:
|
|
29
|
+
def compose(cls, text: Either[Text, Caption]) -> int:
|
|
18
30
|
if not text.isdigit():
|
|
19
31
|
raise ComposeError("Text is not digit.")
|
|
20
32
|
return int(text)
|
|
@@ -35,7 +47,7 @@ else:
|
|
|
35
47
|
def compose(cls, text: Text) -> str:
|
|
36
48
|
if text in cls.texts:
|
|
37
49
|
return text
|
|
38
|
-
raise ComposeError("Text
|
|
50
|
+
raise ComposeError("Text mismatched literal.")
|
|
39
51
|
|
|
40
52
|
|
|
41
|
-
__all__ = ("Text", "TextInteger", "TextLiteral")
|
|
53
|
+
__all__ = ("Caption", "Text", "TextInteger", "TextLiteral")
|
|
@@ -1,40 +1,36 @@
|
|
|
1
1
|
import inspect
|
|
2
2
|
import typing
|
|
3
3
|
|
|
4
|
-
from telegrinder.node.base import ComposeError, Node
|
|
4
|
+
from telegrinder.node.base import ComposeError, IsNode, Node
|
|
5
5
|
from telegrinder.node.container import ContainerNode
|
|
6
6
|
|
|
7
|
-
T = typing.TypeVar("T")
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
def cast_false_to_none(value: T) -> T | None:
|
|
8
|
+
def cast_false_to_none[Value](value: Value) -> Value | None:
|
|
11
9
|
if value is False:
|
|
12
10
|
return None
|
|
13
11
|
return value
|
|
14
12
|
|
|
15
13
|
|
|
16
|
-
def error_on_none(value:
|
|
14
|
+
def error_on_none[Value](value: Value | None) -> Value:
|
|
17
15
|
if value is None:
|
|
18
16
|
raise ComposeError
|
|
19
17
|
return value
|
|
20
18
|
|
|
21
19
|
|
|
22
20
|
def generate_node(
|
|
23
|
-
subnodes: tuple[
|
|
21
|
+
subnodes: tuple[IsNode, ...],
|
|
24
22
|
func: typing.Callable[..., typing.Any],
|
|
25
23
|
casts: tuple[typing.Callable[[typing.Any], typing.Any], ...] = (cast_false_to_none, error_on_none),
|
|
26
24
|
) -> type[Node]:
|
|
27
|
-
async def compose(cls,
|
|
28
|
-
result = func(*
|
|
25
|
+
async def compose(cls, *args: typing.Any) -> typing.Any:
|
|
26
|
+
result = func(*args)
|
|
29
27
|
if inspect.isawaitable(result):
|
|
30
28
|
result = await result
|
|
31
29
|
for cast in casts:
|
|
32
30
|
result = cast(result)
|
|
33
31
|
return result
|
|
34
32
|
|
|
35
|
-
|
|
36
|
-
compose.__annotations__ = container.get_subnodes()
|
|
37
|
-
return type("_ContainerNode", (container,), {"compose": classmethod(compose)})
|
|
33
|
+
return ContainerNode.link_nodes(linked_nodes=list(subnodes), composer=compose)
|
|
38
34
|
|
|
39
35
|
|
|
40
36
|
__all__ = ("generate_node",)
|
telegrinder/py.typed
CHANGED
|
File without changes
|
telegrinder/rules.py
CHANGED
|
@@ -1,62 +1 @@
|
|
|
1
1
|
from .bot.rules import * # noqa: F403
|
|
2
|
-
|
|
3
|
-
__all__ = (
|
|
4
|
-
"ABCRule",
|
|
5
|
-
"AndRule",
|
|
6
|
-
"Argument",
|
|
7
|
-
"CallbackDataEq",
|
|
8
|
-
"CallbackDataJsonEq",
|
|
9
|
-
"CallbackDataJsonModel",
|
|
10
|
-
"CallbackDataMap",
|
|
11
|
-
"CallbackDataMarkup",
|
|
12
|
-
"CallbackQueryDataRule",
|
|
13
|
-
"CallbackQueryRule",
|
|
14
|
-
"ChatJoinRequestRule",
|
|
15
|
-
"Command",
|
|
16
|
-
"EnumTextRule",
|
|
17
|
-
"FuncRule",
|
|
18
|
-
"FuzzyText",
|
|
19
|
-
"HasData",
|
|
20
|
-
"HasEntities",
|
|
21
|
-
"HasInviteLink",
|
|
22
|
-
"HasLocation",
|
|
23
|
-
"HasMention",
|
|
24
|
-
"HasText",
|
|
25
|
-
"InlineQueryChatType",
|
|
26
|
-
"InlineQueryMarkup",
|
|
27
|
-
"InlineQueryRule",
|
|
28
|
-
"InlineQueryText",
|
|
29
|
-
"IntegerInRange",
|
|
30
|
-
"InviteLinkByCreator",
|
|
31
|
-
"InviteLinkName",
|
|
32
|
-
"IsBot",
|
|
33
|
-
"IsChat",
|
|
34
|
-
"IsChatId",
|
|
35
|
-
"IsDice",
|
|
36
|
-
"IsDiceEmoji",
|
|
37
|
-
"IsForum",
|
|
38
|
-
"IsForward",
|
|
39
|
-
"IsForwardType",
|
|
40
|
-
"IsGroup",
|
|
41
|
-
"IsInteger",
|
|
42
|
-
"IsLanguageCode",
|
|
43
|
-
"IsPremium",
|
|
44
|
-
"IsPrivate",
|
|
45
|
-
"IsReply",
|
|
46
|
-
"IsSuperGroup",
|
|
47
|
-
"IsUpdateType",
|
|
48
|
-
"IsUser",
|
|
49
|
-
"IsUserId",
|
|
50
|
-
"Markup",
|
|
51
|
-
"MessageEntities",
|
|
52
|
-
"MessageRule",
|
|
53
|
-
"NodeRule",
|
|
54
|
-
"NotRule",
|
|
55
|
-
"OrRule",
|
|
56
|
-
"Regex",
|
|
57
|
-
"RuleEnum",
|
|
58
|
-
"StartCommand",
|
|
59
|
-
"State",
|
|
60
|
-
"StateMeta",
|
|
61
|
-
"Text",
|
|
62
|
-
)
|
telegrinder/tools/__init__.py
CHANGED
|
@@ -1,43 +1,67 @@
|
|
|
1
|
+
from .adapter import (
|
|
2
|
+
ABCAdapter,
|
|
3
|
+
DataclassAdapter,
|
|
4
|
+
EventAdapter,
|
|
5
|
+
NodeAdapter,
|
|
6
|
+
RawEventAdapter,
|
|
7
|
+
RawUpdateAdapter,
|
|
8
|
+
)
|
|
1
9
|
from .buttons import BaseButton
|
|
10
|
+
from .callback_data_serilization import (
|
|
11
|
+
ABCDataSerializer,
|
|
12
|
+
JSONSerializer,
|
|
13
|
+
MsgPackSerializer,
|
|
14
|
+
)
|
|
2
15
|
from .error_handler import ABCErrorHandler, Catcher, CatcherError, ErrorHandler
|
|
3
16
|
from .formatting import (
|
|
4
|
-
|
|
5
|
-
|
|
17
|
+
Base,
|
|
18
|
+
BlockQuote,
|
|
6
19
|
FormatString,
|
|
7
20
|
HTMLFormatter,
|
|
8
|
-
InviteChatLink,
|
|
9
21
|
Link,
|
|
10
22
|
Mention,
|
|
11
23
|
PreCode,
|
|
12
|
-
ResolveDomain,
|
|
13
24
|
SpecialFormat,
|
|
14
|
-
StartBotLink,
|
|
15
|
-
StartGroupLink,
|
|
16
25
|
TgEmoji,
|
|
17
26
|
block_quote,
|
|
18
27
|
bold,
|
|
19
|
-
channel_boost_link,
|
|
20
28
|
code_inline,
|
|
21
29
|
escape,
|
|
22
|
-
get_channel_boost_link,
|
|
23
|
-
get_invite_chat_link,
|
|
24
|
-
get_mention_link,
|
|
25
|
-
get_resolve_domain_link,
|
|
26
|
-
get_start_bot_link,
|
|
27
|
-
get_start_group_link,
|
|
28
|
-
invite_chat_link,
|
|
29
30
|
italic,
|
|
30
31
|
link,
|
|
31
32
|
mention,
|
|
32
33
|
pre_code,
|
|
33
|
-
resolve_domain,
|
|
34
34
|
spoiler,
|
|
35
|
-
start_bot_link,
|
|
36
|
-
start_group_link,
|
|
37
35
|
strike,
|
|
36
|
+
tg_bot_attach_open_any_chat,
|
|
37
|
+
tg_bot_attach_open_current_chat,
|
|
38
|
+
tg_bot_attach_open_specific_chat,
|
|
39
|
+
tg_bot_start_link,
|
|
40
|
+
tg_bot_startchannel_link,
|
|
41
|
+
tg_bot_startgroup_link,
|
|
42
|
+
tg_chat_folder_link,
|
|
43
|
+
tg_chat_invite_link,
|
|
44
|
+
tg_direct_mini_app_link,
|
|
38
45
|
tg_emoji,
|
|
46
|
+
tg_emoji_link,
|
|
47
|
+
tg_emoji_stickerset_link,
|
|
48
|
+
tg_invoice_link,
|
|
49
|
+
tg_language_pack_link,
|
|
50
|
+
tg_main_mini_app_link,
|
|
51
|
+
tg_mention_link,
|
|
52
|
+
tg_open_message_link,
|
|
53
|
+
tg_premium_multigift_link,
|
|
54
|
+
tg_premium_offer_link,
|
|
55
|
+
tg_private_channel_boost_link,
|
|
56
|
+
tg_private_message_link,
|
|
57
|
+
tg_public_channel_boost_link,
|
|
58
|
+
tg_public_message_link,
|
|
59
|
+
tg_public_username_link,
|
|
60
|
+
tg_share_link,
|
|
61
|
+
tg_story_link,
|
|
39
62
|
underline,
|
|
40
63
|
)
|
|
64
|
+
from .functional import from_optional
|
|
41
65
|
from .global_context import (
|
|
42
66
|
ABCGlobalContext,
|
|
43
67
|
CtxVar,
|
|
@@ -54,7 +78,7 @@ from .i18n import (
|
|
|
54
78
|
SimpleI18n,
|
|
55
79
|
SimpleTranslator,
|
|
56
80
|
)
|
|
57
|
-
from .
|
|
81
|
+
from .input_file_directory import InputFileDirectory
|
|
58
82
|
from .keyboard import (
|
|
59
83
|
AnyMarkup,
|
|
60
84
|
Button,
|
|
@@ -65,11 +89,23 @@ from .keyboard import (
|
|
|
65
89
|
)
|
|
66
90
|
from .limited_dict import LimitedDict
|
|
67
91
|
from .loop_wrapper import ABCLoopWrapper, DelayedTask, Lifespan, LoopWrapper
|
|
68
|
-
from .magic import
|
|
92
|
+
from .magic import (
|
|
93
|
+
cancel_future,
|
|
94
|
+
get_annotations,
|
|
95
|
+
get_cached_translation,
|
|
96
|
+
get_default_args,
|
|
97
|
+
get_func_parameters,
|
|
98
|
+
get_impls,
|
|
99
|
+
impl,
|
|
100
|
+
magic_bundle,
|
|
101
|
+
resolve_arg_names,
|
|
102
|
+
)
|
|
69
103
|
from .parse_mode import ParseMode
|
|
70
104
|
from .state_storage import ABCStateStorage, MemoryStateStorage, StateData
|
|
71
105
|
|
|
72
106
|
__all__ = (
|
|
107
|
+
"ABCAdapter",
|
|
108
|
+
"ABCDataSerializer",
|
|
73
109
|
"ABCErrorHandler",
|
|
74
110
|
"ABCGlobalContext",
|
|
75
111
|
"ABCI18n",
|
|
@@ -78,15 +114,17 @@ __all__ = (
|
|
|
78
114
|
"ABCTranslator",
|
|
79
115
|
"ABCTranslatorMiddleware",
|
|
80
116
|
"AnyMarkup",
|
|
117
|
+
"Base",
|
|
81
118
|
"BaseButton",
|
|
82
|
-
"
|
|
119
|
+
"BlockQuote",
|
|
83
120
|
"Button",
|
|
84
121
|
"Catcher",
|
|
85
122
|
"CatcherError",
|
|
86
|
-
"ChannelBoostLink",
|
|
87
123
|
"CtxVar",
|
|
124
|
+
"DataclassAdapter",
|
|
88
125
|
"DelayedTask",
|
|
89
126
|
"ErrorHandler",
|
|
127
|
+
"EventAdapter",
|
|
90
128
|
"FormatString",
|
|
91
129
|
"GlobalContext",
|
|
92
130
|
"GlobalCtxVar",
|
|
@@ -94,53 +132,74 @@ __all__ = (
|
|
|
94
132
|
"I18nEnum",
|
|
95
133
|
"InlineButton",
|
|
96
134
|
"InlineKeyboard",
|
|
97
|
-
"
|
|
135
|
+
"InputFileDirectory",
|
|
136
|
+
"JSONSerializer",
|
|
98
137
|
"Keyboard",
|
|
99
|
-
"KeyboardSetBase",
|
|
100
|
-
"KeyboardSetYAML",
|
|
101
138
|
"Lifespan",
|
|
102
139
|
"LimitedDict",
|
|
103
140
|
"Link",
|
|
104
141
|
"LoopWrapper",
|
|
105
142
|
"MemoryStateStorage",
|
|
106
143
|
"Mention",
|
|
144
|
+
"MsgPackSerializer",
|
|
145
|
+
"NodeAdapter",
|
|
107
146
|
"ParseMode",
|
|
108
147
|
"PreCode",
|
|
109
|
-
"
|
|
148
|
+
"RawEventAdapter",
|
|
149
|
+
"RawUpdateAdapter",
|
|
110
150
|
"RowButtons",
|
|
111
151
|
"SimpleI18n",
|
|
112
152
|
"SimpleTranslator",
|
|
113
153
|
"SpecialFormat",
|
|
114
|
-
"StartBotLink",
|
|
115
|
-
"StartGroupLink",
|
|
116
154
|
"StateData",
|
|
117
155
|
"TelegrinderContext",
|
|
118
156
|
"TgEmoji",
|
|
119
157
|
"block_quote",
|
|
120
158
|
"bold",
|
|
121
|
-
"
|
|
159
|
+
"cancel_future",
|
|
122
160
|
"code_inline",
|
|
123
161
|
"ctx_var",
|
|
124
162
|
"escape",
|
|
125
|
-
"
|
|
126
|
-
"
|
|
127
|
-
"
|
|
128
|
-
"
|
|
129
|
-
"
|
|
130
|
-
"
|
|
163
|
+
"from_optional",
|
|
164
|
+
"get_annotations",
|
|
165
|
+
"get_cached_translation",
|
|
166
|
+
"get_default_args",
|
|
167
|
+
"get_func_parameters",
|
|
168
|
+
"get_impls",
|
|
131
169
|
"impl",
|
|
132
|
-
"invite_chat_link",
|
|
133
170
|
"italic",
|
|
134
171
|
"link",
|
|
135
172
|
"magic_bundle",
|
|
136
173
|
"mention",
|
|
137
174
|
"pre_code",
|
|
138
175
|
"resolve_arg_names",
|
|
139
|
-
"resolve_domain",
|
|
140
176
|
"spoiler",
|
|
141
|
-
"start_bot_link",
|
|
142
|
-
"start_group_link",
|
|
143
177
|
"strike",
|
|
178
|
+
"tg_bot_attach_open_any_chat",
|
|
179
|
+
"tg_bot_attach_open_current_chat",
|
|
180
|
+
"tg_bot_attach_open_specific_chat",
|
|
181
|
+
"tg_bot_start_link",
|
|
182
|
+
"tg_bot_startchannel_link",
|
|
183
|
+
"tg_bot_startgroup_link",
|
|
184
|
+
"tg_chat_folder_link",
|
|
185
|
+
"tg_chat_invite_link",
|
|
186
|
+
"tg_direct_mini_app_link",
|
|
144
187
|
"tg_emoji",
|
|
188
|
+
"tg_emoji_link",
|
|
189
|
+
"tg_emoji_stickerset_link",
|
|
190
|
+
"tg_invoice_link",
|
|
191
|
+
"tg_language_pack_link",
|
|
192
|
+
"tg_main_mini_app_link",
|
|
193
|
+
"tg_mention_link",
|
|
194
|
+
"tg_open_message_link",
|
|
195
|
+
"tg_premium_multigift_link",
|
|
196
|
+
"tg_premium_offer_link",
|
|
197
|
+
"tg_private_channel_boost_link",
|
|
198
|
+
"tg_private_message_link",
|
|
199
|
+
"tg_public_channel_boost_link",
|
|
200
|
+
"tg_public_message_link",
|
|
201
|
+
"tg_public_username_link",
|
|
202
|
+
"tg_share_link",
|
|
203
|
+
"tg_story_link",
|
|
145
204
|
"underline",
|
|
146
205
|
)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from telegrinder.tools.adapter.abc import ABCAdapter, AdaptResult, Event
|
|
2
|
+
from telegrinder.tools.adapter.dataclass import DataclassAdapter
|
|
3
|
+
from telegrinder.tools.adapter.errors import AdapterError
|
|
4
|
+
from telegrinder.tools.adapter.event import EventAdapter
|
|
5
|
+
from telegrinder.tools.adapter.node import NodeAdapter
|
|
6
|
+
from telegrinder.tools.adapter.raw_event import RawEventAdapter
|
|
7
|
+
from telegrinder.tools.adapter.raw_update import RawUpdateAdapter
|
|
8
|
+
|
|
9
|
+
__all__ = (
|
|
10
|
+
"ABCAdapter",
|
|
11
|
+
"AdaptResult",
|
|
12
|
+
"AdapterError",
|
|
13
|
+
"DataclassAdapter",
|
|
14
|
+
"Event",
|
|
15
|
+
"EventAdapter",
|
|
16
|
+
"NodeAdapter",
|
|
17
|
+
"RawEventAdapter",
|
|
18
|
+
"RawUpdateAdapter",
|
|
19
|
+
)
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
import dataclasses
|
|
3
|
+
import inspect
|
|
4
|
+
import typing
|
|
5
|
+
|
|
6
|
+
from fntypes import Error, Nothing, Ok, Option, Some
|
|
7
|
+
from fntypes.result import Result
|
|
8
|
+
|
|
9
|
+
from telegrinder.modules import logger
|
|
10
|
+
from telegrinder.tools.adapter.errors import AdapterError
|
|
11
|
+
|
|
12
|
+
type AdaptResult[To] = Result[To, AdapterError] | typing.Awaitable[Result[To, AdapterError]]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
if typing.TYPE_CHECKING:
|
|
16
|
+
from telegrinder.api.api import API
|
|
17
|
+
from telegrinder.bot.dispatch.context import Context
|
|
18
|
+
from telegrinder.model import Model
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ABCAdapter[From: "Model", To](abc.ABC):
|
|
22
|
+
ADAPTED_VALUE_KEY: str | None = None
|
|
23
|
+
|
|
24
|
+
@abc.abstractmethod
|
|
25
|
+
def adapt(self, api: "API", update: From, context: "Context") -> AdaptResult[To]:
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclasses.dataclass(slots=True)
|
|
30
|
+
class Event[To]:
|
|
31
|
+
obj: To
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
async def run_adapter[T, U: "Model"](
|
|
35
|
+
adapter: "ABCAdapter[U, T]",
|
|
36
|
+
api: "API",
|
|
37
|
+
update: U,
|
|
38
|
+
context: "Context",
|
|
39
|
+
) -> Option[T]:
|
|
40
|
+
adapt_result = adapter.adapt(api, update, context)
|
|
41
|
+
match await adapt_result if inspect.isawaitable(adapt_result) else adapt_result:
|
|
42
|
+
case Ok(value):
|
|
43
|
+
return Some(value)
|
|
44
|
+
case Error(err):
|
|
45
|
+
logger.debug("Adapter {!r} failed with error message: {!r}", adapter, str(err))
|
|
46
|
+
return Nothing()
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
__all__ = ("ABCAdapter", "AdaptResult", "Event", "run_adapter")
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
from fntypes.option import Nothing, Some
|
|
2
|
+
from fntypes.result import Error, Ok, Result
|
|
3
|
+
|
|
4
|
+
from telegrinder.api.api import API
|
|
5
|
+
from telegrinder.bot.cute_types.base import BaseCute
|
|
6
|
+
from telegrinder.bot.cute_types.update import UpdateCute
|
|
7
|
+
from telegrinder.bot.dispatch.context import Context
|
|
8
|
+
from telegrinder.tools.adapter.abc import ABCAdapter
|
|
9
|
+
from telegrinder.tools.adapter.errors import AdapterError
|
|
10
|
+
from telegrinder.types.enums import UpdateType
|
|
11
|
+
from telegrinder.types.objects import Update
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class DataclassAdapter[Dataclass](ABCAdapter[Update, Dataclass]):
|
|
15
|
+
ADAPTED_VALUE_KEY: str
|
|
16
|
+
|
|
17
|
+
def __init__(
|
|
18
|
+
self,
|
|
19
|
+
dataclass: type[Dataclass],
|
|
20
|
+
update_type: UpdateType | None = None,
|
|
21
|
+
) -> None:
|
|
22
|
+
self.ADAPTED_VALUE_KEY = f"_adapted_dataclass_{dataclass.__name__}"
|
|
23
|
+
self.dataclass = dataclass
|
|
24
|
+
self.update_type = update_type
|
|
25
|
+
|
|
26
|
+
def __repr__(self) -> str:
|
|
27
|
+
return f"<Update -> {self.dataclass.__name__}>"
|
|
28
|
+
|
|
29
|
+
def adapt(self, api: API, update: Update, context: Context) -> Result[Dataclass, AdapterError]:
|
|
30
|
+
if self.ADAPTED_VALUE_KEY in context:
|
|
31
|
+
return Ok(context[self.ADAPTED_VALUE_KEY])
|
|
32
|
+
|
|
33
|
+
update_type = (self.update_type or update.update_type).value
|
|
34
|
+
try:
|
|
35
|
+
if self.dataclass is Update:
|
|
36
|
+
return Ok(update) # type: ignore
|
|
37
|
+
elif issubclass(self.dataclass, UpdateCute):
|
|
38
|
+
dataclass = self.dataclass.from_update(update, bound_api=api)
|
|
39
|
+
else:
|
|
40
|
+
match getattr(update, update_type):
|
|
41
|
+
case Some(val):
|
|
42
|
+
dataclass = (
|
|
43
|
+
self.dataclass.from_update(val, bound_api=api)
|
|
44
|
+
if issubclass(self.dataclass, BaseCute)
|
|
45
|
+
else self.dataclass(**val.to_dict())
|
|
46
|
+
)
|
|
47
|
+
case Nothing():
|
|
48
|
+
return Error(AdapterError(f"Update has no event {update_type!r}."))
|
|
49
|
+
except Exception as e:
|
|
50
|
+
return Error(AdapterError(f"Cannot adapt Update to {self.dataclass!r}, error: {e!r}"))
|
|
51
|
+
|
|
52
|
+
context[self.ADAPTED_VALUE_KEY] = dataclass
|
|
53
|
+
return Ok(dataclass) # type: ignore
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
__all__ = ("DataclassAdapter",)
|
|
@@ -6,19 +6,17 @@ from telegrinder.api.api import API
|
|
|
6
6
|
from telegrinder.bot.cute_types.base import BaseCute
|
|
7
7
|
from telegrinder.bot.cute_types.update import UpdateCute
|
|
8
8
|
from telegrinder.bot.dispatch.context import Context
|
|
9
|
-
from telegrinder.
|
|
10
|
-
from telegrinder.
|
|
11
|
-
from telegrinder.
|
|
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
12
|
from telegrinder.types.enums import UpdateType
|
|
13
13
|
from telegrinder.types.objects import Model, Update
|
|
14
14
|
|
|
15
|
-
ToCute = typing.TypeVar("ToCute", bound=BaseCute)
|
|
16
15
|
|
|
17
|
-
|
|
18
|
-
class EventAdapter(ABCAdapter[Update, ToCute]):
|
|
16
|
+
class EventAdapter[ToEvent: BaseCute](ABCAdapter[Update, ToEvent]):
|
|
19
17
|
ADAPTED_VALUE_KEY: str = "_adapted_cute_event"
|
|
20
18
|
|
|
21
|
-
def __init__(self, event: UpdateType | type[Model], cute_model: type[
|
|
19
|
+
def __init__(self, event: UpdateType | type[Model], cute_model: type[ToEvent]) -> None:
|
|
22
20
|
self.event = event
|
|
23
21
|
self.cute_model = cute_model
|
|
24
22
|
|
|
@@ -26,7 +24,7 @@ class EventAdapter(ABCAdapter[Update, ToCute]):
|
|
|
26
24
|
raw_update_type = (
|
|
27
25
|
f"Update -> {self.event.__name__}"
|
|
28
26
|
if isinstance(self.event, type)
|
|
29
|
-
else f"Update.{self.event.value}"
|
|
27
|
+
else f"Update.{self.event.value}" # type: ignore
|
|
30
28
|
)
|
|
31
29
|
return "<{}: adapt {} -> {}>".format(
|
|
32
30
|
self.__class__.__name__,
|
|
@@ -43,14 +41,14 @@ class EventAdapter(ABCAdapter[Update, ToCute]):
|
|
|
43
41
|
|
|
44
42
|
return None
|
|
45
43
|
|
|
46
|
-
def adapt(self, api: API, update: Update, context: Context) -> Result[
|
|
44
|
+
def adapt(self, api: API, update: Update, context: Context) -> Result[ToEvent, AdapterError]:
|
|
47
45
|
match RawUpdateAdapter().adapt(api, update, context):
|
|
48
46
|
case Ok(update_cute) if event := self.get_event(update_cute):
|
|
49
47
|
if self.ADAPTED_VALUE_KEY in context:
|
|
50
48
|
return Ok(context[self.ADAPTED_VALUE_KEY])
|
|
51
49
|
|
|
52
50
|
adapted = (
|
|
53
|
-
typing.cast(
|
|
51
|
+
typing.cast(ToEvent, event)
|
|
54
52
|
if isinstance(event, BaseCute)
|
|
55
53
|
else self.cute_model.from_update(event, bound_api=api)
|
|
56
54
|
)
|