telegrinder 0.1.dev168__py3-none-any.whl → 0.1.dev170__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 +9 -3
- telegrinder/bot/__init__.py +7 -5
- telegrinder/bot/cute_types/base.py +12 -14
- telegrinder/bot/cute_types/callback_query.py +54 -43
- telegrinder/bot/cute_types/chat_join_request.py +8 -7
- telegrinder/bot/cute_types/chat_member_updated.py +23 -17
- telegrinder/bot/cute_types/inline_query.py +1 -1
- telegrinder/bot/cute_types/message.py +331 -183
- telegrinder/bot/cute_types/update.py +4 -8
- telegrinder/bot/cute_types/utils.py +1 -5
- telegrinder/bot/dispatch/__init__.py +2 -3
- telegrinder/bot/dispatch/abc.py +4 -0
- telegrinder/bot/dispatch/context.py +9 -4
- telegrinder/bot/dispatch/dispatch.py +35 -33
- telegrinder/bot/dispatch/handler/func.py +34 -13
- telegrinder/bot/dispatch/handler/message_reply.py +6 -3
- telegrinder/bot/dispatch/middleware/abc.py +4 -4
- telegrinder/bot/dispatch/process.py +40 -13
- telegrinder/bot/dispatch/return_manager/abc.py +12 -12
- telegrinder/bot/dispatch/return_manager/callback_query.py +1 -3
- telegrinder/bot/dispatch/return_manager/inline_query.py +1 -3
- telegrinder/bot/dispatch/view/abc.py +37 -45
- telegrinder/bot/dispatch/view/box.py +66 -50
- telegrinder/bot/dispatch/view/message.py +1 -5
- telegrinder/bot/dispatch/view/raw.py +6 -6
- telegrinder/bot/dispatch/waiter_machine/__init__.py +2 -1
- telegrinder/bot/dispatch/waiter_machine/machine.py +77 -35
- telegrinder/bot/dispatch/waiter_machine/middleware.py +31 -7
- telegrinder/bot/dispatch/waiter_machine/short_state.py +17 -8
- telegrinder/bot/polling/polling.py +4 -4
- telegrinder/bot/rules/__init__.py +9 -6
- telegrinder/bot/rules/abc.py +99 -22
- telegrinder/bot/rules/adapter/__init__.py +4 -1
- telegrinder/bot/rules/adapter/abc.py +11 -6
- telegrinder/bot/rules/adapter/errors.py +1 -2
- telegrinder/bot/rules/adapter/event.py +14 -9
- telegrinder/bot/rules/adapter/node.py +42 -0
- telegrinder/bot/rules/callback_data.py +4 -4
- telegrinder/bot/rules/chat_join.py +3 -2
- telegrinder/bot/rules/command.py +26 -14
- telegrinder/bot/rules/enum_text.py +5 -5
- telegrinder/bot/rules/func.py +6 -6
- telegrinder/bot/rules/fuzzy.py +5 -7
- telegrinder/bot/rules/inline.py +4 -5
- telegrinder/bot/rules/integer.py +10 -8
- telegrinder/bot/rules/is_from.py +63 -91
- telegrinder/bot/rules/markup.py +5 -5
- telegrinder/bot/rules/mention.py +4 -4
- telegrinder/bot/rules/message.py +1 -1
- telegrinder/bot/rules/node.py +27 -0
- telegrinder/bot/rules/regex.py +5 -5
- telegrinder/bot/rules/rule_enum.py +4 -4
- telegrinder/bot/rules/start.py +5 -5
- telegrinder/bot/rules/text.py +9 -13
- telegrinder/bot/rules/update.py +4 -4
- telegrinder/bot/scenario/__init__.py +3 -3
- telegrinder/bot/scenario/choice.py +2 -3
- telegrinder/model.py +51 -16
- telegrinder/modules.py +14 -6
- telegrinder/msgspec_utils.py +67 -23
- telegrinder/node/__init__.py +26 -8
- telegrinder/node/attachment.py +13 -9
- telegrinder/node/base.py +27 -14
- telegrinder/node/callback_query.py +18 -0
- telegrinder/node/command.py +29 -0
- telegrinder/node/composer.py +119 -30
- telegrinder/node/me.py +14 -0
- telegrinder/node/message.py +2 -4
- telegrinder/node/polymorphic.py +44 -0
- telegrinder/node/rule.py +26 -22
- telegrinder/node/scope.py +36 -0
- telegrinder/node/source.py +37 -10
- telegrinder/node/text.py +11 -5
- telegrinder/node/tools/__init__.py +2 -2
- telegrinder/node/tools/generator.py +6 -6
- telegrinder/tools/__init__.py +8 -13
- telegrinder/tools/buttons.py +23 -17
- telegrinder/tools/error_handler/error_handler.py +11 -14
- telegrinder/tools/formatting/__init__.py +0 -6
- telegrinder/tools/formatting/html.py +10 -12
- telegrinder/tools/formatting/links.py +0 -5
- telegrinder/tools/formatting/spec_html_formats.py +0 -11
- telegrinder/tools/global_context/abc.py +1 -3
- telegrinder/tools/global_context/global_context.py +6 -16
- telegrinder/tools/i18n/simple.py +1 -3
- telegrinder/tools/kb_set/yaml.py +1 -2
- telegrinder/tools/keyboard.py +7 -8
- telegrinder/tools/limited_dict.py +1 -1
- telegrinder/tools/loop_wrapper/loop_wrapper.py +6 -5
- telegrinder/tools/magic.py +27 -5
- telegrinder/types/__init__.py +20 -0
- telegrinder/types/enums.py +37 -31
- telegrinder/types/methods.py +608 -327
- telegrinder/types/objects.py +1139 -716
- {telegrinder-0.1.dev168.dist-info → telegrinder-0.1.dev170.dist-info}/LICENSE +1 -1
- {telegrinder-0.1.dev168.dist-info → telegrinder-0.1.dev170.dist-info}/METADATA +6 -5
- telegrinder-0.1.dev170.dist-info/RECORD +143 -0
- telegrinder/bot/dispatch/composition.py +0 -88
- telegrinder-0.1.dev168.dist-info/RECORD +0 -137
- {telegrinder-0.1.dev168.dist-info → telegrinder-0.1.dev170.dist-info}/WHEEL +0 -0
|
@@ -19,13 +19,13 @@ FuncCatcher = typing.Callable[typing.Concatenate[ExceptionT, ...], typing.Awaita
|
|
|
19
19
|
@dataclasses.dataclass(frozen=True, repr=False)
|
|
20
20
|
class Catcher(typing.Generic[EventT]):
|
|
21
21
|
func: FuncCatcher[BaseException]
|
|
22
|
-
_: dataclasses.KW_ONLY
|
|
23
22
|
exceptions: list[type[BaseException] | BaseException] = dataclasses.field(
|
|
24
23
|
default_factory=lambda: [],
|
|
24
|
+
kw_only=True,
|
|
25
25
|
)
|
|
26
|
-
logging: bool = dataclasses.field(default=False)
|
|
27
|
-
raise_exception: bool = dataclasses.field(default=False)
|
|
28
|
-
ignore_errors: bool = dataclasses.field(default=False)
|
|
26
|
+
logging: bool = dataclasses.field(default=False, kw_only=True)
|
|
27
|
+
raise_exception: bool = dataclasses.field(default=False, kw_only=True)
|
|
28
|
+
ignore_errors: bool = dataclasses.field(default=False, kw_only=True)
|
|
29
29
|
|
|
30
30
|
def __repr__(self) -> str:
|
|
31
31
|
return "<Catcher: function={!r}, logging={}, raise_exception={}, ignore_errors={}>".format(
|
|
@@ -57,9 +57,8 @@ class Catcher(typing.Generic[EventT]):
|
|
|
57
57
|
) -> Result[typing.Any, BaseException]:
|
|
58
58
|
if self.match_exception(exception):
|
|
59
59
|
logger.debug(
|
|
60
|
-
"
|
|
61
|
-
self.func.__name__
|
|
62
|
-
handler_name,
|
|
60
|
+
"Error handler caught an exception {!r} in handler {!r}, running catcher {!r}...".format(
|
|
61
|
+
exception, handler_name, self.func.__name__
|
|
63
62
|
)
|
|
64
63
|
)
|
|
65
64
|
return Ok(
|
|
@@ -68,7 +67,7 @@ class Catcher(typing.Generic[EventT]):
|
|
|
68
67
|
**magic_bundle(self.func, {"event": event, "api": api} | ctx), # type: ignore
|
|
69
68
|
)
|
|
70
69
|
)
|
|
71
|
-
logger.debug("Failed to match exception {!r}
|
|
70
|
+
logger.debug("Failed to match exception {!r}.", exception.__class__.__name__)
|
|
72
71
|
return Error(exception)
|
|
73
72
|
|
|
74
73
|
def match_exception(self, exception: BaseException) -> bool:
|
|
@@ -88,14 +87,11 @@ class ErrorHandler(ABCErrorHandler[EventT]):
|
|
|
88
87
|
return (
|
|
89
88
|
"<{}: exceptions_handled=[{}], catcher={!r}>".format(
|
|
90
89
|
self.__class__.__name__,
|
|
91
|
-
", ".join(
|
|
92
|
-
e.__name__ if isinstance(e, type) else repr(e)
|
|
93
|
-
for e in self.catcher.exceptions
|
|
94
|
-
),
|
|
90
|
+
", ".join(e.__name__ if isinstance(e, type) else repr(e) for e in self.catcher.exceptions),
|
|
95
91
|
self.catcher,
|
|
96
92
|
)
|
|
97
93
|
if self.catcher is not None
|
|
98
|
-
else "<{}
|
|
94
|
+
else "<{}()>".format(self.__class__.__name__)
|
|
99
95
|
)
|
|
100
96
|
|
|
101
97
|
def __call__(
|
|
@@ -133,6 +129,7 @@ class ErrorHandler(ABCErrorHandler[EventT]):
|
|
|
133
129
|
ctx: Context,
|
|
134
130
|
) -> Result[typing.Any, BaseException]:
|
|
135
131
|
assert self.catcher is not None
|
|
132
|
+
logger.debug("Processing the error handler for handler {!r}...", handler.__name__)
|
|
136
133
|
|
|
137
134
|
try:
|
|
138
135
|
return await self.catcher(handler, event, api, ctx)
|
|
@@ -172,7 +169,7 @@ class ErrorHandler(ABCErrorHandler[EventT]):
|
|
|
172
169
|
case Ok(value) as ok:
|
|
173
170
|
if self.catcher.logging:
|
|
174
171
|
logger.debug(
|
|
175
|
-
"Catcher {!r} returned
|
|
172
|
+
"Catcher {!r} returned: {!r}",
|
|
176
173
|
self.catcher.func.__name__,
|
|
177
174
|
value,
|
|
178
175
|
)
|
|
@@ -18,7 +18,6 @@ from .html import (
|
|
|
18
18
|
strike,
|
|
19
19
|
tg_emoji,
|
|
20
20
|
underline,
|
|
21
|
-
user_open_message,
|
|
22
21
|
)
|
|
23
22
|
from .links import (
|
|
24
23
|
get_channel_boost_link,
|
|
@@ -27,7 +26,6 @@ from .links import (
|
|
|
27
26
|
get_resolve_domain_link,
|
|
28
27
|
get_start_bot_link,
|
|
29
28
|
get_start_group_link,
|
|
30
|
-
user_open_message_link,
|
|
31
29
|
)
|
|
32
30
|
from .spec_html_formats import (
|
|
33
31
|
BaseSpecFormat,
|
|
@@ -41,7 +39,6 @@ from .spec_html_formats import (
|
|
|
41
39
|
StartBotLink,
|
|
42
40
|
StartGroupLink,
|
|
43
41
|
TgEmoji,
|
|
44
|
-
UserOpenMessage,
|
|
45
42
|
)
|
|
46
43
|
|
|
47
44
|
__all__ = (
|
|
@@ -58,7 +55,6 @@ __all__ = (
|
|
|
58
55
|
"StartBotLink",
|
|
59
56
|
"StartGroupLink",
|
|
60
57
|
"TgEmoji",
|
|
61
|
-
"UserOpenMessage",
|
|
62
58
|
"block_quote",
|
|
63
59
|
"bold",
|
|
64
60
|
"channel_boost_link",
|
|
@@ -82,6 +78,4 @@ __all__ = (
|
|
|
82
78
|
"strike",
|
|
83
79
|
"tg_emoji",
|
|
84
80
|
"underline",
|
|
85
|
-
"user_open_message",
|
|
86
|
-
"user_open_message_link",
|
|
87
81
|
)
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# NOTE: NEED REFACTORING
|
|
2
|
+
|
|
1
3
|
import html
|
|
2
4
|
import string
|
|
3
5
|
import typing
|
|
@@ -13,7 +15,6 @@ from .links import (
|
|
|
13
15
|
get_resolve_domain_link,
|
|
14
16
|
get_start_bot_link,
|
|
15
17
|
get_start_group_link,
|
|
16
|
-
user_open_message_link,
|
|
17
18
|
)
|
|
18
19
|
from .spec_html_formats import SpecialFormat, is_spec_format
|
|
19
20
|
|
|
@@ -21,6 +22,12 @@ TAG_FORMAT = "<{tag}{data}>{content}</{tag}>"
|
|
|
21
22
|
QUOT_MARK = '"'
|
|
22
23
|
|
|
23
24
|
|
|
25
|
+
class StringFormatterProto(typing.Protocol):
|
|
26
|
+
def format_field(self, value: typing.Any, fmt: str) -> "HTMLFormatter": ...
|
|
27
|
+
|
|
28
|
+
def format(self, __string: str, *args: object, **kwargs: object) -> "HTMLFormatter": ...
|
|
29
|
+
|
|
30
|
+
|
|
24
31
|
class StringFormatter(string.Formatter):
|
|
25
32
|
"""String formatter, using substitutions from args and kwargs.
|
|
26
33
|
The substitutions are identified by braces ('{' and '}') with
|
|
@@ -107,7 +114,7 @@ class StringFormatter(string.Formatter):
|
|
|
107
114
|
|
|
108
115
|
|
|
109
116
|
class FormatString(str):
|
|
110
|
-
|
|
117
|
+
STRING_FORMATTER: StringFormatterProto = StringFormatter()
|
|
111
118
|
|
|
112
119
|
def __new__(cls, string: str) -> typing.Self:
|
|
113
120
|
if isinstance(string, TagFormat):
|
|
@@ -131,7 +138,7 @@ class FormatString(str):
|
|
|
131
138
|
return self.__str__()
|
|
132
139
|
|
|
133
140
|
def format(self, *args: object, **kwargs: object) -> "HTMLFormatter":
|
|
134
|
-
return self.
|
|
141
|
+
return self.STRING_FORMATTER.format(self, *args, **kwargs)
|
|
135
142
|
|
|
136
143
|
|
|
137
144
|
class EscapedString(FormatString):
|
|
@@ -273,14 +280,6 @@ def underline(string: str) -> TagFormat:
|
|
|
273
280
|
return TagFormat(string, tag="u")
|
|
274
281
|
|
|
275
282
|
|
|
276
|
-
def user_open_message(
|
|
277
|
-
user_id: int,
|
|
278
|
-
message: str | None = None,
|
|
279
|
-
string: str | None = None,
|
|
280
|
-
) -> TagFormat:
|
|
281
|
-
return link(user_open_message_link(user_id, message), string)
|
|
282
|
-
|
|
283
|
-
|
|
284
283
|
__all__ = (
|
|
285
284
|
"FormatString",
|
|
286
285
|
"HTMLFormatter",
|
|
@@ -308,5 +307,4 @@ __all__ = (
|
|
|
308
307
|
"strike",
|
|
309
308
|
"tg_emoji",
|
|
310
309
|
"underline",
|
|
311
|
-
"user_open_message",
|
|
312
310
|
)
|
|
@@ -28,10 +28,6 @@ def get_invite_chat_link(invite_link: str) -> str:
|
|
|
28
28
|
return f"tg://join?invite={invite_link}"
|
|
29
29
|
|
|
30
30
|
|
|
31
|
-
def user_open_message_link(user_id: int, message: str | None = None) -> str:
|
|
32
|
-
return f"tg://openmessage?user_id={user_id}" + ("" if not message else f"&msg?text={message}")
|
|
33
|
-
|
|
34
|
-
|
|
35
31
|
__all__ = (
|
|
36
32
|
"get_channel_boost_link",
|
|
37
33
|
"get_invite_chat_link",
|
|
@@ -39,5 +35,4 @@ __all__ = (
|
|
|
39
35
|
"get_resolve_domain_link",
|
|
40
36
|
"get_start_bot_link",
|
|
41
37
|
"get_start_group_link",
|
|
42
|
-
"user_open_message_link",
|
|
43
38
|
)
|
|
@@ -13,7 +13,6 @@ SpecialFormat: typing.TypeAlias = typing.Union[
|
|
|
13
13
|
"StartBotLink",
|
|
14
14
|
"StartGroupLink",
|
|
15
15
|
"TgEmoji",
|
|
16
|
-
"UserOpenMessage",
|
|
17
16
|
]
|
|
18
17
|
|
|
19
18
|
|
|
@@ -107,15 +106,6 @@ class ResolveDomain(BaseSpecFormat):
|
|
|
107
106
|
string: str | None = None
|
|
108
107
|
|
|
109
108
|
|
|
110
|
-
@dataclasses.dataclass(repr=False)
|
|
111
|
-
class UserOpenMessage(BaseSpecFormat):
|
|
112
|
-
__formatter_name__ = "user_open_message"
|
|
113
|
-
|
|
114
|
-
user_id: int
|
|
115
|
-
message: str | None = None
|
|
116
|
-
string: str | None = None
|
|
117
|
-
|
|
118
|
-
|
|
119
109
|
__all__ = (
|
|
120
110
|
"BaseSpecFormat",
|
|
121
111
|
"ChannelBoostLink",
|
|
@@ -128,5 +118,4 @@ __all__ = (
|
|
|
128
118
|
"StartBotLink",
|
|
129
119
|
"StartGroupLink",
|
|
130
120
|
"TgEmoji",
|
|
131
|
-
"UserOpenMessage",
|
|
132
121
|
)
|
|
@@ -35,9 +35,7 @@ class GlobalCtxVar(typing.Generic[T]):
|
|
|
35
35
|
|
|
36
36
|
@classmethod
|
|
37
37
|
def collect(cls, name: str, ctx_value: T | CtxVariable[T]) -> typing.Self:
|
|
38
|
-
ctx_value = (
|
|
39
|
-
CtxVar(ctx_value) if not isinstance(ctx_value, CtxVar | GlobalCtxVar) else ctx_value
|
|
40
|
-
)
|
|
38
|
+
ctx_value = CtxVar(ctx_value) if not isinstance(ctx_value, CtxVar | GlobalCtxVar) else ctx_value
|
|
41
39
|
params = ctx_value.__dict__
|
|
42
40
|
params["name"] = name
|
|
43
41
|
return cls(**params)
|
|
@@ -84,7 +84,6 @@ def ctx_var(value: T, *, const: bool = False) -> T:
|
|
|
84
84
|
@dataclasses.dataclass(frozen=True, eq=False)
|
|
85
85
|
class RootAttr:
|
|
86
86
|
name: str
|
|
87
|
-
_: dataclasses.KW_ONLY
|
|
88
87
|
can_be_read: bool = dataclasses.field(default=True, kw_only=True)
|
|
89
88
|
can_be_rewritten: bool = dataclasses.field(default=False, kw_only=True)
|
|
90
89
|
|
|
@@ -124,9 +123,7 @@ class Storage:
|
|
|
124
123
|
order_default=True,
|
|
125
124
|
field_specifiers=(ctx_var,),
|
|
126
125
|
)
|
|
127
|
-
class GlobalContext(
|
|
128
|
-
ABCGlobalContext, typing.Generic[CtxValueT], dict[str, GlobalCtxVar[CtxValueT]]
|
|
129
|
-
):
|
|
126
|
+
class GlobalContext(ABCGlobalContext, typing.Generic[CtxValueT], dict[str, GlobalCtxVar[CtxValueT]]):
|
|
130
127
|
"""GlobalContext.
|
|
131
128
|
|
|
132
129
|
```
|
|
@@ -315,10 +312,10 @@ class GlobalContext(
|
|
|
315
312
|
var_value_type: type[T],
|
|
316
313
|
) -> Option[GlobalCtxVar[T]]: ...
|
|
317
314
|
|
|
318
|
-
def pop(self, var_name: str, var_value_type
|
|
315
|
+
def pop(self, var_name: str, var_value_type=object): # type: ignore
|
|
319
316
|
"""Pop context variable by name."""
|
|
320
317
|
|
|
321
|
-
val = self.get(var_name, var_value_type)
|
|
318
|
+
val = self.get(var_name, var_value_type) # type: ignore
|
|
322
319
|
if val:
|
|
323
320
|
del self[var_name]
|
|
324
321
|
return val
|
|
@@ -334,13 +331,10 @@ class GlobalContext(
|
|
|
334
331
|
var_value_type: type[T],
|
|
335
332
|
) -> Option[GlobalCtxVar[T]]: ...
|
|
336
333
|
|
|
337
|
-
def get(
|
|
338
|
-
self,
|
|
339
|
-
var_name: str,
|
|
340
|
-
var_value_type: type[T] = object,
|
|
341
|
-
) -> Option[GlobalCtxVar[T]]:
|
|
334
|
+
def get(self, var_name, var_value_type=object): # type: ignore
|
|
342
335
|
"""Get context variable by name."""
|
|
343
336
|
|
|
337
|
+
var_value_type = typing.Any if var_value_type is object else type
|
|
344
338
|
generic_types = typing.get_args(get_orig_class(self))
|
|
345
339
|
if generic_types and var_value_type is object:
|
|
346
340
|
var_value_type = generic_types[0]
|
|
@@ -369,11 +363,7 @@ class GlobalContext(
|
|
|
369
363
|
var_value_type: type[T],
|
|
370
364
|
) -> Option[T]: ...
|
|
371
365
|
|
|
372
|
-
def get_value(
|
|
373
|
-
self,
|
|
374
|
-
var_name: str,
|
|
375
|
-
var_value_type: type[T] = object,
|
|
376
|
-
) -> Option[T]:
|
|
366
|
+
def get_value(self, var_name, var_value_type=object): # type: ignore
|
|
377
367
|
"""Get context variable value by name."""
|
|
378
368
|
|
|
379
369
|
return self.get(var_name, var_value_type).map(lambda var: var.value)
|
telegrinder/tools/i18n/simple.py
CHANGED
|
@@ -29,9 +29,7 @@ class SimpleI18n(ABCI18n):
|
|
|
29
29
|
return result
|
|
30
30
|
|
|
31
31
|
def get_translator_by_locale(self, locale: str) -> "SimpleTranslator":
|
|
32
|
-
return SimpleTranslator(
|
|
33
|
-
locale, self.translators.get(locale, self.translators[self.default_locale])
|
|
34
|
-
)
|
|
32
|
+
return SimpleTranslator(locale, self.translators.get(locale, self.translators[self.default_locale]))
|
|
35
33
|
|
|
36
34
|
|
|
37
35
|
class SimpleTranslator(ABCTranslator):
|
telegrinder/tools/kb_set/yaml.py
CHANGED
|
@@ -44,8 +44,7 @@ class KeyboardSetYAML(KeyboardSetBase):
|
|
|
44
44
|
or not isinstance(kb_config["buttons"], list)
|
|
45
45
|
):
|
|
46
46
|
raise KeyboardSetError(
|
|
47
|
-
"Keyboard should be dict with field buttons which must be a list, "
|
|
48
|
-
"check documentation."
|
|
47
|
+
"Keyboard should be dict with field buttons which must be a list, " "check documentation."
|
|
49
48
|
)
|
|
50
49
|
|
|
51
50
|
buttons = kb_config.pop("buttons")
|
telegrinder/tools/keyboard.py
CHANGED
|
@@ -5,7 +5,6 @@ from types import NoneType
|
|
|
5
5
|
|
|
6
6
|
from fntypes.option import Nothing, Some
|
|
7
7
|
|
|
8
|
-
from telegrinder.msgspec_utils import Option
|
|
9
8
|
from telegrinder.types.objects import (
|
|
10
9
|
InlineKeyboardMarkup,
|
|
11
10
|
ReplyKeyboardMarkup,
|
|
@@ -18,12 +17,12 @@ DictStrAny: typing.TypeAlias = dict[str, typing.Any]
|
|
|
18
17
|
AnyMarkup: typing.TypeAlias = InlineKeyboardMarkup | ReplyKeyboardMarkup
|
|
19
18
|
|
|
20
19
|
|
|
21
|
-
@dataclasses.dataclass
|
|
20
|
+
@dataclasses.dataclass(kw_only=True)
|
|
22
21
|
class KeyboardModel:
|
|
23
|
-
resize_keyboard: bool
|
|
24
|
-
one_time_keyboard: bool
|
|
25
|
-
selective: bool
|
|
26
|
-
is_persistent: bool
|
|
22
|
+
resize_keyboard: bool
|
|
23
|
+
one_time_keyboard: bool
|
|
24
|
+
selective: bool
|
|
25
|
+
is_persistent: bool
|
|
27
26
|
keyboard: list[list[DictStrAny]]
|
|
28
27
|
|
|
29
28
|
|
|
@@ -40,7 +39,7 @@ class ABCMarkup(ABC, typing.Generic[ButtonT]):
|
|
|
40
39
|
pass
|
|
41
40
|
|
|
42
41
|
@classmethod
|
|
43
|
-
def
|
|
42
|
+
def get_empty_markup(cls) -> AnyMarkup:
|
|
44
43
|
return cls().get_markup()
|
|
45
44
|
|
|
46
45
|
def add(self, row_or_button: RowButtons[ButtonT] | ButtonT) -> typing.Self:
|
|
@@ -102,7 +101,7 @@ class Keyboard(ABCMarkup[Button], KeyboardModel):
|
|
|
102
101
|
def get_markup(self) -> ReplyKeyboardMarkup:
|
|
103
102
|
return ReplyKeyboardMarkup(**self.dict())
|
|
104
103
|
|
|
105
|
-
def
|
|
104
|
+
def keyboard_remove(self, *, selective: bool = False) -> ReplyKeyboardRemove:
|
|
106
105
|
return ReplyKeyboardRemove(remove_keyboard=True, selective=Some(selective))
|
|
107
106
|
|
|
108
107
|
|
|
@@ -48,7 +48,10 @@ class DelayedTask(typing.Generic[CoroFunc]):
|
|
|
48
48
|
await asyncio.sleep(self.seconds)
|
|
49
49
|
if self.is_cancelled:
|
|
50
50
|
break
|
|
51
|
-
|
|
51
|
+
try:
|
|
52
|
+
await self.handler(*args, **kwargs)
|
|
53
|
+
except Exception as e:
|
|
54
|
+
logger.exception("Error in delayed task: {}", str(e))
|
|
52
55
|
if not self.repeat:
|
|
53
56
|
break
|
|
54
57
|
|
|
@@ -63,13 +66,11 @@ class Lifespan:
|
|
|
63
66
|
shutdown_tasks: list[CoroutineTask[typing.Any]] = dataclasses.field(default_factory=lambda: [])
|
|
64
67
|
|
|
65
68
|
def on_startup(self, task_or_func: Task) -> Task:
|
|
66
|
-
|
|
67
|
-
self.startup_tasks.append(task_or_func)
|
|
69
|
+
self.startup_tasks.append(to_coroutine_task(task_or_func))
|
|
68
70
|
return task_or_func
|
|
69
71
|
|
|
70
72
|
def on_shutdown(self, task_or_func: Task) -> Task:
|
|
71
|
-
|
|
72
|
-
self.shutdown_tasks.append(task_or_func)
|
|
73
|
+
self.shutdown_tasks.append(to_coroutine_task(task_or_func))
|
|
73
74
|
return task_or_func
|
|
74
75
|
|
|
75
76
|
def start(self, loop: asyncio.AbstractEventLoop) -> None:
|
telegrinder/tools/magic.py
CHANGED
|
@@ -8,8 +8,11 @@ if typing.TYPE_CHECKING:
|
|
|
8
8
|
|
|
9
9
|
T = typing.TypeVar("T", bound=ABCRule)
|
|
10
10
|
|
|
11
|
+
Impl: typing.TypeAlias = type[classmethod]
|
|
11
12
|
FuncType: typing.TypeAlias = types.FunctionType | typing.Callable[..., typing.Any]
|
|
13
|
+
|
|
12
14
|
TRANSLATIONS_KEY: typing.Final[str] = "_translations"
|
|
15
|
+
IMPL_MARK: typing.Final[str] = "_is_impl"
|
|
13
16
|
|
|
14
17
|
|
|
15
18
|
def resolve_arg_names(func: FuncType, start_idx: int = 1) -> tuple[str, ...]:
|
|
@@ -21,6 +24,14 @@ def get_default_args(func: FuncType) -> dict[str, typing.Any]:
|
|
|
21
24
|
return dict(zip(fspec.args[::-1], (fspec.defaults or ())[::-1]))
|
|
22
25
|
|
|
23
26
|
|
|
27
|
+
def get_annotations(func: FuncType) -> dict[str, typing.Any]:
|
|
28
|
+
return {
|
|
29
|
+
name: parameter.annotation
|
|
30
|
+
for name, parameter in inspect.signature(func).parameters.items()
|
|
31
|
+
if parameter.annotation is not inspect._empty
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
|
|
24
35
|
def to_str(s: str | enum.Enum) -> str:
|
|
25
36
|
if isinstance(s, enum.Enum):
|
|
26
37
|
return str(s.value)
|
|
@@ -42,11 +53,8 @@ def magic_bundle(
|
|
|
42
53
|
return args
|
|
43
54
|
|
|
44
55
|
|
|
45
|
-
def get_cached_translation(rule: "T", locale: str) ->
|
|
46
|
-
|
|
47
|
-
if not translations or locale not in translations:
|
|
48
|
-
return None
|
|
49
|
-
return translations[locale]
|
|
56
|
+
def get_cached_translation(rule: "T", locale: str) -> "T | None":
|
|
57
|
+
return getattr(rule, TRANSLATIONS_KEY, {}).get(locale)
|
|
50
58
|
|
|
51
59
|
|
|
52
60
|
def cache_translation(base_rule: "T", locale: str, translated_rule: "T") -> None:
|
|
@@ -54,6 +62,18 @@ def cache_translation(base_rule: "T", locale: str, translated_rule: "T") -> None
|
|
|
54
62
|
setattr(base_rule, TRANSLATIONS_KEY, {locale: translated_rule, **translations})
|
|
55
63
|
|
|
56
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]
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@typing.cast(typing.Callable[..., Impl], lambda f: f)
|
|
71
|
+
def impl(method: typing.Callable[..., typing.Any]):
|
|
72
|
+
bound_method = classmethod(method)
|
|
73
|
+
setattr(method, IMPL_MARK, True)
|
|
74
|
+
return bound_method
|
|
75
|
+
|
|
76
|
+
|
|
57
77
|
__all__ = (
|
|
58
78
|
"TRANSLATIONS_KEY",
|
|
59
79
|
"cache_translation",
|
|
@@ -61,6 +81,8 @@ __all__ = (
|
|
|
61
81
|
"get_default_args",
|
|
62
82
|
"get_default_args",
|
|
63
83
|
"magic_bundle",
|
|
84
|
+
"impl",
|
|
64
85
|
"resolve_arg_names",
|
|
65
86
|
"to_str",
|
|
87
|
+
"get_annotations",
|
|
66
88
|
)
|
telegrinder/types/__init__.py
CHANGED
|
@@ -136,6 +136,9 @@ __all__ = (
|
|
|
136
136
|
"InputMediaPhoto",
|
|
137
137
|
"InputMediaVideo",
|
|
138
138
|
"InputMessageContent",
|
|
139
|
+
"InputPaidMedia",
|
|
140
|
+
"InputPaidMediaPhoto",
|
|
141
|
+
"InputPaidMediaVideo",
|
|
139
142
|
"InputPollOption",
|
|
140
143
|
"InputSticker",
|
|
141
144
|
"InputTextMessageContent",
|
|
@@ -171,6 +174,11 @@ __all__ = (
|
|
|
171
174
|
"MessageReactionUpdated",
|
|
172
175
|
"Model",
|
|
173
176
|
"OrderInfo",
|
|
177
|
+
"PaidMedia",
|
|
178
|
+
"PaidMediaInfo",
|
|
179
|
+
"PaidMediaPhoto",
|
|
180
|
+
"PaidMediaPreview",
|
|
181
|
+
"PaidMediaVideo",
|
|
174
182
|
"PassportData",
|
|
175
183
|
"PassportElementError",
|
|
176
184
|
"PassportElementErrorDataField",
|
|
@@ -198,15 +206,22 @@ __all__ = (
|
|
|
198
206
|
"ReactionTypeCustomEmoji",
|
|
199
207
|
"ReactionTypeEmoji",
|
|
200
208
|
"ReactionTypeType",
|
|
209
|
+
"RefundedPayment",
|
|
201
210
|
"ReplyKeyboardMarkup",
|
|
202
211
|
"ReplyKeyboardRemove",
|
|
203
212
|
"ReplyParameters",
|
|
204
213
|
"ResponseParameters",
|
|
214
|
+
"RevenueWithdrawalState",
|
|
215
|
+
"RevenueWithdrawalStateFailed",
|
|
216
|
+
"RevenueWithdrawalStatePending",
|
|
217
|
+
"RevenueWithdrawalStateSucceeded",
|
|
205
218
|
"SentWebAppMessage",
|
|
206
219
|
"SharedUser",
|
|
207
220
|
"ShippingAddress",
|
|
208
221
|
"ShippingOption",
|
|
209
222
|
"ShippingQuery",
|
|
223
|
+
"StarTransaction",
|
|
224
|
+
"StarTransactions",
|
|
210
225
|
"Sticker",
|
|
211
226
|
"StickerFormat",
|
|
212
227
|
"StickerSet",
|
|
@@ -217,6 +232,11 @@ __all__ = (
|
|
|
217
232
|
"SwitchInlineQueryChosenChat",
|
|
218
233
|
"TextQuote",
|
|
219
234
|
"TopicIconColor",
|
|
235
|
+
"TransactionPartner",
|
|
236
|
+
"TransactionPartnerFragment",
|
|
237
|
+
"TransactionPartnerOther",
|
|
238
|
+
"TransactionPartnerTelegramAds",
|
|
239
|
+
"TransactionPartnerUser",
|
|
220
240
|
"Update",
|
|
221
241
|
"UpdateType",
|
|
222
242
|
"User",
|