telegrinder 0.1.dev170__py3-none-any.whl → 0.2.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 +2 -2
- telegrinder/api/__init__.py +1 -2
- telegrinder/api/api.py +15 -6
- telegrinder/api/error.py +2 -1
- telegrinder/api/token.py +36 -0
- telegrinder/bot/__init__.py +12 -6
- telegrinder/bot/bot.py +18 -6
- telegrinder/bot/cute_types/__init__.py +7 -7
- telegrinder/bot/cute_types/base.py +122 -20
- telegrinder/bot/cute_types/callback_query.py +10 -6
- telegrinder/bot/cute_types/chat_join_request.py +4 -5
- telegrinder/bot/cute_types/chat_member_updated.py +4 -6
- telegrinder/bot/cute_types/inline_query.py +3 -4
- telegrinder/bot/cute_types/message.py +32 -21
- telegrinder/bot/cute_types/update.py +51 -4
- telegrinder/bot/cute_types/utils.py +3 -466
- telegrinder/bot/dispatch/__init__.py +10 -11
- telegrinder/bot/dispatch/abc.py +8 -5
- telegrinder/bot/dispatch/context.py +17 -8
- telegrinder/bot/dispatch/dispatch.py +71 -48
- telegrinder/bot/dispatch/handler/__init__.py +3 -3
- telegrinder/bot/dispatch/handler/abc.py +4 -4
- telegrinder/bot/dispatch/handler/func.py +46 -22
- telegrinder/bot/dispatch/handler/message_reply.py +6 -7
- telegrinder/bot/dispatch/middleware/__init__.py +1 -1
- telegrinder/bot/dispatch/middleware/abc.py +2 -2
- telegrinder/bot/dispatch/process.py +38 -19
- telegrinder/bot/dispatch/return_manager/__init__.py +4 -4
- telegrinder/bot/dispatch/return_manager/abc.py +3 -3
- telegrinder/bot/dispatch/return_manager/callback_query.py +1 -2
- telegrinder/bot/dispatch/return_manager/inline_query.py +1 -2
- telegrinder/bot/dispatch/return_manager/message.py +1 -2
- telegrinder/bot/dispatch/view/__init__.py +8 -8
- telegrinder/bot/dispatch/view/abc.py +18 -16
- telegrinder/bot/dispatch/view/box.py +75 -64
- telegrinder/bot/dispatch/view/callback_query.py +1 -2
- telegrinder/bot/dispatch/view/chat_join_request.py +1 -2
- telegrinder/bot/dispatch/view/chat_member.py +16 -2
- telegrinder/bot/dispatch/view/inline_query.py +1 -2
- telegrinder/bot/dispatch/view/message.py +12 -5
- telegrinder/bot/dispatch/view/raw.py +9 -8
- telegrinder/bot/dispatch/waiter_machine/__init__.py +3 -3
- telegrinder/bot/dispatch/waiter_machine/machine.py +12 -8
- telegrinder/bot/dispatch/waiter_machine/middleware.py +1 -1
- telegrinder/bot/dispatch/waiter_machine/short_state.py +4 -3
- telegrinder/bot/polling/abc.py +1 -1
- telegrinder/bot/polling/polling.py +6 -6
- telegrinder/bot/rules/__init__.py +20 -20
- telegrinder/bot/rules/abc.py +57 -43
- telegrinder/bot/rules/adapter/__init__.py +5 -5
- telegrinder/bot/rules/adapter/abc.py +6 -3
- telegrinder/bot/rules/adapter/errors.py +2 -1
- telegrinder/bot/rules/adapter/event.py +28 -13
- telegrinder/bot/rules/adapter/node.py +28 -22
- telegrinder/bot/rules/adapter/raw_update.py +13 -5
- telegrinder/bot/rules/callback_data.py +4 -4
- telegrinder/bot/rules/chat_join.py +4 -4
- telegrinder/bot/rules/command.py +5 -7
- telegrinder/bot/rules/func.py +2 -2
- telegrinder/bot/rules/fuzzy.py +1 -1
- telegrinder/bot/rules/inline.py +3 -3
- telegrinder/bot/rules/integer.py +1 -2
- telegrinder/bot/rules/markup.py +5 -3
- telegrinder/bot/rules/message_entities.py +2 -2
- telegrinder/bot/rules/node.py +2 -2
- telegrinder/bot/rules/regex.py +1 -1
- telegrinder/bot/rules/rule_enum.py +1 -1
- telegrinder/bot/rules/text.py +1 -2
- telegrinder/bot/rules/update.py +1 -2
- telegrinder/bot/scenario/abc.py +2 -2
- telegrinder/bot/scenario/checkbox.py +3 -4
- telegrinder/bot/scenario/choice.py +1 -2
- telegrinder/model.py +89 -45
- telegrinder/modules.py +3 -3
- telegrinder/msgspec_utils.py +85 -57
- telegrinder/node/__init__.py +17 -10
- telegrinder/node/attachment.py +19 -16
- telegrinder/node/base.py +46 -22
- telegrinder/node/callback_query.py +5 -9
- telegrinder/node/command.py +6 -2
- telegrinder/node/composer.py +102 -77
- telegrinder/node/container.py +3 -3
- telegrinder/node/event.py +68 -0
- telegrinder/node/me.py +3 -0
- telegrinder/node/message.py +6 -10
- telegrinder/node/polymorphic.py +15 -10
- telegrinder/node/rule.py +20 -6
- telegrinder/node/scope.py +9 -1
- telegrinder/node/source.py +21 -11
- telegrinder/node/text.py +4 -4
- telegrinder/node/update.py +7 -4
- telegrinder/py.typed +0 -0
- telegrinder/rules.py +59 -0
- telegrinder/tools/__init__.py +2 -2
- telegrinder/tools/buttons.py +5 -10
- telegrinder/tools/error_handler/abc.py +2 -2
- telegrinder/tools/error_handler/error.py +2 -0
- telegrinder/tools/error_handler/error_handler.py +6 -6
- telegrinder/tools/formatting/spec_html_formats.py +10 -10
- telegrinder/tools/global_context/__init__.py +2 -2
- telegrinder/tools/global_context/global_context.py +3 -3
- telegrinder/tools/global_context/telegrinder_ctx.py +4 -4
- telegrinder/tools/keyboard.py +3 -3
- telegrinder/tools/loop_wrapper/loop_wrapper.py +47 -13
- telegrinder/tools/magic.py +96 -18
- telegrinder/types/__init__.py +1 -0
- telegrinder/types/enums.py +2 -0
- telegrinder/types/methods.py +91 -15
- telegrinder/types/objects.py +49 -24
- telegrinder/verification_utils.py +1 -3
- {telegrinder-0.1.dev170.dist-info → telegrinder-0.2.0.dist-info}/METADATA +2 -2
- telegrinder-0.2.0.dist-info/RECORD +145 -0
- telegrinder/api/abc.py +0 -73
- telegrinder-0.1.dev170.dist-info/RECORD +0 -143
- {telegrinder-0.1.dev170.dist-info → telegrinder-0.2.0.dist-info}/LICENSE +0 -0
- {telegrinder-0.1.dev170.dist-info → telegrinder-0.2.0.dist-info}/WHEEL +0 -0
telegrinder/rules.py
CHANGED
|
@@ -1 +1,60 @@
|
|
|
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
|
+
"IsInteger",
|
|
30
|
+
"IntegerInRange",
|
|
31
|
+
"InviteLinkByCreator",
|
|
32
|
+
"InviteLinkName",
|
|
33
|
+
"IsBot",
|
|
34
|
+
"IsChat",
|
|
35
|
+
"IsChatId",
|
|
36
|
+
"IsDice",
|
|
37
|
+
"IsDiceEmoji",
|
|
38
|
+
"IsForum",
|
|
39
|
+
"IsForward",
|
|
40
|
+
"IsForwardType",
|
|
41
|
+
"IsGroup",
|
|
42
|
+
"IsLanguageCode",
|
|
43
|
+
"IsPremium",
|
|
44
|
+
"IsPrivate",
|
|
45
|
+
"IsReply",
|
|
46
|
+
"IsSuperGroup",
|
|
47
|
+
"IsUpdateType",
|
|
48
|
+
"IsUser",
|
|
49
|
+
"IsUserId",
|
|
50
|
+
"Markup",
|
|
51
|
+
"MessageEntities",
|
|
52
|
+
"MessageRule",
|
|
53
|
+
"NotRule",
|
|
54
|
+
"OrRule",
|
|
55
|
+
"Regex",
|
|
56
|
+
"RuleEnum",
|
|
57
|
+
"StartCommand",
|
|
58
|
+
"Text",
|
|
59
|
+
"NodeRule",
|
|
60
|
+
)
|
telegrinder/tools/__init__.py
CHANGED
|
@@ -43,7 +43,7 @@ from .global_context import (
|
|
|
43
43
|
CtxVar,
|
|
44
44
|
GlobalContext,
|
|
45
45
|
GlobalCtxVar,
|
|
46
|
-
|
|
46
|
+
TelegrinderContext,
|
|
47
47
|
ctx_var,
|
|
48
48
|
)
|
|
49
49
|
from .i18n import (
|
|
@@ -110,7 +110,7 @@ __all__ = (
|
|
|
110
110
|
"SpecialFormat",
|
|
111
111
|
"StartBotLink",
|
|
112
112
|
"StartGroupLink",
|
|
113
|
-
"
|
|
113
|
+
"TelegrinderContext",
|
|
114
114
|
"TgEmoji",
|
|
115
115
|
"escape",
|
|
116
116
|
"get_channel_boost_link",
|
telegrinder/tools/buttons.py
CHANGED
|
@@ -3,25 +3,20 @@ import typing
|
|
|
3
3
|
|
|
4
4
|
import msgspec
|
|
5
5
|
|
|
6
|
-
from telegrinder.
|
|
7
|
-
from telegrinder.types import (
|
|
6
|
+
from telegrinder.msgspec_utils import DataclassInstance, encoder
|
|
7
|
+
from telegrinder.types.objects import (
|
|
8
8
|
CallbackGame,
|
|
9
9
|
KeyboardButtonPollType,
|
|
10
10
|
KeyboardButtonRequestChat,
|
|
11
11
|
KeyboardButtonRequestUsers,
|
|
12
|
+
LoginUrl,
|
|
12
13
|
SwitchInlineQueryChosenChat,
|
|
13
14
|
WebAppInfo,
|
|
14
15
|
)
|
|
15
|
-
from telegrinder.types.objects import LoginUrl
|
|
16
16
|
|
|
17
17
|
ButtonT = typing.TypeVar("ButtonT", bound="BaseButton")
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
@typing.runtime_checkable
|
|
21
|
-
class DataclassInstance(typing.Protocol):
|
|
22
|
-
__dataclass_fields__: typing.ClassVar[dict[str, dataclasses.Field[typing.Any]]]
|
|
23
|
-
|
|
24
|
-
|
|
25
20
|
@dataclasses.dataclass
|
|
26
21
|
class BaseButton:
|
|
27
22
|
def get_data(self) -> dict[str, typing.Any]:
|
|
@@ -44,7 +39,7 @@ class RowButtons(typing.Generic[ButtonT]):
|
|
|
44
39
|
return [b.get_data() for b in self.buttons]
|
|
45
40
|
|
|
46
41
|
|
|
47
|
-
@dataclasses.dataclass
|
|
42
|
+
@dataclasses.dataclass(slots=True)
|
|
48
43
|
class Button(BaseButton):
|
|
49
44
|
text: str
|
|
50
45
|
request_contact: bool = dataclasses.field(default=False, kw_only=True)
|
|
@@ -61,7 +56,7 @@ class Button(BaseButton):
|
|
|
61
56
|
web_app: dict[str, typing.Any] | WebAppInfo | None = dataclasses.field(default=None, kw_only=True)
|
|
62
57
|
|
|
63
58
|
|
|
64
|
-
@dataclasses.dataclass
|
|
59
|
+
@dataclasses.dataclass(slots=True)
|
|
65
60
|
class InlineButton(BaseButton):
|
|
66
61
|
text: str
|
|
67
62
|
url: str | None = dataclasses.field(default=None, kw_only=True)
|
|
@@ -3,7 +3,7 @@ from abc import ABC, abstractmethod
|
|
|
3
3
|
|
|
4
4
|
from fntypes.result import Result
|
|
5
5
|
|
|
6
|
-
from telegrinder.api import
|
|
6
|
+
from telegrinder.api import API
|
|
7
7
|
from telegrinder.bot.dispatch.context import Context
|
|
8
8
|
|
|
9
9
|
EventT = typing.TypeVar("EventT")
|
|
@@ -24,7 +24,7 @@ class ABCErrorHandler(ABC, typing.Generic[EventT]):
|
|
|
24
24
|
self,
|
|
25
25
|
handler: Handler[EventT],
|
|
26
26
|
event: EventT,
|
|
27
|
-
api:
|
|
27
|
+
api: API,
|
|
28
28
|
ctx: Context,
|
|
29
29
|
) -> Result[typing.Any, typing.Any]:
|
|
30
30
|
"""Run error handler."""
|
|
@@ -3,7 +3,7 @@ import typing
|
|
|
3
3
|
|
|
4
4
|
from fntypes.result import Error, Ok, Result
|
|
5
5
|
|
|
6
|
-
from telegrinder.api import
|
|
6
|
+
from telegrinder.api import API
|
|
7
7
|
from telegrinder.bot.dispatch.context import Context
|
|
8
8
|
from telegrinder.modules import logger
|
|
9
9
|
from telegrinder.tools.magic import magic_bundle
|
|
@@ -16,7 +16,7 @@ ExceptionT = typing.TypeVar("ExceptionT", bound=BaseException, contravariant=Tru
|
|
|
16
16
|
FuncCatcher = typing.Callable[typing.Concatenate[ExceptionT, ...], typing.Awaitable[typing.Any]]
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
@dataclasses.dataclass(frozen=True, repr=False)
|
|
19
|
+
@dataclasses.dataclass(frozen=True, repr=False, slots=True)
|
|
20
20
|
class Catcher(typing.Generic[EventT]):
|
|
21
21
|
func: FuncCatcher[BaseException]
|
|
22
22
|
exceptions: list[type[BaseException] | BaseException] = dataclasses.field(
|
|
@@ -39,7 +39,7 @@ class Catcher(typing.Generic[EventT]):
|
|
|
39
39
|
self,
|
|
40
40
|
handler: Handler[EventT],
|
|
41
41
|
event: EventT,
|
|
42
|
-
api:
|
|
42
|
+
api: API,
|
|
43
43
|
ctx: Context,
|
|
44
44
|
) -> Result[typing.Any, BaseException]:
|
|
45
45
|
try:
|
|
@@ -49,7 +49,7 @@ class Catcher(typing.Generic[EventT]):
|
|
|
49
49
|
|
|
50
50
|
async def process_exception(
|
|
51
51
|
self,
|
|
52
|
-
api:
|
|
52
|
+
api: API,
|
|
53
53
|
event: EventT,
|
|
54
54
|
ctx: Context,
|
|
55
55
|
exception: BaseException,
|
|
@@ -125,7 +125,7 @@ class ErrorHandler(ABCErrorHandler[EventT]):
|
|
|
125
125
|
self,
|
|
126
126
|
handler: Handler[EventT],
|
|
127
127
|
event: EventT,
|
|
128
|
-
api:
|
|
128
|
+
api: API,
|
|
129
129
|
ctx: Context,
|
|
130
130
|
) -> Result[typing.Any, BaseException]:
|
|
131
131
|
assert self.catcher is not None
|
|
@@ -159,7 +159,7 @@ class ErrorHandler(ABCErrorHandler[EventT]):
|
|
|
159
159
|
self,
|
|
160
160
|
handler: Handler[EventT],
|
|
161
161
|
event: EventT,
|
|
162
|
-
api:
|
|
162
|
+
api: API,
|
|
163
163
|
ctx: Context,
|
|
164
164
|
) -> Result[typing.Any, BaseException]:
|
|
165
165
|
if not self.catcher:
|
|
@@ -24,7 +24,7 @@ def is_spec_format(obj: typing.Any) -> typing.TypeGuard[SpecialFormat]:
|
|
|
24
24
|
)
|
|
25
25
|
|
|
26
26
|
|
|
27
|
-
@dataclasses.dataclass(repr=False)
|
|
27
|
+
@dataclasses.dataclass(repr=False, slots=True)
|
|
28
28
|
class BaseSpecFormat:
|
|
29
29
|
__formatter_name__: typing.ClassVar[str] = dataclasses.field(init=False, repr=False)
|
|
30
30
|
|
|
@@ -32,7 +32,7 @@ class BaseSpecFormat:
|
|
|
32
32
|
return f"<Special formatter {self.__class__.__name__!r} -> {self.__formatter_name__!r}>"
|
|
33
33
|
|
|
34
34
|
|
|
35
|
-
@dataclasses.dataclass(repr=False)
|
|
35
|
+
@dataclasses.dataclass(repr=False, slots=True)
|
|
36
36
|
class ChannelBoostLink(BaseSpecFormat):
|
|
37
37
|
__formatter_name__ = "channel_boost_link"
|
|
38
38
|
|
|
@@ -40,7 +40,7 @@ class ChannelBoostLink(BaseSpecFormat):
|
|
|
40
40
|
string: str | None = None
|
|
41
41
|
|
|
42
42
|
|
|
43
|
-
@dataclasses.dataclass(repr=False)
|
|
43
|
+
@dataclasses.dataclass(repr=False, slots=True)
|
|
44
44
|
class InviteChatLink(BaseSpecFormat):
|
|
45
45
|
__formatter_name__ = "invite_chat_link"
|
|
46
46
|
|
|
@@ -48,7 +48,7 @@ class InviteChatLink(BaseSpecFormat):
|
|
|
48
48
|
string: str | None = None
|
|
49
49
|
|
|
50
50
|
|
|
51
|
-
@dataclasses.dataclass(repr=False)
|
|
51
|
+
@dataclasses.dataclass(repr=False, slots=True)
|
|
52
52
|
class Mention(BaseSpecFormat):
|
|
53
53
|
__formatter_name__ = "mention"
|
|
54
54
|
|
|
@@ -56,7 +56,7 @@ class Mention(BaseSpecFormat):
|
|
|
56
56
|
user_id: int
|
|
57
57
|
|
|
58
58
|
|
|
59
|
-
@dataclasses.dataclass(repr=False)
|
|
59
|
+
@dataclasses.dataclass(repr=False, slots=True)
|
|
60
60
|
class Link(BaseSpecFormat):
|
|
61
61
|
__formatter_name__ = "link"
|
|
62
62
|
|
|
@@ -64,7 +64,7 @@ class Link(BaseSpecFormat):
|
|
|
64
64
|
string: str | None = None
|
|
65
65
|
|
|
66
66
|
|
|
67
|
-
@dataclasses.dataclass(repr=False)
|
|
67
|
+
@dataclasses.dataclass(repr=False, slots=True)
|
|
68
68
|
class PreCode(BaseSpecFormat):
|
|
69
69
|
__formatter_name__ = "pre_code"
|
|
70
70
|
|
|
@@ -72,7 +72,7 @@ class PreCode(BaseSpecFormat):
|
|
|
72
72
|
lang: str | ProgrammingLanguage | None = None
|
|
73
73
|
|
|
74
74
|
|
|
75
|
-
@dataclasses.dataclass(repr=False)
|
|
75
|
+
@dataclasses.dataclass(repr=False, slots=True)
|
|
76
76
|
class TgEmoji(BaseSpecFormat):
|
|
77
77
|
__formatter_name__ = "tg_emoji"
|
|
78
78
|
|
|
@@ -80,7 +80,7 @@ class TgEmoji(BaseSpecFormat):
|
|
|
80
80
|
emoji_id: int
|
|
81
81
|
|
|
82
82
|
|
|
83
|
-
@dataclasses.dataclass(repr=False)
|
|
83
|
+
@dataclasses.dataclass(repr=False, slots=True)
|
|
84
84
|
class StartBotLink(BaseSpecFormat):
|
|
85
85
|
__formatter_name__ = "start_bot_link"
|
|
86
86
|
|
|
@@ -89,7 +89,7 @@ class StartBotLink(BaseSpecFormat):
|
|
|
89
89
|
string: str | None
|
|
90
90
|
|
|
91
91
|
|
|
92
|
-
@dataclasses.dataclass(repr=False)
|
|
92
|
+
@dataclasses.dataclass(repr=False, slots=True)
|
|
93
93
|
class StartGroupLink(BaseSpecFormat):
|
|
94
94
|
__formatter_name__ = "start_group_link"
|
|
95
95
|
|
|
@@ -98,7 +98,7 @@ class StartGroupLink(BaseSpecFormat):
|
|
|
98
98
|
string: str | None = None
|
|
99
99
|
|
|
100
100
|
|
|
101
|
-
@dataclasses.dataclass(repr=False)
|
|
101
|
+
@dataclasses.dataclass(repr=False, slots=True)
|
|
102
102
|
class ResolveDomain(BaseSpecFormat):
|
|
103
103
|
__formatter_name__ = "resolve_domain"
|
|
104
104
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
from .abc import ABCGlobalContext, CtxVar, GlobalCtxVar
|
|
2
2
|
from .global_context import GlobalContext, ctx_var
|
|
3
|
-
from .telegrinder_ctx import
|
|
3
|
+
from .telegrinder_ctx import TelegrinderContext
|
|
4
4
|
|
|
5
5
|
__all__ = (
|
|
6
6
|
"ABCGlobalContext",
|
|
7
7
|
"CtxVar",
|
|
8
8
|
"GlobalContext",
|
|
9
9
|
"GlobalCtxVar",
|
|
10
|
-
"
|
|
10
|
+
"TelegrinderContext",
|
|
11
11
|
"ctx_var",
|
|
12
12
|
)
|
|
@@ -81,7 +81,7 @@ def ctx_var(value: T, *, const: bool = False) -> T:
|
|
|
81
81
|
return typing.cast(T, CtxVar(value, const=const))
|
|
82
82
|
|
|
83
83
|
|
|
84
|
-
@dataclasses.dataclass(frozen=True, eq=False)
|
|
84
|
+
@dataclasses.dataclass(frozen=True, eq=False, slots=True)
|
|
85
85
|
class RootAttr:
|
|
86
86
|
name: str
|
|
87
87
|
can_be_read: bool = dataclasses.field(default=True, kw_only=True)
|
|
@@ -91,7 +91,7 @@ class RootAttr:
|
|
|
91
91
|
return self.name == __value
|
|
92
92
|
|
|
93
93
|
|
|
94
|
-
@dataclasses.dataclass(repr=False, frozen=True)
|
|
94
|
+
@dataclasses.dataclass(repr=False, frozen=True, slots=True)
|
|
95
95
|
class Storage:
|
|
96
96
|
_storage: dict[str, "GlobalContext"] = dataclasses.field(
|
|
97
97
|
default_factory=lambda: {},
|
|
@@ -334,7 +334,7 @@ class GlobalContext(ABCGlobalContext, typing.Generic[CtxValueT], dict[str, Globa
|
|
|
334
334
|
def get(self, var_name, var_value_type=object): # type: ignore
|
|
335
335
|
"""Get context variable by name."""
|
|
336
336
|
|
|
337
|
-
var_value_type = typing.Any if var_value_type is object else
|
|
337
|
+
var_value_type = typing.Any if var_value_type is object else var_value_type
|
|
338
338
|
generic_types = typing.get_args(get_orig_class(self))
|
|
339
339
|
if generic_types and var_value_type is object:
|
|
340
340
|
var_value_type = generic_types[0]
|
|
@@ -5,14 +5,14 @@ import vbml
|
|
|
5
5
|
from telegrinder.tools.global_context import GlobalContext, ctx_var
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
class
|
|
8
|
+
class TelegrinderContext(GlobalContext):
|
|
9
9
|
"""Basic type-hinted telegrinder context with context name `"telegrinder"`.
|
|
10
10
|
|
|
11
11
|
You can use this class or GlobalContext:
|
|
12
12
|
```
|
|
13
|
-
from telegrinder.tools.global_context import GlobalContext,
|
|
13
|
+
from telegrinder.tools.global_context import GlobalContext, TelegrinderContext
|
|
14
14
|
|
|
15
|
-
ctx1 =
|
|
15
|
+
ctx1 = TelegrinderContext()
|
|
16
16
|
ctx2 = GlobalContext("telegrinder") # same, but without the type-hints
|
|
17
17
|
assert ctx1 == ctx2 # ok
|
|
18
18
|
```"""
|
|
@@ -22,4 +22,4 @@ class TelegrinderCtx(GlobalContext):
|
|
|
22
22
|
vbml_patcher: typing.ClassVar[vbml.Patcher] = ctx_var(vbml.Patcher(), const=True)
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
__all__ = ("
|
|
25
|
+
__all__ = ("TelegrinderContext",)
|
telegrinder/tools/keyboard.py
CHANGED
|
@@ -17,7 +17,7 @@ DictStrAny: typing.TypeAlias = dict[str, typing.Any]
|
|
|
17
17
|
AnyMarkup: typing.TypeAlias = InlineKeyboardMarkup | ReplyKeyboardMarkup
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
@dataclasses.dataclass(kw_only=True)
|
|
20
|
+
@dataclasses.dataclass(kw_only=True, slots=True)
|
|
21
21
|
class KeyboardModel:
|
|
22
22
|
resize_keyboard: bool
|
|
23
23
|
one_time_keyboard: bool
|
|
@@ -77,7 +77,7 @@ class ABCMarkup(ABC, typing.Generic[ButtonT]):
|
|
|
77
77
|
return self
|
|
78
78
|
|
|
79
79
|
|
|
80
|
-
@dataclasses.dataclass(kw_only=True)
|
|
80
|
+
@dataclasses.dataclass(kw_only=True, slots=True)
|
|
81
81
|
class Keyboard(ABCMarkup[Button], KeyboardModel):
|
|
82
82
|
BUTTON = Button
|
|
83
83
|
|
|
@@ -94,7 +94,7 @@ class Keyboard(ABCMarkup[Button], KeyboardModel):
|
|
|
94
94
|
self.keyboard = [row for row in self.keyboard if row]
|
|
95
95
|
return {
|
|
96
96
|
k: v.unwrap() if v and isinstance(v, Some) else v
|
|
97
|
-
for k, v in self.
|
|
97
|
+
for k, v in dataclasses.asdict(self).items()
|
|
98
98
|
if type(v) not in (NoneType, Nothing)
|
|
99
99
|
}
|
|
100
100
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import contextlib
|
|
3
3
|
import dataclasses
|
|
4
|
+
import datetime
|
|
4
5
|
import typing
|
|
5
6
|
|
|
6
7
|
from telegrinder.modules import logger
|
|
@@ -32,7 +33,7 @@ def to_coroutine_task(task: Task) -> CoroutineTask[typing.Any]:
|
|
|
32
33
|
return task
|
|
33
34
|
|
|
34
35
|
|
|
35
|
-
@dataclasses.dataclass
|
|
36
|
+
@dataclasses.dataclass(slots=True)
|
|
36
37
|
class DelayedTask(typing.Generic[CoroFunc]):
|
|
37
38
|
handler: CoroFunc
|
|
38
39
|
seconds: float
|
|
@@ -60,23 +61,23 @@ class DelayedTask(typing.Generic[CoroFunc]):
|
|
|
60
61
|
self._cancelled = True
|
|
61
62
|
|
|
62
63
|
|
|
63
|
-
@dataclasses.dataclass(kw_only=True)
|
|
64
|
+
@dataclasses.dataclass(kw_only=True, slots=True, frozen=True)
|
|
64
65
|
class Lifespan:
|
|
65
66
|
startup_tasks: list[CoroutineTask[typing.Any]] = dataclasses.field(default_factory=lambda: [])
|
|
66
67
|
shutdown_tasks: list[CoroutineTask[typing.Any]] = dataclasses.field(default_factory=lambda: [])
|
|
67
68
|
|
|
68
|
-
def on_startup(self,
|
|
69
|
-
self.startup_tasks.append(to_coroutine_task(
|
|
70
|
-
return
|
|
69
|
+
def on_startup(self, task: Task, /) -> Task:
|
|
70
|
+
self.startup_tasks.append(to_coroutine_task(task))
|
|
71
|
+
return task
|
|
71
72
|
|
|
72
|
-
def on_shutdown(self,
|
|
73
|
-
self.shutdown_tasks.append(to_coroutine_task(
|
|
74
|
-
return
|
|
73
|
+
def on_shutdown(self, task: Task, /) -> Task:
|
|
74
|
+
self.shutdown_tasks.append(to_coroutine_task(task))
|
|
75
|
+
return task
|
|
75
76
|
|
|
76
|
-
def start(self, loop: asyncio.AbstractEventLoop) -> None:
|
|
77
|
+
def start(self, loop: asyncio.AbstractEventLoop, /) -> None:
|
|
77
78
|
run_tasks(self.startup_tasks, loop)
|
|
78
79
|
|
|
79
|
-
def shutdown(self, loop: asyncio.AbstractEventLoop) -> None:
|
|
80
|
+
def shutdown(self, loop: asyncio.AbstractEventLoop, /) -> None:
|
|
80
81
|
run_tasks(self.shutdown_tasks, loop)
|
|
81
82
|
|
|
82
83
|
|
|
@@ -86,10 +87,11 @@ class LoopWrapper(ABCLoopWrapper):
|
|
|
86
87
|
*,
|
|
87
88
|
tasks: list[CoroutineTask[typing.Any]] | None = None,
|
|
88
89
|
lifespan: Lifespan | None = None,
|
|
90
|
+
event_loop: asyncio.AbstractEventLoop | None = None,
|
|
89
91
|
) -> None:
|
|
90
92
|
self.tasks: list[CoroutineTask[typing.Any]] = tasks or []
|
|
91
93
|
self.lifespan = lifespan or Lifespan()
|
|
92
|
-
self._loop = asyncio.new_event_loop()
|
|
94
|
+
self._loop = event_loop or asyncio.new_event_loop()
|
|
93
95
|
|
|
94
96
|
def __repr__(self) -> str:
|
|
95
97
|
return "<{}: loop={!r} with tasks={!r}, lifespan={!r}>".format(
|
|
@@ -143,6 +145,10 @@ class LoopWrapper(ABCLoopWrapper):
|
|
|
143
145
|
with contextlib.suppress(asyncio.CancelledError):
|
|
144
146
|
self._loop.run_until_complete(task_to_cancel)
|
|
145
147
|
|
|
148
|
+
@typing.overload
|
|
149
|
+
def timer(self, *, seconds: datetime.timedelta) -> typing.Callable[..., typing.Any]: ...
|
|
150
|
+
|
|
151
|
+
@typing.overload
|
|
146
152
|
def timer(
|
|
147
153
|
self,
|
|
148
154
|
*,
|
|
@@ -150,7 +156,19 @@ class LoopWrapper(ABCLoopWrapper):
|
|
|
150
156
|
hours: int = 0,
|
|
151
157
|
minutes: int = 0,
|
|
152
158
|
seconds: float = 0,
|
|
153
|
-
):
|
|
159
|
+
) -> typing.Callable[..., typing.Any]: ...
|
|
160
|
+
|
|
161
|
+
def timer(
|
|
162
|
+
self,
|
|
163
|
+
*,
|
|
164
|
+
days: int = 0,
|
|
165
|
+
hours: int = 0,
|
|
166
|
+
minutes: int = 0,
|
|
167
|
+
seconds: float | datetime.timedelta = 0,
|
|
168
|
+
) -> typing.Callable[..., typing.Any]:
|
|
169
|
+
if isinstance(seconds, datetime.timedelta):
|
|
170
|
+
seconds = seconds.total_seconds()
|
|
171
|
+
|
|
154
172
|
seconds += minutes * 60
|
|
155
173
|
seconds += hours * 60 * 60
|
|
156
174
|
seconds += days * 24 * 60 * 60
|
|
@@ -161,6 +179,10 @@ class LoopWrapper(ABCLoopWrapper):
|
|
|
161
179
|
|
|
162
180
|
return decorator
|
|
163
181
|
|
|
182
|
+
@typing.overload
|
|
183
|
+
def interval(self, *, seconds: datetime.timedelta) -> typing.Callable[..., typing.Any]: ...
|
|
184
|
+
|
|
185
|
+
@typing.overload
|
|
164
186
|
def interval(
|
|
165
187
|
self,
|
|
166
188
|
*,
|
|
@@ -168,7 +190,19 @@ class LoopWrapper(ABCLoopWrapper):
|
|
|
168
190
|
hours: int = 0,
|
|
169
191
|
minutes: int = 0,
|
|
170
192
|
seconds: float = 0,
|
|
171
|
-
):
|
|
193
|
+
) -> typing.Callable[..., typing.Any]: ...
|
|
194
|
+
|
|
195
|
+
def interval(
|
|
196
|
+
self,
|
|
197
|
+
*,
|
|
198
|
+
days: int = 0,
|
|
199
|
+
hours: int = 0,
|
|
200
|
+
minutes: int = 0,
|
|
201
|
+
seconds: float | datetime.timedelta = 0,
|
|
202
|
+
) -> typing.Callable[..., typing.Any]:
|
|
203
|
+
if isinstance(seconds, datetime.timedelta):
|
|
204
|
+
seconds = seconds.total_seconds()
|
|
205
|
+
|
|
172
206
|
seconds += minutes * 60
|
|
173
207
|
seconds += hours * 60 * 60
|
|
174
208
|
seconds += days * 24 * 60 * 60
|
telegrinder/tools/magic.py
CHANGED
|
@@ -2,11 +2,17 @@ import enum
|
|
|
2
2
|
import inspect
|
|
3
3
|
import types
|
|
4
4
|
import typing
|
|
5
|
+
from functools import wraps
|
|
5
6
|
|
|
6
7
|
if typing.TYPE_CHECKING:
|
|
7
8
|
from telegrinder.bot.rules.abc import ABCRule
|
|
9
|
+
from telegrinder.node.base import Node
|
|
8
10
|
|
|
9
11
|
T = typing.TypeVar("T", bound=ABCRule)
|
|
12
|
+
F = typing.TypeVar(
|
|
13
|
+
"F",
|
|
14
|
+
bound=typing.Callable[typing.Concatenate[typing.Callable[..., typing.Any], ...], typing.Any],
|
|
15
|
+
)
|
|
10
16
|
|
|
11
17
|
Impl: typing.TypeAlias = type[classmethod]
|
|
12
18
|
FuncType: typing.TypeAlias = types.FunctionType | typing.Callable[..., typing.Any]
|
|
@@ -15,21 +21,36 @@ TRANSLATIONS_KEY: typing.Final[str] = "_translations"
|
|
|
15
21
|
IMPL_MARK: typing.Final[str] = "_is_impl"
|
|
16
22
|
|
|
17
23
|
|
|
24
|
+
def cache_magic_value(mark_key: str, /):
|
|
25
|
+
def inner(func: "F") -> "F":
|
|
26
|
+
|
|
27
|
+
@wraps(func)
|
|
28
|
+
def wrapper(*args: typing.Any, **kwargs: typing.Any) -> typing.Any:
|
|
29
|
+
if mark_key not in args[0].__dict__:
|
|
30
|
+
args[0].__dict__[mark_key] = func(*args, **kwargs)
|
|
31
|
+
return args[0].__dict__[mark_key]
|
|
32
|
+
|
|
33
|
+
return wrapper # type: ignore
|
|
34
|
+
return inner
|
|
35
|
+
|
|
36
|
+
|
|
18
37
|
def resolve_arg_names(func: FuncType, start_idx: int = 1) -> tuple[str, ...]:
|
|
19
38
|
return func.__code__.co_varnames[start_idx : func.__code__.co_argcount]
|
|
20
39
|
|
|
21
40
|
|
|
41
|
+
@cache_magic_value("__default_args__")
|
|
22
42
|
def get_default_args(func: FuncType) -> dict[str, typing.Any]:
|
|
23
43
|
fspec = inspect.getfullargspec(func)
|
|
24
|
-
|
|
44
|
+
if not fspec.defaults:
|
|
45
|
+
return {}
|
|
46
|
+
return dict(zip(fspec.args[-len(fspec.defaults):], fspec.defaults))
|
|
25
47
|
|
|
26
48
|
|
|
27
|
-
def get_annotations(func: FuncType) -> dict[str, typing.Any]:
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
49
|
+
def get_annotations(func: FuncType, *, return_type: bool = False) -> dict[str, typing.Any]:
|
|
50
|
+
annotations = func.__annotations__
|
|
51
|
+
if not return_type and "return" in func.__annotations__:
|
|
52
|
+
annotations.pop("return")
|
|
53
|
+
return annotations
|
|
33
54
|
|
|
34
55
|
|
|
35
56
|
def to_str(s: str | enum.Enum) -> str:
|
|
@@ -38,13 +59,64 @@ def to_str(s: str | enum.Enum) -> str:
|
|
|
38
59
|
return s
|
|
39
60
|
|
|
40
61
|
|
|
62
|
+
@typing.overload
|
|
63
|
+
def magic_bundle(handler: FuncType, kw: dict[str, typing.Any]) -> dict[str, typing.Any]:
|
|
64
|
+
...
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@typing.overload
|
|
68
|
+
def magic_bundle(handler: FuncType, kw: dict[enum.Enum, typing.Any]) -> dict[str, typing.Any]:
|
|
69
|
+
...
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@typing.overload
|
|
41
73
|
def magic_bundle(
|
|
42
74
|
handler: FuncType,
|
|
43
|
-
kw: dict[str
|
|
75
|
+
kw: dict[str, typing.Any],
|
|
44
76
|
*,
|
|
45
77
|
start_idx: int = 1,
|
|
46
78
|
bundle_ctx: bool = True,
|
|
47
79
|
) -> dict[str, typing.Any]:
|
|
80
|
+
...
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@typing.overload
|
|
84
|
+
def magic_bundle(
|
|
85
|
+
handler: FuncType,
|
|
86
|
+
kw: dict[enum.Enum, typing.Any],
|
|
87
|
+
*,
|
|
88
|
+
start_idx: int = 1,
|
|
89
|
+
bundle_ctx: bool = True,
|
|
90
|
+
) -> dict[str, typing.Any]:
|
|
91
|
+
...
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@typing.overload
|
|
95
|
+
def magic_bundle(
|
|
96
|
+
handler: FuncType,
|
|
97
|
+
kw: dict[type, typing.Any],
|
|
98
|
+
*,
|
|
99
|
+
typebundle: typing.Literal[True] = True,
|
|
100
|
+
) -> dict[str, typing.Any]:
|
|
101
|
+
...
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def magic_bundle(
|
|
105
|
+
handler: FuncType,
|
|
106
|
+
kw: dict[typing.Any, typing.Any],
|
|
107
|
+
*,
|
|
108
|
+
start_idx: int = 1,
|
|
109
|
+
bundle_ctx: bool = True,
|
|
110
|
+
typebundle: bool = False,
|
|
111
|
+
) -> dict[str, typing.Any]:
|
|
112
|
+
|
|
113
|
+
if typebundle:
|
|
114
|
+
types = get_annotations(handler, return_type=False)
|
|
115
|
+
bundle: dict[str, typing.Any] = {}
|
|
116
|
+
for name, type in types.items():
|
|
117
|
+
bundle[name] = kw[type]
|
|
118
|
+
return bundle
|
|
119
|
+
|
|
48
120
|
names = resolve_arg_names(handler, start_idx=start_idx)
|
|
49
121
|
args = get_default_args(handler)
|
|
50
122
|
args.update({to_str(k): v for k, v in kw.items() if to_str(k) in names})
|
|
@@ -59,30 +131,36 @@ def get_cached_translation(rule: "T", locale: str) -> "T | None":
|
|
|
59
131
|
|
|
60
132
|
def cache_translation(base_rule: "T", locale: str, translated_rule: "T") -> None:
|
|
61
133
|
translations = getattr(base_rule, TRANSLATIONS_KEY, {})
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
def get_impls(cls: type[typing.Any]) -> list[typing.Callable[..., typing.Any]]:
|
|
66
|
-
functions = [func.__func__ for func in vars(cls).values() if isinstance(func, classmethod)]
|
|
67
|
-
return [impl for impl in functions if getattr(impl, IMPL_MARK, False) is True]
|
|
134
|
+
translations[locale] = translated_rule
|
|
135
|
+
setattr(base_rule, TRANSLATIONS_KEY, translations)
|
|
68
136
|
|
|
69
137
|
|
|
70
138
|
@typing.cast(typing.Callable[..., Impl], lambda f: f)
|
|
71
139
|
def impl(method: typing.Callable[..., typing.Any]):
|
|
72
|
-
bound_method = classmethod(method)
|
|
73
140
|
setattr(method, IMPL_MARK, True)
|
|
74
|
-
return
|
|
141
|
+
return classmethod(method)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def get_impls(cls: type["Node"]) -> list[typing.Callable[..., typing.Any]]:
|
|
145
|
+
return [
|
|
146
|
+
func.__func__
|
|
147
|
+
for func in vars(cls).values()
|
|
148
|
+
if isinstance(func, classmethod) and getattr(func.__func__, IMPL_MARK, False)
|
|
149
|
+
]
|
|
150
|
+
|
|
75
151
|
|
|
76
152
|
|
|
77
153
|
__all__ = (
|
|
78
154
|
"TRANSLATIONS_KEY",
|
|
155
|
+
"cache_magic_value",
|
|
79
156
|
"cache_translation",
|
|
157
|
+
"get_annotations",
|
|
80
158
|
"get_cached_translation",
|
|
81
159
|
"get_default_args",
|
|
82
160
|
"get_default_args",
|
|
83
|
-
"magic_bundle",
|
|
84
161
|
"impl",
|
|
162
|
+
"get_impls",
|
|
163
|
+
"magic_bundle",
|
|
85
164
|
"resolve_arg_names",
|
|
86
165
|
"to_str",
|
|
87
|
-
"get_annotations",
|
|
88
166
|
)
|