telegrinder 0.3.4.post1__py3-none-any.whl → 0.4.1__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 +55 -129
- 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 +28 -9
- 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 +236 -60
- 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.1.dist-info}/LICENSE +2 -2
- telegrinder-0.4.1.dist-info/METADATA +143 -0
- telegrinder-0.4.1.dist-info/RECORD +182 -0
- {telegrinder-0.3.4.post1.dist-info → telegrinder-0.4.1.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
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
|
|
3
|
+
from fntypes.result import Result
|
|
4
|
+
|
|
5
|
+
from telegrinder.api.api import API
|
|
6
|
+
from telegrinder.api.error import APIError
|
|
7
|
+
from telegrinder.bot.cute_types.base import BaseCute, compose_method_params
|
|
8
|
+
from telegrinder.model import get_params
|
|
9
|
+
from telegrinder.tools.magic import shortcut
|
|
10
|
+
from telegrinder.types.objects import PreCheckoutQuery, User
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class PreCheckoutQueryCute(BaseCute[PreCheckoutQuery], PreCheckoutQuery, kw_only=True):
|
|
14
|
+
api: API
|
|
15
|
+
|
|
16
|
+
@property
|
|
17
|
+
def from_user(self) -> User:
|
|
18
|
+
return self.from_
|
|
19
|
+
|
|
20
|
+
@shortcut("answer_pre_checkout_query", custom_params={"pre_checkout_query_id"})
|
|
21
|
+
async def answer(
|
|
22
|
+
self,
|
|
23
|
+
ok: bool,
|
|
24
|
+
*,
|
|
25
|
+
error_message: str | None = None,
|
|
26
|
+
pre_checkout_query_id: str | None = None,
|
|
27
|
+
**other: typing.Any,
|
|
28
|
+
) -> Result[bool, APIError]:
|
|
29
|
+
"""Shortcut `API.answer_pre_checkout_query()`, see the [documentation](https://core.telegram.org/bots/api#answerprecheckoutquery)
|
|
30
|
+
|
|
31
|
+
Once the user has confirmed their payment and shipping details, the Bot
|
|
32
|
+
API sends the final confirmation in the form of an Update with the field pre_checkout_query.
|
|
33
|
+
Use this method to respond to such pre-checkout queries. On success, True
|
|
34
|
+
is returned. Note: The Bot API must receive an answer within 10 seconds after
|
|
35
|
+
the pre-checkout query was sent."""
|
|
36
|
+
params = compose_method_params(
|
|
37
|
+
get_params(locals()), self, default_params={("pre_checkout_query_id", "id")}
|
|
38
|
+
)
|
|
39
|
+
return await self.ctx_api.answer_pre_checkout_query(**params)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
__all__ = ("PreCheckoutQueryCute",)
|
|
@@ -9,7 +9,8 @@ from telegrinder.bot.cute_types.chat_join_request import ChatJoinRequestCute
|
|
|
9
9
|
from telegrinder.bot.cute_types.chat_member_updated import ChatMemberUpdatedCute
|
|
10
10
|
from telegrinder.bot.cute_types.inline_query import InlineQueryCute
|
|
11
11
|
from telegrinder.bot.cute_types.message import MessageCute
|
|
12
|
-
from telegrinder.
|
|
12
|
+
from telegrinder.bot.cute_types.pre_checkout_query import PreCheckoutQueryCute
|
|
13
|
+
from telegrinder.model import UNSET, From, field
|
|
13
14
|
from telegrinder.msgspec_utils import Option
|
|
14
15
|
from telegrinder.types.objects import *
|
|
15
16
|
|
|
@@ -20,13 +21,13 @@ class UpdateCute(BaseCute[Update], Update, kw_only=True):
|
|
|
20
21
|
api: API
|
|
21
22
|
|
|
22
23
|
message: Option[MessageCute] = field(
|
|
23
|
-
default=
|
|
24
|
+
default=UNSET,
|
|
24
25
|
converter=From[MessageCute | None],
|
|
25
26
|
)
|
|
26
27
|
"""Optional. New incoming message of any kind - text, photo, sticker, etc."""
|
|
27
28
|
|
|
28
29
|
edited_message: Option[MessageCute] = field(
|
|
29
|
-
default=
|
|
30
|
+
default=UNSET,
|
|
30
31
|
converter=From[MessageCute | None],
|
|
31
32
|
)
|
|
32
33
|
"""Optional. New version of a message that is known to the bot and was edited.
|
|
@@ -34,14 +35,14 @@ class UpdateCute(BaseCute[Update], Update, kw_only=True):
|
|
|
34
35
|
either unavailable or not actively used by your bot."""
|
|
35
36
|
|
|
36
37
|
channel_post: Option[MessageCute] = field(
|
|
37
|
-
default=
|
|
38
|
+
default=UNSET,
|
|
38
39
|
converter=From[MessageCute | None],
|
|
39
40
|
)
|
|
40
41
|
"""Optional. New incoming channel post of any kind - text, photo, sticker,
|
|
41
42
|
etc."""
|
|
42
43
|
|
|
43
44
|
edited_channel_post: Option[MessageCute] = field(
|
|
44
|
-
default=
|
|
45
|
+
default=UNSET,
|
|
45
46
|
converter=From[MessageCute | None],
|
|
46
47
|
)
|
|
47
48
|
"""Optional. New version of a channel post that is known to the bot and was edited.
|
|
@@ -49,31 +50,31 @@ class UpdateCute(BaseCute[Update], Update, kw_only=True):
|
|
|
49
50
|
either unavailable or not actively used by your bot."""
|
|
50
51
|
|
|
51
52
|
business_message: Option[MessageCute] = field(
|
|
52
|
-
default=
|
|
53
|
+
default=UNSET,
|
|
53
54
|
converter=From[MessageCute | None],
|
|
54
55
|
)
|
|
55
56
|
"""Optional. New message from a connected business account."""
|
|
56
57
|
|
|
57
58
|
edited_business_message: Option[MessageCute] = field(
|
|
58
|
-
default=
|
|
59
|
+
default=UNSET,
|
|
59
60
|
converter=From[MessageCute | None],
|
|
60
61
|
)
|
|
61
62
|
"""Optional. New version of a message from a connected business account."""
|
|
62
63
|
|
|
63
64
|
inline_query: Option[InlineQueryCute] = field(
|
|
64
|
-
default=
|
|
65
|
+
default=UNSET,
|
|
65
66
|
converter=From[InlineQueryCute | None],
|
|
66
67
|
)
|
|
67
68
|
"""Optional. New incoming inline query."""
|
|
68
69
|
|
|
69
70
|
callback_query: Option[CallbackQueryCute] = field(
|
|
70
|
-
default=
|
|
71
|
+
default=UNSET,
|
|
71
72
|
converter=From[CallbackQueryCute | None],
|
|
72
73
|
)
|
|
73
74
|
"""Optional. New incoming callback query."""
|
|
74
75
|
|
|
75
76
|
my_chat_member: Option[ChatMemberUpdatedCute] = field(
|
|
76
|
-
default=
|
|
77
|
+
default=UNSET,
|
|
77
78
|
converter=From[ChatMemberUpdatedCute | None],
|
|
78
79
|
)
|
|
79
80
|
"""Optional. The bot's chat member status was updated in a chat. For private
|
|
@@ -81,7 +82,7 @@ class UpdateCute(BaseCute[Update], Update, kw_only=True):
|
|
|
81
82
|
by the user."""
|
|
82
83
|
|
|
83
84
|
chat_member: Option[ChatMemberUpdatedCute] = field(
|
|
84
|
-
default=
|
|
85
|
+
default=UNSET,
|
|
85
86
|
converter=From[ChatMemberUpdatedCute | None],
|
|
86
87
|
)
|
|
87
88
|
"""Optional. A chat member's status was updated in a chat. The bot must be an
|
|
@@ -89,12 +90,19 @@ class UpdateCute(BaseCute[Update], Update, kw_only=True):
|
|
|
89
90
|
the list of allowed_updates to receive these updates."""
|
|
90
91
|
|
|
91
92
|
chat_join_request: Option[ChatJoinRequestCute] = field(
|
|
92
|
-
default=
|
|
93
|
+
default=UNSET,
|
|
93
94
|
converter=From[ChatJoinRequestCute | None],
|
|
94
95
|
)
|
|
95
96
|
"""Optional. A request to join the chat has been sent. The bot must have the can_invite_users
|
|
96
97
|
administrator right in the chat to receive these updates."""
|
|
97
98
|
|
|
99
|
+
pre_checkout_query: Option[PreCheckoutQueryCute] = field(
|
|
100
|
+
default=UNSET,
|
|
101
|
+
converter=From[PreCheckoutQueryCute | None],
|
|
102
|
+
)
|
|
103
|
+
"""Optional. New incoming pre-checkout query. Contains full information
|
|
104
|
+
about checkout."""
|
|
105
|
+
|
|
98
106
|
def get_event(self, event_model: type[EventModel]) -> Option[EventModel]:
|
|
99
107
|
if isinstance(self.incoming_update, event_model):
|
|
100
108
|
return Some(self.incoming_update)
|
|
@@ -8,15 +8,13 @@ from telegrinder.types.objects import (
|
|
|
8
8
|
InputMediaDocument,
|
|
9
9
|
InputMediaPhoto,
|
|
10
10
|
InputMediaVideo,
|
|
11
|
-
LinkPreviewOptions,
|
|
12
11
|
MessageEntity,
|
|
13
12
|
ReactionEmoji,
|
|
14
13
|
ReactionType,
|
|
15
14
|
ReactionTypeEmoji,
|
|
16
|
-
ReplyParameters,
|
|
17
15
|
)
|
|
18
16
|
|
|
19
|
-
InputMedia
|
|
17
|
+
type InputMedia = typing.Union[
|
|
20
18
|
InputMediaAnimation,
|
|
21
19
|
InputMediaAudio,
|
|
22
20
|
InputMediaDocument,
|
|
@@ -45,36 +43,10 @@ def compose_reactions(
|
|
|
45
43
|
if isinstance(emoji, ReactionEmoji)
|
|
46
44
|
else (ReactionTypeEmoji(ReactionEmoji(emoji)) if isinstance(emoji, str) else emoji)
|
|
47
45
|
)
|
|
48
|
-
for emoji in
|
|
46
|
+
for emoji in reactions
|
|
49
47
|
]
|
|
50
48
|
|
|
51
49
|
|
|
52
|
-
def compose_reply_params(
|
|
53
|
-
message_id: int | None,
|
|
54
|
-
chat_id: int | str | None,
|
|
55
|
-
*,
|
|
56
|
-
allow_sending_without_reply: bool | None = None,
|
|
57
|
-
quote: str | None = None,
|
|
58
|
-
quote_parse_mode: str | None = None,
|
|
59
|
-
quote_entities: list[MessageEntity] | None = None,
|
|
60
|
-
quote_position: int | None = None,
|
|
61
|
-
**other: typing.Any,
|
|
62
|
-
) -> ReplyParameters:
|
|
63
|
-
return ReplyParameters(**get_params(locals()))
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
def compose_link_preview_options(
|
|
67
|
-
*,
|
|
68
|
-
is_disabled: bool | None = None,
|
|
69
|
-
url: str | None = None,
|
|
70
|
-
prefer_small_media: bool | None = None,
|
|
71
|
-
prefer_large_media: bool | None = None,
|
|
72
|
-
show_above_text: bool | None = None,
|
|
73
|
-
**other: typing.Any,
|
|
74
|
-
) -> LinkPreviewOptions:
|
|
75
|
-
return LinkPreviewOptions(**get_params(locals()))
|
|
76
|
-
|
|
77
|
-
|
|
78
50
|
def input_media(
|
|
79
51
|
type: typing.Literal["animation", "audio", "document", "photo", "video"],
|
|
80
52
|
media: str | InputFile,
|
|
@@ -87,9 +59,4 @@ def input_media(
|
|
|
87
59
|
return INPUT_MEDIA_TYPES[type](**get_params(locals()))
|
|
88
60
|
|
|
89
61
|
|
|
90
|
-
__all__ = (
|
|
91
|
-
"compose_link_preview_options",
|
|
92
|
-
"compose_reactions",
|
|
93
|
-
"compose_reply_params",
|
|
94
|
-
"input_media",
|
|
95
|
-
)
|
|
62
|
+
__all__ = ("compose_reactions", "input_media")
|
|
@@ -21,6 +21,7 @@ from telegrinder.bot.dispatch.return_manager import (
|
|
|
21
21
|
InlineQueryReturnManager,
|
|
22
22
|
Manager,
|
|
23
23
|
MessageReturnManager,
|
|
24
|
+
PreCheckoutQueryManager,
|
|
24
25
|
register_manager,
|
|
25
26
|
)
|
|
26
27
|
from telegrinder.bot.dispatch.view import (
|
|
@@ -33,6 +34,7 @@ from telegrinder.bot.dispatch.view import (
|
|
|
33
34
|
ChatMemberView,
|
|
34
35
|
InlineQueryView,
|
|
35
36
|
MessageView,
|
|
37
|
+
PreCheckoutQueryView,
|
|
36
38
|
RawEventView,
|
|
37
39
|
ViewBox,
|
|
38
40
|
)
|
|
@@ -84,6 +86,8 @@ __all__ = (
|
|
|
84
86
|
"MessageReturnManager",
|
|
85
87
|
"MessageView",
|
|
86
88
|
"PhotoReplyHandler",
|
|
89
|
+
"PreCheckoutQueryManager",
|
|
90
|
+
"PreCheckoutQueryView",
|
|
87
91
|
"RawEventView",
|
|
88
92
|
"ShortState",
|
|
89
93
|
"StateViewHasher",
|
telegrinder/bot/dispatch/abc.py
CHANGED
|
@@ -8,10 +8,10 @@ from abc import ABC, abstractmethod
|
|
|
8
8
|
from fntypes.option import Option
|
|
9
9
|
|
|
10
10
|
from telegrinder.api.api import API
|
|
11
|
-
from telegrinder.tools.global_context.abc import ABCGlobalContext
|
|
12
11
|
from telegrinder.types.objects import Update
|
|
13
12
|
|
|
14
|
-
|
|
13
|
+
if typing.TYPE_CHECKING:
|
|
14
|
+
from telegrinder.bot.dispatch.view.abc import ABCView
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
class PathExistsError(BaseException):
|
|
@@ -19,21 +19,20 @@ class PathExistsError(BaseException):
|
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
class ABCDispatch(ABC):
|
|
22
|
-
@property
|
|
23
22
|
@abstractmethod
|
|
24
|
-
def
|
|
23
|
+
async def feed(self, event: Update, api: API[typing.Any]) -> bool:
|
|
25
24
|
pass
|
|
26
25
|
|
|
27
26
|
@abstractmethod
|
|
28
|
-
|
|
27
|
+
def load(self, external: typing.Self) -> None:
|
|
29
28
|
pass
|
|
30
29
|
|
|
31
30
|
@abstractmethod
|
|
32
|
-
def
|
|
31
|
+
def get_view[T](self, of_type: type[T]) -> Option[T]:
|
|
33
32
|
pass
|
|
34
33
|
|
|
35
34
|
@abstractmethod
|
|
36
|
-
def
|
|
35
|
+
def get_views(self) -> dict[str, "ABCView"]:
|
|
37
36
|
pass
|
|
38
37
|
|
|
39
38
|
def load_many(self, *externals: typing.Self) -> None:
|
|
@@ -43,8 +42,8 @@ class ABCDispatch(ABC):
|
|
|
43
42
|
def load_from_dir(self, directory: str | pathlib.Path) -> bool:
|
|
44
43
|
"""Loads dispatchers from a directory containing Python modules where global variables
|
|
45
44
|
are declared with instances of dispatch.
|
|
46
|
-
Returns True if dispatchers were found, otherwise False.
|
|
47
|
-
|
|
45
|
+
Returns True if dispatchers were found, otherwise False.
|
|
46
|
+
"""
|
|
48
47
|
directory = pathlib.Path(directory)
|
|
49
48
|
|
|
50
49
|
if not directory.exists():
|
|
@@ -9,10 +9,8 @@ from telegrinder.types.objects import Update
|
|
|
9
9
|
if typing.TYPE_CHECKING:
|
|
10
10
|
from telegrinder.node.composer import NodeCollection
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
Key: typing.TypeAlias = str | enum.Enum
|
|
15
|
-
AnyValue: typing.TypeAlias = typing.Any
|
|
12
|
+
type Key = str | enum.Enum
|
|
13
|
+
type AnyValue = typing.Any
|
|
16
14
|
|
|
17
15
|
|
|
18
16
|
class Context(dict[str, AnyValue]):
|
|
@@ -78,15 +76,15 @@ class Context(dict[str, AnyValue]):
|
|
|
78
76
|
def get(self, key: Key) -> AnyValue | None: ...
|
|
79
77
|
|
|
80
78
|
@typing.overload
|
|
81
|
-
def get(self, key: Key, default: T) -> T | AnyValue: ...
|
|
79
|
+
def get[T](self, key: Key, default: T) -> T | AnyValue: ...
|
|
82
80
|
|
|
83
81
|
@typing.overload
|
|
84
82
|
def get(self, key: Key, default: None = None) -> AnyValue | None: ...
|
|
85
83
|
|
|
86
|
-
def get(self, key: Key, default: T | None = None) -> T | AnyValue | None:
|
|
84
|
+
def get[T](self, key: Key, default: T | None = None) -> T | AnyValue | None:
|
|
87
85
|
return dict.get(self, key, default)
|
|
88
86
|
|
|
89
|
-
def get_or_set(self, key: Key, default: T) -> T:
|
|
87
|
+
def get_or_set[T](self, key: Key, default: T) -> T:
|
|
90
88
|
if key not in self:
|
|
91
89
|
self.set(key, default)
|
|
92
90
|
return self.get(key, default)
|
|
@@ -1,20 +1,25 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import dataclasses
|
|
2
4
|
|
|
3
5
|
import typing_extensions as typing
|
|
4
6
|
from fntypes import Nothing, Option, Some
|
|
5
7
|
from vbml.patcher import Patcher
|
|
6
8
|
|
|
7
|
-
from telegrinder.api.api import API
|
|
8
|
-
from telegrinder.bot.cute_types.base import BaseCute
|
|
9
|
-
from telegrinder.bot.cute_types.update import UpdateCute
|
|
9
|
+
from telegrinder.api.api import API, HTTPClient
|
|
10
10
|
from telegrinder.bot.dispatch.abc import ABCDispatch
|
|
11
|
+
from telegrinder.bot.dispatch.context import Context
|
|
11
12
|
from telegrinder.bot.dispatch.handler.func import ErrorHandlerT, Func, FuncHandler
|
|
13
|
+
from telegrinder.bot.dispatch.middleware.abc import run_middleware
|
|
14
|
+
from telegrinder.bot.dispatch.middleware.global_middleware import GlobalMiddleware
|
|
15
|
+
from telegrinder.bot.dispatch.view.abc import ABCView
|
|
12
16
|
from telegrinder.bot.dispatch.view.box import (
|
|
13
17
|
CallbackQueryView,
|
|
14
18
|
ChatJoinRequestView,
|
|
15
19
|
ChatMemberView,
|
|
16
20
|
InlineQueryView,
|
|
17
21
|
MessageView,
|
|
22
|
+
PreCheckoutQueryView,
|
|
18
23
|
RawEventView,
|
|
19
24
|
ViewBox,
|
|
20
25
|
)
|
|
@@ -25,11 +30,13 @@ from telegrinder.types.enums import UpdateType
|
|
|
25
30
|
from telegrinder.types.objects import Update
|
|
26
31
|
|
|
27
32
|
if typing.TYPE_CHECKING:
|
|
33
|
+
from telegrinder.bot.cute_types.base import BaseCute
|
|
34
|
+
from telegrinder.bot.cute_types.update import UpdateCute
|
|
28
35
|
from telegrinder.bot.rules.abc import ABCRule
|
|
29
36
|
|
|
30
37
|
T = typing.TypeVar("T", default=typing.Any)
|
|
31
38
|
R = typing.TypeVar("R", covariant=True, default=typing.Any)
|
|
32
|
-
Event = typing.TypeVar("Event", bound=BaseCute)
|
|
39
|
+
Event = typing.TypeVar("Event", bound="BaseCute")
|
|
33
40
|
P = typing.ParamSpec("P", default=...)
|
|
34
41
|
|
|
35
42
|
DEFAULT_DATACLASS: typing.Final[type[Update]] = Update
|
|
@@ -44,12 +51,26 @@ class Dispatch(
|
|
|
44
51
|
ChatMemberView,
|
|
45
52
|
InlineQueryView,
|
|
46
53
|
MessageView,
|
|
54
|
+
PreCheckoutQueryView,
|
|
55
|
+
RawEventView,
|
|
56
|
+
],
|
|
57
|
+
typing.Generic[
|
|
58
|
+
HTTPClient,
|
|
59
|
+
CallbackQueryView,
|
|
60
|
+
ChatJoinRequestView,
|
|
61
|
+
ChatMemberView,
|
|
62
|
+
InlineQueryView,
|
|
63
|
+
MessageView,
|
|
64
|
+
PreCheckoutQueryView,
|
|
47
65
|
RawEventView,
|
|
48
66
|
],
|
|
49
67
|
):
|
|
50
68
|
_global_context: TelegrinderContext = dataclasses.field(
|
|
51
69
|
init=False,
|
|
52
|
-
default_factory=
|
|
70
|
+
default_factory=TelegrinderContext,
|
|
71
|
+
)
|
|
72
|
+
global_middleware: "GlobalMiddleware" = dataclasses.field(
|
|
73
|
+
default_factory=lambda: GlobalMiddleware(),
|
|
53
74
|
)
|
|
54
75
|
|
|
55
76
|
def __repr__(self) -> str:
|
|
@@ -62,31 +83,30 @@ class Dispatch(
|
|
|
62
83
|
@property
|
|
63
84
|
def patcher(self) -> Patcher:
|
|
64
85
|
"""Alias `patcher` to get `vbml.Patcher` from the global context."""
|
|
65
|
-
|
|
66
86
|
return self.global_context.vbml_patcher
|
|
67
87
|
|
|
68
88
|
@typing.overload
|
|
69
89
|
def handle(
|
|
70
90
|
self,
|
|
71
91
|
*rules: "ABCRule",
|
|
72
|
-
|
|
73
|
-
) -> typing.Callable[[Func[P, R]], FuncHandler[UpdateCute, Func[P, R], ErrorHandler[UpdateCute]]]: ...
|
|
92
|
+
final: bool = True,
|
|
93
|
+
) -> typing.Callable[[Func[P, R]], FuncHandler["UpdateCute", Func[P, R], ErrorHandler[UpdateCute]]]: ...
|
|
74
94
|
|
|
75
95
|
@typing.overload
|
|
76
96
|
def handle(
|
|
77
97
|
self,
|
|
78
98
|
*rules: "ABCRule",
|
|
79
99
|
dataclass: type[T],
|
|
80
|
-
|
|
81
|
-
) -> typing.Callable[[Func[P, R]], FuncHandler[UpdateCute, Func[P, R], ErrorHandler[T]]]: ...
|
|
100
|
+
final: bool = True,
|
|
101
|
+
) -> typing.Callable[[Func[P, R]], FuncHandler["UpdateCute", Func[P, R], ErrorHandler[T]]]: ...
|
|
82
102
|
|
|
83
103
|
@typing.overload
|
|
84
104
|
def handle(
|
|
85
105
|
self,
|
|
86
106
|
*rules: "ABCRule",
|
|
87
107
|
update_type: UpdateType,
|
|
88
|
-
|
|
89
|
-
) -> typing.Callable[[Func[P, R]], FuncHandler[UpdateCute, Func[P, R], ErrorHandler[UpdateCute]]]: ...
|
|
108
|
+
final: bool = True,
|
|
109
|
+
) -> typing.Callable[[Func[P, R]], FuncHandler["UpdateCute", Func[P, R], ErrorHandler[UpdateCute]]]: ...
|
|
90
110
|
|
|
91
111
|
@typing.overload
|
|
92
112
|
def handle(
|
|
@@ -94,16 +114,16 @@ class Dispatch(
|
|
|
94
114
|
*rules: "ABCRule",
|
|
95
115
|
dataclass: type[T],
|
|
96
116
|
update_type: UpdateType,
|
|
97
|
-
|
|
98
|
-
) -> typing.Callable[[Func[P, R]], FuncHandler[UpdateCute, Func[P, R], ErrorHandler[T]]]: ...
|
|
117
|
+
final: bool = True,
|
|
118
|
+
) -> typing.Callable[[Func[P, R]], FuncHandler["UpdateCute", Func[P, R], ErrorHandler[T]]]: ...
|
|
99
119
|
|
|
100
120
|
@typing.overload
|
|
101
121
|
def handle(
|
|
102
122
|
self,
|
|
103
123
|
*rules: "ABCRule",
|
|
104
124
|
error_handler: ErrorHandlerT,
|
|
105
|
-
|
|
106
|
-
) -> typing.Callable[[Func[P, R]], FuncHandler[UpdateCute, Func[P, R], ErrorHandlerT]]: ...
|
|
125
|
+
final: bool = True,
|
|
126
|
+
) -> typing.Callable[[Func[P, R]], FuncHandler["UpdateCute", Func[P, R], ErrorHandlerT]]: ...
|
|
107
127
|
|
|
108
128
|
@typing.overload
|
|
109
129
|
def handle(
|
|
@@ -111,27 +131,27 @@ class Dispatch(
|
|
|
111
131
|
*rules: "ABCRule",
|
|
112
132
|
update_type: UpdateType,
|
|
113
133
|
error_handler: ErrorHandlerT,
|
|
114
|
-
|
|
115
|
-
) -> typing.Callable[[Func[P, R]], FuncHandler[UpdateCute, Func[P, R], ErrorHandlerT]]: ...
|
|
134
|
+
final: bool = True,
|
|
135
|
+
) -> typing.Callable[[Func[P, R]], FuncHandler["UpdateCute", Func[P, R], ErrorHandlerT]]: ...
|
|
116
136
|
|
|
117
137
|
@typing.overload
|
|
118
138
|
def handle(
|
|
119
139
|
self,
|
|
120
140
|
*rules: "ABCRule",
|
|
121
|
-
dataclass: type[
|
|
141
|
+
dataclass: type[T],
|
|
122
142
|
error_handler: ErrorHandlerT,
|
|
123
|
-
|
|
124
|
-
) -> typing.Callable[[Func[P, R]], FuncHandler[
|
|
143
|
+
final: bool = True,
|
|
144
|
+
) -> typing.Callable[[Func[P, R]], FuncHandler[T, Func[P, R], ErrorHandlerT]]: ...
|
|
125
145
|
|
|
126
146
|
@typing.overload
|
|
127
147
|
def handle(
|
|
128
148
|
self,
|
|
129
149
|
*rules: "ABCRule",
|
|
130
|
-
dataclass: type[
|
|
150
|
+
dataclass: type[T],
|
|
131
151
|
update_type: UpdateType,
|
|
132
152
|
error_handler: ErrorHandlerT,
|
|
133
|
-
|
|
134
|
-
) -> typing.Callable[[Func[P, R]], FuncHandler[
|
|
153
|
+
final: bool = True,
|
|
154
|
+
) -> typing.Callable[[Func[P, R]], FuncHandler[T, Func[P, R], ErrorHandlerT]]: ...
|
|
135
155
|
|
|
136
156
|
@typing.overload
|
|
137
157
|
def handle(
|
|
@@ -140,8 +160,8 @@ class Dispatch(
|
|
|
140
160
|
update_type: UpdateType | None = None,
|
|
141
161
|
dataclass: type[T] = DEFAULT_DATACLASS,
|
|
142
162
|
error_handler: typing.Literal[None] = None,
|
|
143
|
-
|
|
144
|
-
) -> typing.Callable[[Func[P, R]], FuncHandler[
|
|
163
|
+
final: bool = True,
|
|
164
|
+
) -> typing.Callable[[Func[P, R]], FuncHandler[T, Func[P, R], ErrorHandler[T]]]: ...
|
|
145
165
|
|
|
146
166
|
def handle(
|
|
147
167
|
self,
|
|
@@ -149,13 +169,13 @@ class Dispatch(
|
|
|
149
169
|
update_type: UpdateType | None = None,
|
|
150
170
|
dataclass: type[typing.Any] = DEFAULT_DATACLASS,
|
|
151
171
|
error_handler: ErrorHandlerT | None = None,
|
|
152
|
-
|
|
172
|
+
final: bool = True,
|
|
153
173
|
) -> typing.Callable[..., typing.Any]:
|
|
154
174
|
def wrapper(func):
|
|
155
175
|
handler = FuncHandler(
|
|
156
176
|
func,
|
|
157
177
|
list(rules),
|
|
158
|
-
|
|
178
|
+
final=final,
|
|
159
179
|
dataclass=dataclass,
|
|
160
180
|
error_handler=error_handler or ErrorHandler(),
|
|
161
181
|
update_type=update_type,
|
|
@@ -165,12 +185,27 @@ class Dispatch(
|
|
|
165
185
|
|
|
166
186
|
return wrapper
|
|
167
187
|
|
|
168
|
-
async def feed(self, event: Update, api: API) -> bool:
|
|
188
|
+
async def feed(self, event: Update, api: API[HTTPClient]) -> bool:
|
|
169
189
|
logger.debug(
|
|
170
190
|
"Processing update (update_id={}, update_type={!r})",
|
|
171
191
|
event.update_id,
|
|
172
192
|
event.update_type.name,
|
|
173
193
|
)
|
|
194
|
+
context = Context(raw_update=event)
|
|
195
|
+
|
|
196
|
+
if (
|
|
197
|
+
await run_middleware(
|
|
198
|
+
self.global_middleware.pre,
|
|
199
|
+
api,
|
|
200
|
+
event, # type: ignore
|
|
201
|
+
raw_event=event,
|
|
202
|
+
ctx=context,
|
|
203
|
+
adapter=self.global_middleware.adapter,
|
|
204
|
+
)
|
|
205
|
+
is False
|
|
206
|
+
):
|
|
207
|
+
return False
|
|
208
|
+
|
|
174
209
|
for view in self.get_views().values():
|
|
175
210
|
if await view.check(event):
|
|
176
211
|
logger.debug(
|
|
@@ -179,23 +214,40 @@ class Dispatch(
|
|
|
179
214
|
event.update_type.name,
|
|
180
215
|
view,
|
|
181
216
|
)
|
|
182
|
-
if await view.process(event, api):
|
|
217
|
+
if await view.process(event, api, context):
|
|
183
218
|
return True
|
|
219
|
+
|
|
220
|
+
await run_middleware(
|
|
221
|
+
self.global_middleware.post,
|
|
222
|
+
api,
|
|
223
|
+
event,
|
|
224
|
+
raw_event=event,
|
|
225
|
+
ctx=context,
|
|
226
|
+
adapter=self.global_middleware.adapter,
|
|
227
|
+
)
|
|
228
|
+
|
|
184
229
|
return False
|
|
185
230
|
|
|
186
231
|
def load(self, external: typing.Self) -> None:
|
|
187
|
-
|
|
232
|
+
views_external = external.get_views()
|
|
233
|
+
|
|
188
234
|
for name, view in self.get_views().items():
|
|
189
|
-
assert name in
|
|
190
|
-
view.load(
|
|
235
|
+
assert name in views_external, f"View {name!r} is undefined in external dispatch."
|
|
236
|
+
view.load(views_external[name])
|
|
191
237
|
setattr(external, name, view)
|
|
192
238
|
|
|
239
|
+
self.global_middleware.filters.difference_update(external.global_middleware.filters)
|
|
240
|
+
|
|
193
241
|
def get_view(self, of_type: type[T]) -> Option[T]:
|
|
194
242
|
for view in self.get_views().values():
|
|
195
243
|
if isinstance(view, of_type):
|
|
196
244
|
return Some(view)
|
|
197
245
|
return Nothing()
|
|
198
246
|
|
|
247
|
+
def get_views(self) -> dict[str, ABCView]:
|
|
248
|
+
"""Get all views."""
|
|
249
|
+
return {name: view for name, view in self.__dict__.items() if isinstance(view, ABCView)}
|
|
250
|
+
|
|
199
251
|
__call__ = handle
|
|
200
252
|
|
|
201
253
|
|
|
@@ -3,21 +3,20 @@ from abc import ABC, abstractmethod
|
|
|
3
3
|
|
|
4
4
|
from telegrinder.api import API
|
|
5
5
|
from telegrinder.bot.dispatch.context import Context
|
|
6
|
-
from telegrinder.
|
|
6
|
+
from telegrinder.tools.adapter.abc import ABCAdapter
|
|
7
7
|
from telegrinder.types.objects import Update
|
|
8
8
|
|
|
9
|
-
T = typing.TypeVar("T", bound=Model)
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
class ABCHandler[Event](ABC):
|
|
11
|
+
final: bool
|
|
12
|
+
adapter: ABCAdapter[Update, Event] | None = None
|
|
14
13
|
|
|
15
14
|
@abstractmethod
|
|
16
15
|
async def check(self, api: API, event: Update, ctx: Context | None = None) -> bool:
|
|
17
16
|
pass
|
|
18
17
|
|
|
19
18
|
@abstractmethod
|
|
20
|
-
async def run(self, api: API, event:
|
|
19
|
+
async def run(self, api: API, event: Event, ctx: Context) -> typing.Any:
|
|
21
20
|
pass
|
|
22
21
|
|
|
23
22
|
|
|
@@ -15,7 +15,7 @@ class AudioReplyHandler(BaseReplyHandler):
|
|
|
15
15
|
*rules: ABCRule,
|
|
16
16
|
caption: str | None = None,
|
|
17
17
|
parse_mode: str | None = None,
|
|
18
|
-
|
|
18
|
+
final: bool = True,
|
|
19
19
|
as_reply: bool = False,
|
|
20
20
|
preset_context: Context | None = None,
|
|
21
21
|
**default_params: typing.Any,
|
|
@@ -25,7 +25,7 @@ class AudioReplyHandler(BaseReplyHandler):
|
|
|
25
25
|
self.caption = caption
|
|
26
26
|
super().__init__(
|
|
27
27
|
*rules,
|
|
28
|
-
|
|
28
|
+
final=final,
|
|
29
29
|
as_reply=as_reply,
|
|
30
30
|
preset_context=preset_context,
|
|
31
31
|
**default_params,
|
|
@@ -13,7 +13,7 @@ from telegrinder.bot.rules.abc import ABCRule
|
|
|
13
13
|
from telegrinder.modules import logger
|
|
14
14
|
from telegrinder.types.objects import Update
|
|
15
15
|
|
|
16
|
-
APIMethod
|
|
16
|
+
type APIMethod = typing.Callable[
|
|
17
17
|
typing.Concatenate[MessageCute, ...], typing.Awaitable[Result[typing.Any, APIError]]
|
|
18
18
|
]
|
|
19
19
|
|
|
@@ -22,14 +22,14 @@ class BaseReplyHandler(ABCHandler[MessageCute], abc.ABC):
|
|
|
22
22
|
def __init__(
|
|
23
23
|
self,
|
|
24
24
|
*rules: ABCRule,
|
|
25
|
-
|
|
25
|
+
final: bool = True,
|
|
26
26
|
as_reply: bool = False,
|
|
27
27
|
preset_context: Context | None = None,
|
|
28
28
|
**default_params: typing.Any,
|
|
29
29
|
) -> None:
|
|
30
30
|
self.rules = list(rules)
|
|
31
31
|
self.as_reply = as_reply
|
|
32
|
-
self.
|
|
32
|
+
self.final = final
|
|
33
33
|
self.default_params = default_params
|
|
34
34
|
self.preset_context = preset_context or Context()
|
|
35
35
|
|
|
@@ -15,7 +15,7 @@ class DocumentReplyHandler(BaseReplyHandler):
|
|
|
15
15
|
*rules: ABCRule,
|
|
16
16
|
caption: str | None = None,
|
|
17
17
|
parse_mode: str | None = None,
|
|
18
|
-
|
|
18
|
+
final: bool = True,
|
|
19
19
|
as_reply: bool = False,
|
|
20
20
|
preset_context: Context | None = None,
|
|
21
21
|
**default_params: typing.Any,
|
|
@@ -25,7 +25,7 @@ class DocumentReplyHandler(BaseReplyHandler):
|
|
|
25
25
|
self.caption = caption
|
|
26
26
|
super().__init__(
|
|
27
27
|
*rules,
|
|
28
|
-
|
|
28
|
+
final=final,
|
|
29
29
|
as_reply=as_reply,
|
|
30
30
|
preset_context=preset_context,
|
|
31
31
|
**default_params,
|