telegrinder 0.3.4__py3-none-any.whl → 0.4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of telegrinder might be problematic. Click here for more details.
- telegrinder/__init__.py +148 -149
- telegrinder/api/__init__.py +9 -8
- telegrinder/api/api.py +101 -93
- telegrinder/api/error.py +20 -16
- telegrinder/api/response.py +20 -20
- telegrinder/api/token.py +36 -36
- telegrinder/bot/__init__.py +72 -66
- telegrinder/bot/bot.py +83 -76
- telegrinder/bot/cute_types/__init__.py +19 -17
- telegrinder/bot/cute_types/base.py +184 -258
- telegrinder/bot/cute_types/callback_query.py +400 -385
- telegrinder/bot/cute_types/chat_join_request.py +62 -61
- telegrinder/bot/cute_types/chat_member_updated.py +157 -160
- telegrinder/bot/cute_types/inline_query.py +44 -43
- telegrinder/bot/cute_types/message.py +2590 -2637
- telegrinder/bot/cute_types/pre_checkout_query.py +42 -0
- telegrinder/bot/cute_types/update.py +112 -104
- telegrinder/bot/cute_types/utils.py +62 -95
- telegrinder/bot/dispatch/__init__.py +59 -55
- telegrinder/bot/dispatch/abc.py +76 -77
- telegrinder/bot/dispatch/context.py +96 -98
- telegrinder/bot/dispatch/dispatch.py +254 -202
- telegrinder/bot/dispatch/handler/__init__.py +13 -13
- telegrinder/bot/dispatch/handler/abc.py +23 -24
- telegrinder/bot/dispatch/handler/audio_reply.py +44 -44
- telegrinder/bot/dispatch/handler/base.py +57 -57
- telegrinder/bot/dispatch/handler/document_reply.py +44 -44
- telegrinder/bot/dispatch/handler/func.py +129 -135
- telegrinder/bot/dispatch/handler/media_group_reply.py +44 -43
- telegrinder/bot/dispatch/handler/message_reply.py +36 -36
- telegrinder/bot/dispatch/handler/photo_reply.py +44 -44
- telegrinder/bot/dispatch/handler/sticker_reply.py +37 -37
- telegrinder/bot/dispatch/handler/video_reply.py +44 -44
- telegrinder/bot/dispatch/middleware/__init__.py +3 -3
- telegrinder/bot/dispatch/middleware/abc.py +97 -22
- telegrinder/bot/dispatch/middleware/global_middleware.py +70 -0
- telegrinder/bot/dispatch/process.py +151 -157
- telegrinder/bot/dispatch/return_manager/__init__.py +15 -13
- telegrinder/bot/dispatch/return_manager/abc.py +104 -108
- telegrinder/bot/dispatch/return_manager/callback_query.py +20 -20
- telegrinder/bot/dispatch/return_manager/inline_query.py +15 -15
- telegrinder/bot/dispatch/return_manager/message.py +36 -36
- telegrinder/bot/dispatch/return_manager/pre_checkout_query.py +20 -0
- telegrinder/bot/dispatch/view/__init__.py +15 -13
- telegrinder/bot/dispatch/view/abc.py +45 -41
- telegrinder/bot/dispatch/view/base.py +231 -200
- telegrinder/bot/dispatch/view/box.py +140 -129
- telegrinder/bot/dispatch/view/callback_query.py +16 -17
- telegrinder/bot/dispatch/view/chat_join_request.py +11 -16
- telegrinder/bot/dispatch/view/chat_member.py +37 -39
- telegrinder/bot/dispatch/view/inline_query.py +16 -17
- telegrinder/bot/dispatch/view/message.py +43 -44
- telegrinder/bot/dispatch/view/pre_checkout_query.py +16 -0
- telegrinder/bot/dispatch/view/raw.py +116 -114
- telegrinder/bot/dispatch/waiter_machine/__init__.py +17 -17
- telegrinder/bot/dispatch/waiter_machine/actions.py +14 -13
- telegrinder/bot/dispatch/waiter_machine/hasher/__init__.py +8 -8
- telegrinder/bot/dispatch/waiter_machine/hasher/callback.py +55 -55
- telegrinder/bot/dispatch/waiter_machine/hasher/hasher.py +59 -57
- telegrinder/bot/dispatch/waiter_machine/hasher/message.py +51 -51
- telegrinder/bot/dispatch/waiter_machine/hasher/state.py +20 -19
- telegrinder/bot/dispatch/waiter_machine/machine.py +251 -172
- telegrinder/bot/dispatch/waiter_machine/middleware.py +94 -89
- telegrinder/bot/dispatch/waiter_machine/short_state.py +57 -68
- telegrinder/bot/polling/__init__.py +4 -4
- telegrinder/bot/polling/abc.py +25 -25
- telegrinder/bot/polling/polling.py +139 -131
- telegrinder/bot/rules/__init__.py +85 -62
- telegrinder/bot/rules/abc.py +213 -206
- telegrinder/bot/rules/callback_data.py +122 -163
- telegrinder/bot/rules/chat_join.py +45 -43
- telegrinder/bot/rules/command.py +126 -126
- telegrinder/bot/rules/enum_text.py +33 -36
- telegrinder/bot/rules/func.py +28 -26
- telegrinder/bot/rules/fuzzy.py +24 -24
- telegrinder/bot/rules/id.py +24 -0
- telegrinder/bot/rules/inline.py +58 -56
- telegrinder/bot/rules/integer.py +21 -20
- telegrinder/bot/rules/is_from.py +127 -127
- telegrinder/bot/rules/logic.py +18 -0
- telegrinder/bot/rules/markup.py +42 -43
- telegrinder/bot/rules/mention.py +14 -14
- telegrinder/bot/rules/message.py +15 -17
- telegrinder/bot/rules/message_entities.py +33 -35
- telegrinder/bot/rules/node.py +33 -27
- telegrinder/bot/rules/payload.py +81 -0
- telegrinder/bot/rules/payment_invoice.py +29 -0
- telegrinder/bot/rules/regex.py +36 -37
- telegrinder/bot/rules/rule_enum.py +72 -72
- telegrinder/bot/rules/start.py +42 -42
- telegrinder/bot/rules/state.py +35 -37
- telegrinder/bot/rules/text.py +38 -33
- telegrinder/bot/rules/update.py +15 -15
- telegrinder/bot/scenario/__init__.py +5 -5
- telegrinder/bot/scenario/abc.py +17 -19
- telegrinder/bot/scenario/checkbox.py +174 -176
- telegrinder/bot/scenario/choice.py +48 -51
- telegrinder/client/__init__.py +12 -4
- telegrinder/client/abc.py +100 -75
- telegrinder/client/aiohttp.py +134 -130
- telegrinder/client/form_data.py +31 -0
- telegrinder/client/sonic.py +212 -0
- telegrinder/model.py +208 -315
- telegrinder/modules.py +239 -237
- telegrinder/msgspec_json.py +14 -14
- telegrinder/msgspec_utils.py +478 -410
- telegrinder/node/__init__.py +86 -25
- telegrinder/node/attachment.py +163 -87
- telegrinder/node/base.py +288 -160
- telegrinder/node/callback_query.py +54 -53
- telegrinder/node/command.py +34 -33
- telegrinder/node/composer.py +163 -198
- telegrinder/node/container.py +33 -27
- telegrinder/node/either.py +82 -0
- telegrinder/node/event.py +54 -65
- telegrinder/node/file.py +51 -0
- telegrinder/node/me.py +15 -16
- telegrinder/node/payload.py +78 -0
- telegrinder/node/polymorphic.py +67 -48
- telegrinder/node/rule.py +72 -76
- telegrinder/node/scope.py +36 -38
- telegrinder/node/source.py +87 -71
- telegrinder/node/text.py +53 -41
- telegrinder/node/tools/__init__.py +3 -3
- telegrinder/node/tools/generator.py +36 -40
- telegrinder/py.typed +0 -0
- telegrinder/rules.py +1 -62
- telegrinder/tools/__init__.py +152 -93
- telegrinder/tools/adapter/__init__.py +19 -0
- telegrinder/tools/adapter/abc.py +49 -0
- telegrinder/tools/adapter/dataclass.py +56 -0
- telegrinder/{bot/rules → tools}/adapter/errors.py +5 -5
- telegrinder/{bot/rules → tools}/adapter/event.py +63 -65
- telegrinder/{bot/rules → tools}/adapter/node.py +46 -48
- telegrinder/{bot/rules → tools}/adapter/raw_event.py +27 -27
- telegrinder/{bot/rules → tools}/adapter/raw_update.py +30 -30
- telegrinder/tools/buttons.py +106 -80
- telegrinder/tools/callback_data_serilization/__init__.py +5 -0
- telegrinder/tools/callback_data_serilization/abc.py +51 -0
- telegrinder/tools/callback_data_serilization/json_ser.py +60 -0
- telegrinder/tools/callback_data_serilization/msgpack_ser.py +172 -0
- telegrinder/tools/error_handler/__init__.py +7 -7
- telegrinder/tools/error_handler/abc.py +30 -33
- telegrinder/tools/error_handler/error.py +9 -9
- telegrinder/tools/error_handler/error_handler.py +179 -193
- telegrinder/tools/formatting/__init__.py +83 -63
- telegrinder/tools/formatting/deep_links.py +541 -0
- telegrinder/tools/formatting/{html.py → html_formatter.py} +266 -294
- telegrinder/tools/formatting/spec_html_formats.py +71 -117
- telegrinder/tools/functional.py +8 -12
- telegrinder/tools/global_context/__init__.py +7 -7
- telegrinder/tools/global_context/abc.py +63 -63
- telegrinder/tools/global_context/global_context.py +387 -412
- telegrinder/tools/global_context/telegrinder_ctx.py +27 -27
- telegrinder/tools/i18n/__init__.py +7 -7
- telegrinder/tools/i18n/abc.py +30 -30
- telegrinder/tools/i18n/middleware/__init__.py +3 -3
- telegrinder/tools/i18n/middleware/abc.py +22 -25
- telegrinder/tools/i18n/simple.py +43 -43
- telegrinder/tools/input_file_directory.py +30 -0
- telegrinder/tools/keyboard.py +128 -128
- telegrinder/tools/lifespan.py +105 -0
- telegrinder/tools/limited_dict.py +32 -37
- telegrinder/tools/loop_wrapper/__init__.py +4 -4
- telegrinder/tools/loop_wrapper/abc.py +20 -15
- telegrinder/tools/loop_wrapper/loop_wrapper.py +169 -224
- telegrinder/tools/magic.py +307 -157
- telegrinder/tools/parse_mode.py +6 -6
- telegrinder/tools/state_storage/__init__.py +4 -4
- telegrinder/tools/state_storage/abc.py +31 -35
- telegrinder/tools/state_storage/memory.py +25 -25
- telegrinder/tools/strings.py +13 -0
- telegrinder/types/__init__.py +268 -260
- telegrinder/types/enums.py +711 -701
- telegrinder/types/input_file.py +51 -0
- telegrinder/types/methods.py +5055 -4633
- telegrinder/types/objects.py +7058 -6950
- telegrinder/verification_utils.py +30 -32
- {telegrinder-0.3.4.dist-info → telegrinder-0.4.0.dist-info}/LICENSE +22 -22
- telegrinder-0.4.0.dist-info/METADATA +144 -0
- telegrinder-0.4.0.dist-info/RECORD +182 -0
- {telegrinder-0.3.4.dist-info → telegrinder-0.4.0.dist-info}/WHEEL +1 -1
- telegrinder/bot/rules/adapter/__init__.py +0 -17
- telegrinder/bot/rules/adapter/abc.py +0 -31
- telegrinder/node/message.py +0 -14
- telegrinder/node/update.py +0 -15
- telegrinder/tools/formatting/links.py +0 -38
- telegrinder/tools/kb_set/__init__.py +0 -4
- telegrinder/tools/kb_set/base.py +0 -15
- telegrinder/tools/kb_set/yaml.py +0 -63
- telegrinder-0.3.4.dist-info/METADATA +0 -110
- telegrinder-0.3.4.dist-info/RECORD +0 -165
telegrinder/tools/magic.py
CHANGED
|
@@ -1,168 +1,318 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import dataclasses
|
|
5
|
+
import enum
|
|
6
|
+
import inspect
|
|
7
|
+
import types
|
|
8
|
+
import typing
|
|
9
|
+
from collections import OrderedDict
|
|
10
|
+
from functools import wraps
|
|
11
|
+
|
|
12
|
+
from fntypes import Result
|
|
13
|
+
|
|
14
|
+
from telegrinder.model import get_params
|
|
15
|
+
|
|
16
|
+
if typing.TYPE_CHECKING:
|
|
17
|
+
from telegrinder.bot.rules.abc import ABCRule
|
|
18
|
+
from telegrinder.node.polymorphic import Polymorphic
|
|
19
|
+
|
|
20
|
+
type Impl = type[classmethod]
|
|
21
|
+
type FuncType = types.FunctionType | typing.Callable[..., typing.Any]
|
|
22
|
+
|
|
23
|
+
type Executor[T] = typing.Callable[
|
|
24
|
+
[T, str, dict[str, typing.Any]],
|
|
25
|
+
typing.Awaitable[Result[typing.Any, typing.Any]],
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
TRANSLATIONS_KEY: typing.Final[str] = "_translations"
|
|
29
|
+
IMPL_MARK: typing.Final[str] = "_is_impl"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclasses.dataclass(slots=True, frozen=True)
|
|
33
|
+
class Shortcut[T]:
|
|
34
|
+
method_name: str
|
|
35
|
+
executor: Executor[T] | None = dataclasses.field(default=None, kw_only=True)
|
|
36
|
+
custom_params: set[str] = dataclasses.field(default_factory=lambda: set(), kw_only=True)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def cache_magic_value(mark_key: str, /):
|
|
40
|
+
def inner[Func: typing.Callable[..., typing.Any]](func: Func) -> Func:
|
|
41
|
+
@wraps(func)
|
|
42
|
+
def wrapper(*args: typing.Any, **kwargs: typing.Any) -> typing.Any:
|
|
43
|
+
if mark_key not in args[0].__dict__:
|
|
44
|
+
args[0].__dict__[mark_key] = func(*args, **kwargs)
|
|
45
|
+
return args[0].__dict__[mark_key]
|
|
46
|
+
|
|
47
|
+
return wrapper # type: ignore
|
|
48
|
+
|
|
49
|
+
return inner
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def resolve_arg_names(func: FuncType, start_idx: int = 1) -> tuple[str, ...]:
|
|
53
|
+
return func.__code__.co_varnames[start_idx : func.__code__.co_argcount]
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@cache_magic_value("__default_args__")
|
|
57
|
+
def get_default_args(func: FuncType) -> dict[str, typing.Any]:
|
|
58
|
+
kwdefaults = func.__kwdefaults__
|
|
59
|
+
if kwdefaults:
|
|
60
|
+
return kwdefaults
|
|
61
|
+
|
|
62
|
+
defaults = func.__defaults__
|
|
63
|
+
if not defaults:
|
|
64
|
+
return {}
|
|
65
|
+
|
|
66
|
+
return {k: defaults[i] for i, k in enumerate(resolve_arg_names(func, start_idx=0)[-len(defaults) :])}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@cache_magic_value("__func_parameters__")
|
|
70
|
+
def get_func_parameters(func: FuncType, /) -> FuncParams:
|
|
71
|
+
func_params: FuncParams = {"args": [], "kwargs": []}
|
|
72
|
+
|
|
73
|
+
for k, p in inspect.signature(func).parameters.items():
|
|
74
|
+
if k in ("self", "cls"):
|
|
75
|
+
continue
|
|
76
|
+
|
|
77
|
+
match p.kind:
|
|
78
|
+
case p.POSITIONAL_OR_KEYWORD | p.POSITIONAL_ONLY:
|
|
79
|
+
func_params["args"].append((k, p.default))
|
|
80
|
+
case p.KEYWORD_ONLY:
|
|
81
|
+
func_params["kwargs"].append((k, p.default))
|
|
82
|
+
case p.VAR_POSITIONAL:
|
|
83
|
+
func_params["var_args"] = k
|
|
84
|
+
case p.VAR_KEYWORD:
|
|
85
|
+
func_params["var_kwargs"] = k
|
|
86
|
+
|
|
87
|
+
return func_params
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def get_annotations(func: FuncType, *, return_type: bool = False) -> dict[str, typing.Any]:
|
|
91
|
+
annotations = func.__annotations__
|
|
92
|
+
if not return_type:
|
|
93
|
+
annotations.pop("return", None)
|
|
94
|
+
return annotations
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def to_str(s: str | enum.Enum) -> str:
|
|
98
|
+
if isinstance(s, enum.Enum):
|
|
99
|
+
return str(s.value)
|
|
100
|
+
return s
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@typing.overload
|
|
104
|
+
def magic_bundle(function: FuncType, kw: dict[str, typing.Any]) -> dict[str, typing.Any]: ...
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@typing.overload
|
|
108
|
+
def magic_bundle(function: FuncType, kw: dict[enum.Enum, typing.Any]) -> dict[str, typing.Any]: ...
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
@typing.overload
|
|
112
|
+
def magic_bundle(
|
|
113
|
+
function: FuncType,
|
|
114
|
+
kw: dict[str, typing.Any],
|
|
115
|
+
*,
|
|
116
|
+
start_idx: int = 1,
|
|
117
|
+
bundle_ctx: bool = True,
|
|
118
|
+
) -> dict[str, typing.Any]: ...
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
@typing.overload
|
|
122
|
+
def magic_bundle(
|
|
123
|
+
function: FuncType,
|
|
124
|
+
kw: dict[enum.Enum, typing.Any],
|
|
125
|
+
*,
|
|
126
|
+
start_idx: int = 1,
|
|
127
|
+
bundle_ctx: bool = True,
|
|
128
|
+
) -> dict[str, typing.Any]: ...
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
@typing.overload
|
|
132
|
+
def magic_bundle(
|
|
133
|
+
function: FuncType,
|
|
134
|
+
kw: dict[type[typing.Any], typing.Any],
|
|
135
|
+
*,
|
|
136
|
+
typebundle: typing.Literal[True] = True,
|
|
137
|
+
) -> dict[str, typing.Any]: ...
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def magic_bundle(
|
|
141
|
+
function: FuncType,
|
|
142
|
+
kw: dict[typing.Any, typing.Any],
|
|
143
|
+
*,
|
|
144
|
+
start_idx: int = 1,
|
|
145
|
+
bundle_ctx: bool = True,
|
|
146
|
+
typebundle: bool = False,
|
|
147
|
+
) -> dict[str, typing.Any]:
|
|
148
|
+
# Bundle considering the function annotations
|
|
149
|
+
if typebundle:
|
|
150
|
+
return {name: kw[t] for name, t in get_annotations(function, return_type=False).items() if t in kw}
|
|
151
|
+
|
|
152
|
+
names = resolve_arg_names(function, start_idx=start_idx)
|
|
153
|
+
# Determine if the function is only have the **kwargs parameter, then bundle all kw
|
|
154
|
+
if "var_kwargs" in get_func_parameters(function) and not names:
|
|
155
|
+
return {to_str(k): v for k, v in kw.items()}
|
|
156
|
+
|
|
157
|
+
# Bundle considering the function parameters and defaults
|
|
158
|
+
args = get_default_args(function) | {n: v for k, v in kw.items() if (n := to_str(k)) in names}
|
|
159
|
+
if "ctx" in names and bundle_ctx:
|
|
160
|
+
args["ctx"] = kw
|
|
161
|
+
|
|
162
|
+
return args
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def join_dicts[Key: typing.Hashable, Value](
|
|
166
|
+
left_dict: dict[Key, typing.Any],
|
|
167
|
+
right_dict: dict[typing.Any, Value],
|
|
168
|
+
) -> dict[Key, Value]:
|
|
169
|
+
return {key: right_dict[type_key] for key, type_key in left_dict.items()}
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def get_cached_translation[Rule: ABCRule](rule: Rule, locale: str) -> Rule | None:
|
|
173
|
+
return getattr(rule, TRANSLATIONS_KEY, {}).get(locale)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def cache_translation[Rule: ABCRule](
|
|
177
|
+
base_rule: Rule,
|
|
178
|
+
locale: str,
|
|
179
|
+
translated_rule: Rule,
|
|
180
|
+
) -> None:
|
|
181
|
+
translations = getattr(base_rule, TRANSLATIONS_KEY, {})
|
|
182
|
+
translations[locale] = translated_rule
|
|
183
|
+
setattr(base_rule, TRANSLATIONS_KEY, translations)
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
@typing.cast(typing.Callable[..., Impl], lambda f: f)
|
|
187
|
+
def impl(method: typing.Callable[..., typing.Any]):
|
|
188
|
+
setattr(method, IMPL_MARK, True)
|
|
189
|
+
return classmethod(method)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def get_impls(cls: type[Polymorphic]) -> list[typing.Callable[..., typing.Any]]:
|
|
193
|
+
moprh_impls = getattr(cls, "__morph_impls__", None)
|
|
194
|
+
if moprh_impls is not None:
|
|
195
|
+
return moprh_impls
|
|
196
|
+
|
|
197
|
+
impls = []
|
|
198
|
+
for cls_ in cls.mro():
|
|
199
|
+
impls += [
|
|
200
|
+
func.__func__
|
|
201
|
+
for func in vars(cls_).values()
|
|
202
|
+
if isinstance(func, classmethod) and getattr(func.__func__, IMPL_MARK, False)
|
|
203
|
+
]
|
|
204
|
+
|
|
205
|
+
setattr(cls, "__morph_impls__", impls)
|
|
206
|
+
return impls
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
class FuncParams(typing.TypedDict, total=True):
|
|
210
|
+
args: list[tuple[str, typing.Any | inspect.Parameter.empty]]
|
|
211
|
+
kwargs: list[tuple[str, typing.Any | inspect.Parameter.empty]]
|
|
212
|
+
var_args: typing.NotRequired[str]
|
|
213
|
+
var_kwargs: typing.NotRequired[str]
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def shortcut[T](
|
|
217
|
+
method_name: str,
|
|
218
|
+
*,
|
|
219
|
+
executor: Executor[T] | None = None,
|
|
220
|
+
custom_params: set[str] | None = None,
|
|
221
|
+
):
|
|
222
|
+
"""Decorate a cute method as a shortcut."""
|
|
223
|
+
|
|
224
|
+
def wrapper[F: typing.Callable[..., typing.Any]](func: F) -> F:
|
|
225
|
+
@wraps(func)
|
|
226
|
+
async def inner(
|
|
227
|
+
self: T,
|
|
228
|
+
*args: typing.Any,
|
|
229
|
+
**kwargs: typing.Any,
|
|
230
|
+
) -> typing.Any:
|
|
231
|
+
if executor is None:
|
|
232
|
+
return await func(self, *args, **kwargs)
|
|
233
|
+
|
|
234
|
+
params: dict[str, typing.Any] = OrderedDict()
|
|
235
|
+
func_params = get_func_parameters(func)
|
|
236
|
+
|
|
237
|
+
for index, (arg, default) in enumerate(func_params["args"]):
|
|
238
|
+
if len(args) > index:
|
|
239
|
+
params[arg] = args[index]
|
|
240
|
+
elif default is not inspect.Parameter.empty:
|
|
241
|
+
params[arg] = default
|
|
242
|
+
|
|
243
|
+
if var_args := func_params.get("var_args"):
|
|
244
|
+
params[var_args] = args[len(func_params["args"]) :]
|
|
245
|
+
|
|
246
|
+
for kwarg, default in func_params["kwargs"]:
|
|
247
|
+
params[kwarg] = (
|
|
248
|
+
kwargs.pop(kwarg, default) if default is not inspect.Parameter.empty else kwargs.pop(kwarg)
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
if var_kwargs := func_params.get("var_kwargs"):
|
|
252
|
+
params[var_kwargs] = kwargs.copy()
|
|
253
|
+
|
|
254
|
+
return await executor(self, method_name, get_params(params))
|
|
255
|
+
|
|
256
|
+
inner.__shortcut__ = Shortcut( # type: ignore
|
|
257
|
+
method_name=method_name,
|
|
258
|
+
executor=executor,
|
|
259
|
+
custom_params=custom_params or set(),
|
|
260
|
+
)
|
|
261
|
+
return inner # type: ignore
|
|
262
|
+
|
|
263
|
+
return wrapper
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
# Source code: https://github.com/facebookincubator/later/blob/main/later/task.py#L75
|
|
267
|
+
async def cancel_future(fut: asyncio.Future[typing.Any], /) -> None:
|
|
268
|
+
if fut.done():
|
|
269
|
+
return
|
|
270
|
+
|
|
271
|
+
fut.cancel()
|
|
272
|
+
exc: asyncio.CancelledError | None = None
|
|
273
|
+
|
|
274
|
+
while not fut.done():
|
|
275
|
+
shielded = asyncio.shield(fut)
|
|
276
|
+
try:
|
|
277
|
+
await asyncio.wait([shielded])
|
|
278
|
+
except asyncio.CancelledError as ex:
|
|
279
|
+
exc = ex
|
|
280
|
+
finally:
|
|
281
|
+
# Insure we handle the exception/value that may exist on the shielded task
|
|
282
|
+
# This will prevent errors logged to the asyncio logger
|
|
283
|
+
if shielded.done() and not shielded.cancelled() and not shielded.exception():
|
|
284
|
+
shielded.result()
|
|
285
|
+
|
|
286
|
+
if fut.cancelled():
|
|
287
|
+
if exc is None:
|
|
288
|
+
return
|
|
289
|
+
raise exc from None
|
|
290
|
+
|
|
291
|
+
ex = fut.exception()
|
|
292
|
+
if ex is not None:
|
|
293
|
+
raise ex from None
|
|
294
|
+
|
|
295
|
+
raise asyncio.InvalidStateError(
|
|
296
|
+
f"Task did not raise CancelledError on cancel: {fut!r} had result {fut.result()!r}",
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
__all__ = (
|
|
301
|
+
"Shortcut",
|
|
156
302
|
"TRANSLATIONS_KEY",
|
|
157
303
|
"cache_magic_value",
|
|
158
304
|
"cache_translation",
|
|
305
|
+
"cancel_future",
|
|
159
306
|
"get_annotations",
|
|
160
307
|
"get_cached_translation",
|
|
161
308
|
"get_default_args",
|
|
162
309
|
"get_default_args",
|
|
310
|
+
"get_func_parameters",
|
|
163
311
|
"get_impls",
|
|
164
312
|
"impl",
|
|
313
|
+
"join_dicts",
|
|
165
314
|
"magic_bundle",
|
|
166
315
|
"resolve_arg_names",
|
|
167
|
-
"
|
|
168
|
-
|
|
316
|
+
"shortcut",
|
|
317
|
+
"to_str",
|
|
318
|
+
)
|
telegrinder/tools/parse_mode.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
class ParseMode:
|
|
2
|
-
MARKDOWNV2 = "MarkdownV2"
|
|
3
|
-
HTML = "HTML"
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
__all__ = ("ParseMode",)
|
|
1
|
+
class ParseMode:
|
|
2
|
+
MARKDOWNV2 = "MarkdownV2"
|
|
3
|
+
HTML = "HTML"
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
__all__ = ("ParseMode",)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from telegrinder.tools.state_storage.abc import ABCStateStorage, StateData
|
|
2
|
-
from telegrinder.tools.state_storage.memory import MemoryStateStorage
|
|
3
|
-
|
|
4
|
-
__all__ = ("ABCStateStorage", "MemoryStateStorage", "StateData")
|
|
1
|
+
from telegrinder.tools.state_storage.abc import ABCStateStorage, StateData
|
|
2
|
+
from telegrinder.tools.state_storage.memory import MemoryStateStorage
|
|
3
|
+
|
|
4
|
+
__all__ = ("ABCStateStorage", "MemoryStateStorage", "StateData")
|
|
@@ -1,35 +1,31 @@
|
|
|
1
|
-
import abc
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
@abc.abstractmethod
|
|
21
|
-
async def
|
|
22
|
-
|
|
23
|
-
@abc.abstractmethod
|
|
24
|
-
async def
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
return State(storage=self, key=key)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
__all__ = ("ABCStateStorage", "StateData")
|
|
1
|
+
import abc
|
|
2
|
+
import dataclasses
|
|
3
|
+
import enum
|
|
4
|
+
|
|
5
|
+
from fntypes.option import Option
|
|
6
|
+
|
|
7
|
+
from telegrinder.bot.rules.state import State, StateMeta
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclasses.dataclass(frozen=True, slots=True)
|
|
11
|
+
class StateData[Payload]:
|
|
12
|
+
key: str | enum.Enum
|
|
13
|
+
payload: Payload
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ABCStateStorage[Payload](abc.ABC):
|
|
17
|
+
@abc.abstractmethod
|
|
18
|
+
async def get(self, user_id: int) -> Option[StateData[Payload]]: ...
|
|
19
|
+
|
|
20
|
+
@abc.abstractmethod
|
|
21
|
+
async def delete(self, user_id: int) -> None: ...
|
|
22
|
+
|
|
23
|
+
@abc.abstractmethod
|
|
24
|
+
async def set(self, user_id: int, key: str | enum.Enum, payload: Payload) -> None: ...
|
|
25
|
+
|
|
26
|
+
def State(self, key: str | StateMeta | enum.Enum = StateMeta.ANY, /) -> State[Payload]: # noqa: N802
|
|
27
|
+
"""Can be used as a shortcut to get a state rule dependant on current storage."""
|
|
28
|
+
return State(storage=self, key=key)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
__all__ = ("ABCStateStorage", "StateData")
|
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
import typing
|
|
2
|
-
|
|
3
|
-
from fntypes.option import Option
|
|
4
|
-
|
|
5
|
-
from telegrinder.tools.functional import from_optional
|
|
6
|
-
from telegrinder.tools.state_storage.abc import ABCStateStorage, StateData
|
|
7
|
-
|
|
8
|
-
Payload
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class MemoryStateStorage(ABCStateStorage[Payload]):
|
|
12
|
-
def __init__(self) -> None:
|
|
13
|
-
self.storage: dict[int, StateData[Payload]] = {}
|
|
14
|
-
|
|
15
|
-
async def get(self, user_id: int) -> Option[StateData[Payload]]:
|
|
16
|
-
return from_optional(self.storage.get(user_id))
|
|
17
|
-
|
|
18
|
-
async def set(self, user_id: int, key: str, payload: Payload) -> None:
|
|
19
|
-
self.storage[user_id] = StateData(key, payload)
|
|
20
|
-
|
|
21
|
-
async def delete(self, user_id: int) -> None:
|
|
22
|
-
self.storage.pop(user_id)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
__all__ = ("MemoryStateStorage",)
|
|
1
|
+
import typing
|
|
2
|
+
|
|
3
|
+
from fntypes.option import Option
|
|
4
|
+
|
|
5
|
+
from telegrinder.tools.functional import from_optional
|
|
6
|
+
from telegrinder.tools.state_storage.abc import ABCStateStorage, StateData
|
|
7
|
+
|
|
8
|
+
type Payload = dict[str, typing.Any]
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class MemoryStateStorage(ABCStateStorage[Payload]):
|
|
12
|
+
def __init__(self) -> None:
|
|
13
|
+
self.storage: dict[int, StateData[Payload]] = {}
|
|
14
|
+
|
|
15
|
+
async def get(self, user_id: int) -> Option[StateData[Payload]]:
|
|
16
|
+
return from_optional(self.storage.get(user_id))
|
|
17
|
+
|
|
18
|
+
async def set(self, user_id: int, key: str, payload: Payload) -> None:
|
|
19
|
+
self.storage[user_id] = StateData(key, payload)
|
|
20
|
+
|
|
21
|
+
async def delete(self, user_id: int) -> None:
|
|
22
|
+
self.storage.pop(user_id)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
__all__ = ("MemoryStateStorage",)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
PATTERN = re.compile(r"[^ a-z A-Z 0-9 \s]")
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def to_pascal_case(string: str, /) -> str:
|
|
7
|
+
return "".join(
|
|
8
|
+
"".join(char.capitalize() for char in sub_str)
|
|
9
|
+
for sub_str in (char.split() for char in PATTERN.split(string))
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
__all__ = ("to_pascal_case",)
|