telegrinder 0.4.2__py3-none-any.whl → 0.5.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 +37 -55
- telegrinder/__meta__.py +1 -0
- telegrinder/api/__init__.py +6 -4
- telegrinder/api/api.py +100 -26
- telegrinder/api/error.py +42 -8
- telegrinder/api/response.py +4 -1
- telegrinder/api/token.py +2 -2
- telegrinder/bot/__init__.py +9 -25
- telegrinder/bot/bot.py +31 -25
- telegrinder/bot/cute_types/__init__.py +0 -0
- telegrinder/bot/cute_types/base.py +103 -61
- telegrinder/bot/cute_types/callback_query.py +447 -400
- telegrinder/bot/cute_types/chat_join_request.py +59 -62
- telegrinder/bot/cute_types/chat_member_updated.py +154 -157
- telegrinder/bot/cute_types/inline_query.py +41 -44
- telegrinder/bot/cute_types/message.py +2621 -2590
- telegrinder/bot/cute_types/pre_checkout_query.py +38 -42
- telegrinder/bot/cute_types/update.py +1 -8
- telegrinder/bot/cute_types/utils.py +1 -1
- telegrinder/bot/dispatch/__init__.py +10 -15
- telegrinder/bot/dispatch/abc.py +12 -11
- telegrinder/bot/dispatch/action.py +104 -0
- telegrinder/bot/dispatch/context.py +32 -26
- telegrinder/bot/dispatch/dispatch.py +61 -134
- telegrinder/bot/dispatch/handler/__init__.py +2 -0
- telegrinder/bot/dispatch/handler/abc.py +10 -8
- telegrinder/bot/dispatch/handler/audio_reply.py +2 -3
- telegrinder/bot/dispatch/handler/base.py +10 -33
- telegrinder/bot/dispatch/handler/document_reply.py +2 -3
- telegrinder/bot/dispatch/handler/func.py +55 -87
- telegrinder/bot/dispatch/handler/media_group_reply.py +2 -3
- telegrinder/bot/dispatch/handler/message_reply.py +2 -3
- telegrinder/bot/dispatch/handler/photo_reply.py +2 -3
- telegrinder/bot/dispatch/handler/sticker_reply.py +2 -3
- telegrinder/bot/dispatch/handler/video_reply.py +2 -3
- telegrinder/bot/dispatch/middleware/__init__.py +0 -0
- telegrinder/bot/dispatch/middleware/abc.py +79 -55
- telegrinder/bot/dispatch/middleware/global_middleware.py +18 -33
- telegrinder/bot/dispatch/process.py +84 -105
- telegrinder/bot/dispatch/return_manager/__init__.py +0 -0
- telegrinder/bot/dispatch/return_manager/abc.py +102 -65
- telegrinder/bot/dispatch/return_manager/callback_query.py +4 -5
- telegrinder/bot/dispatch/return_manager/inline_query.py +3 -4
- telegrinder/bot/dispatch/return_manager/message.py +8 -10
- telegrinder/bot/dispatch/return_manager/pre_checkout_query.py +4 -5
- telegrinder/bot/dispatch/view/__init__.py +4 -4
- telegrinder/bot/dispatch/view/abc.py +6 -16
- telegrinder/bot/dispatch/view/base.py +54 -178
- telegrinder/bot/dispatch/view/box.py +19 -18
- telegrinder/bot/dispatch/view/callback_query.py +4 -8
- telegrinder/bot/dispatch/view/chat_join_request.py +5 -6
- telegrinder/bot/dispatch/view/chat_member.py +5 -25
- telegrinder/bot/dispatch/view/error.py +9 -0
- telegrinder/bot/dispatch/view/inline_query.py +4 -8
- telegrinder/bot/dispatch/view/message.py +5 -25
- telegrinder/bot/dispatch/view/pre_checkout_query.py +4 -8
- telegrinder/bot/dispatch/view/raw.py +3 -109
- telegrinder/bot/dispatch/waiter_machine/__init__.py +2 -5
- telegrinder/bot/dispatch/waiter_machine/actions.py +6 -4
- telegrinder/bot/dispatch/waiter_machine/hasher/__init__.py +1 -3
- telegrinder/bot/dispatch/waiter_machine/hasher/callback.py +1 -1
- telegrinder/bot/dispatch/waiter_machine/hasher/hasher.py +11 -7
- telegrinder/bot/dispatch/waiter_machine/hasher/message.py +0 -0
- telegrinder/bot/dispatch/waiter_machine/machine.py +43 -60
- telegrinder/bot/dispatch/waiter_machine/middleware.py +19 -23
- telegrinder/bot/dispatch/waiter_machine/short_state.py +6 -5
- telegrinder/bot/polling/__init__.py +0 -0
- telegrinder/bot/polling/abc.py +0 -0
- telegrinder/bot/polling/polling.py +209 -88
- telegrinder/bot/rules/__init__.py +3 -16
- telegrinder/bot/rules/abc.py +42 -122
- telegrinder/bot/rules/callback_data.py +29 -49
- telegrinder/bot/rules/chat_join.py +5 -23
- telegrinder/bot/rules/command.py +8 -4
- telegrinder/bot/rules/enum_text.py +3 -4
- telegrinder/bot/rules/func.py +7 -14
- telegrinder/bot/rules/fuzzy.py +3 -4
- telegrinder/bot/rules/inline.py +8 -20
- telegrinder/bot/rules/integer.py +2 -3
- telegrinder/bot/rules/is_from.py +12 -11
- telegrinder/bot/rules/logic.py +11 -5
- telegrinder/bot/rules/markup.py +22 -14
- telegrinder/bot/rules/mention.py +8 -7
- telegrinder/bot/rules/message_entities.py +8 -4
- telegrinder/bot/rules/node.py +23 -12
- telegrinder/bot/rules/payload.py +5 -4
- telegrinder/bot/rules/payment_invoice.py +6 -21
- telegrinder/bot/rules/regex.py +2 -4
- telegrinder/bot/rules/rule_enum.py +8 -7
- telegrinder/bot/rules/start.py +5 -6
- telegrinder/bot/rules/state.py +1 -1
- telegrinder/bot/rules/text.py +4 -15
- telegrinder/bot/rules/update.py +3 -4
- telegrinder/bot/scenario/__init__.py +0 -0
- telegrinder/bot/scenario/abc.py +6 -5
- telegrinder/bot/scenario/checkbox.py +1 -1
- telegrinder/bot/scenario/choice.py +30 -39
- telegrinder/client/__init__.py +3 -5
- telegrinder/client/abc.py +11 -6
- telegrinder/client/aiohttp.py +141 -27
- telegrinder/client/form_data.py +1 -1
- telegrinder/model.py +61 -89
- telegrinder/modules.py +325 -102
- telegrinder/msgspec_utils/__init__.py +40 -0
- telegrinder/msgspec_utils/abc.py +18 -0
- telegrinder/msgspec_utils/custom_types/__init__.py +6 -0
- telegrinder/msgspec_utils/custom_types/datetime.py +24 -0
- telegrinder/msgspec_utils/custom_types/enum_meta.py +43 -0
- telegrinder/msgspec_utils/custom_types/literal.py +25 -0
- telegrinder/msgspec_utils/custom_types/option.py +17 -0
- telegrinder/msgspec_utils/decoder.py +389 -0
- telegrinder/msgspec_utils/encoder.py +206 -0
- telegrinder/{msgspec_json.py → msgspec_utils/json.py} +6 -5
- telegrinder/msgspec_utils/tools.py +75 -0
- telegrinder/node/__init__.py +24 -7
- telegrinder/node/attachment.py +1 -0
- telegrinder/node/base.py +154 -72
- telegrinder/node/callback_query.py +5 -5
- telegrinder/node/collection.py +39 -0
- telegrinder/node/command.py +1 -2
- telegrinder/node/composer.py +121 -72
- telegrinder/node/container.py +11 -8
- telegrinder/node/context.py +48 -0
- telegrinder/node/either.py +27 -40
- telegrinder/node/error.py +41 -0
- telegrinder/node/event.py +37 -11
- telegrinder/node/exceptions.py +7 -0
- telegrinder/node/file.py +0 -0
- telegrinder/node/i18n.py +108 -0
- telegrinder/node/me.py +3 -2
- telegrinder/node/payload.py +1 -1
- telegrinder/node/polymorphic.py +63 -28
- telegrinder/node/reply_message.py +12 -0
- telegrinder/node/rule.py +6 -13
- telegrinder/node/scope.py +14 -5
- telegrinder/node/session.py +53 -0
- telegrinder/node/source.py +41 -9
- telegrinder/node/text.py +1 -2
- telegrinder/node/tools/__init__.py +0 -0
- telegrinder/node/tools/generator.py +3 -5
- telegrinder/node/utility.py +16 -0
- telegrinder/py.typed +0 -0
- telegrinder/rules.py +0 -0
- telegrinder/tools/__init__.py +48 -88
- telegrinder/tools/aio.py +103 -0
- telegrinder/tools/callback_data_serialization/__init__.py +5 -0
- telegrinder/tools/{callback_data_serilization → callback_data_serialization}/abc.py +0 -0
- telegrinder/tools/{callback_data_serilization → callback_data_serialization}/json_ser.py +2 -3
- telegrinder/tools/{callback_data_serilization → callback_data_serialization}/msgpack_ser.py +45 -27
- telegrinder/tools/final.py +21 -0
- telegrinder/tools/formatting/__init__.py +2 -18
- telegrinder/tools/formatting/deep_links/__init__.py +39 -0
- telegrinder/tools/formatting/{deep_links.py → deep_links/links.py} +12 -85
- telegrinder/tools/formatting/deep_links/parsing.py +90 -0
- telegrinder/tools/formatting/deep_links/validators.py +8 -0
- telegrinder/tools/formatting/html_formatter.py +18 -45
- telegrinder/tools/fullname.py +83 -0
- telegrinder/tools/global_context/__init__.py +4 -3
- telegrinder/tools/global_context/abc.py +17 -14
- telegrinder/tools/global_context/builtin_context.py +39 -0
- telegrinder/tools/global_context/global_context.py +138 -39
- telegrinder/tools/input_file_directory.py +0 -0
- telegrinder/tools/keyboard/__init__.py +39 -0
- telegrinder/tools/keyboard/abc.py +159 -0
- telegrinder/tools/keyboard/base.py +77 -0
- telegrinder/tools/keyboard/buttons/__init__.py +14 -0
- telegrinder/tools/keyboard/buttons/base.py +18 -0
- telegrinder/tools/{buttons.py → keyboard/buttons/buttons.py} +71 -23
- telegrinder/tools/keyboard/buttons/static_buttons.py +56 -0
- telegrinder/tools/keyboard/buttons/tools.py +18 -0
- telegrinder/tools/keyboard/data.py +20 -0
- telegrinder/tools/keyboard/keyboard.py +131 -0
- telegrinder/tools/keyboard/static_keyboard.py +83 -0
- telegrinder/tools/lifespan.py +87 -51
- telegrinder/tools/limited_dict.py +4 -1
- telegrinder/tools/loop_wrapper.py +332 -0
- telegrinder/tools/magic/__init__.py +32 -0
- telegrinder/tools/magic/annotations.py +165 -0
- telegrinder/tools/magic/dictionary.py +20 -0
- telegrinder/tools/magic/function.py +246 -0
- telegrinder/tools/magic/shortcut.py +111 -0
- telegrinder/tools/parse_mode.py +9 -3
- telegrinder/tools/singleton/__init__.py +4 -0
- telegrinder/tools/singleton/abc.py +14 -0
- telegrinder/tools/singleton/singleton.py +18 -0
- telegrinder/tools/state_storage/__init__.py +0 -0
- telegrinder/tools/state_storage/abc.py +6 -1
- telegrinder/tools/state_storage/memory.py +1 -1
- telegrinder/tools/strings.py +0 -0
- telegrinder/types/__init__.py +307 -268
- telegrinder/types/enums.py +64 -37
- telegrinder/types/input_file.py +3 -3
- telegrinder/types/methods.py +5699 -5055
- telegrinder/types/methods_utils.py +62 -0
- telegrinder/types/objects.py +7846 -7058
- telegrinder/verification_utils.py +3 -1
- telegrinder-0.5.0.dist-info/METADATA +162 -0
- telegrinder-0.5.0.dist-info/RECORD +200 -0
- {telegrinder-0.4.2.dist-info → telegrinder-0.5.0.dist-info}/licenses/LICENSE +2 -2
- telegrinder/bot/dispatch/waiter_machine/hasher/state.py +0 -20
- telegrinder/bot/rules/id.py +0 -24
- telegrinder/bot/rules/message.py +0 -15
- telegrinder/client/sonic.py +0 -212
- telegrinder/msgspec_utils.py +0 -478
- telegrinder/tools/adapter/__init__.py +0 -19
- telegrinder/tools/adapter/abc.py +0 -49
- telegrinder/tools/adapter/dataclass.py +0 -56
- telegrinder/tools/adapter/errors.py +0 -5
- telegrinder/tools/adapter/event.py +0 -61
- telegrinder/tools/adapter/node.py +0 -46
- telegrinder/tools/adapter/raw_event.py +0 -27
- telegrinder/tools/adapter/raw_update.py +0 -30
- telegrinder/tools/callback_data_serilization/__init__.py +0 -5
- telegrinder/tools/error_handler/__init__.py +0 -10
- telegrinder/tools/error_handler/abc.py +0 -30
- telegrinder/tools/error_handler/error.py +0 -9
- telegrinder/tools/error_handler/error_handler.py +0 -179
- telegrinder/tools/formatting/spec_html_formats.py +0 -75
- telegrinder/tools/functional.py +0 -8
- telegrinder/tools/global_context/telegrinder_ctx.py +0 -27
- telegrinder/tools/i18n/__init__.py +0 -12
- telegrinder/tools/i18n/abc.py +0 -32
- telegrinder/tools/i18n/middleware/__init__.py +0 -3
- telegrinder/tools/i18n/middleware/abc.py +0 -22
- telegrinder/tools/i18n/simple.py +0 -43
- telegrinder/tools/keyboard.py +0 -132
- telegrinder/tools/loop_wrapper/__init__.py +0 -4
- telegrinder/tools/loop_wrapper/abc.py +0 -20
- telegrinder/tools/loop_wrapper/loop_wrapper.py +0 -169
- telegrinder/tools/magic.py +0 -344
- telegrinder-0.4.2.dist-info/METADATA +0 -151
- telegrinder-0.4.2.dist-info/RECORD +0 -182
- {telegrinder-0.4.2.dist-info → telegrinder-0.5.0.dist-info}/WHEEL +0 -0
telegrinder/bot/rules/abc.py
CHANGED
|
@@ -1,52 +1,21 @@
|
|
|
1
|
-
import inspect
|
|
2
1
|
from abc import ABC, abstractmethod
|
|
2
|
+
from collections import deque
|
|
3
3
|
from functools import cached_property
|
|
4
4
|
|
|
5
5
|
import typing_extensions as typing
|
|
6
6
|
|
|
7
|
-
from telegrinder.
|
|
7
|
+
from telegrinder.api.api import API
|
|
8
8
|
from telegrinder.bot.dispatch.context import Context
|
|
9
9
|
from telegrinder.bot.dispatch.process import check_rule
|
|
10
|
-
from telegrinder.node.base import
|
|
11
|
-
from telegrinder.tools.
|
|
12
|
-
from telegrinder.
|
|
13
|
-
from telegrinder.tools.adapter.raw_update import RawUpdateAdapter
|
|
14
|
-
from telegrinder.tools.i18n.abc import ABCTranslator
|
|
15
|
-
from telegrinder.tools.magic import (
|
|
16
|
-
cache_translation,
|
|
17
|
-
get_annotations,
|
|
18
|
-
get_cached_translation,
|
|
19
|
-
get_default_args,
|
|
20
|
-
)
|
|
21
|
-
from telegrinder.types.objects import Update as UpdateObject
|
|
22
|
-
|
|
23
|
-
if typing.TYPE_CHECKING:
|
|
24
|
-
from telegrinder.node.composer import NodeCollection
|
|
25
|
-
|
|
26
|
-
AdaptTo = typing.TypeVar("AdaptTo", default=typing.Any, contravariant=True)
|
|
10
|
+
from telegrinder.node.base import IsNode, get_nodes
|
|
11
|
+
from telegrinder.tools.fullname import fullname
|
|
12
|
+
from telegrinder.types.objects import Update
|
|
27
13
|
|
|
28
14
|
type CheckResult = bool | typing.Awaitable[bool]
|
|
29
15
|
|
|
30
|
-
Message: typing.TypeAlias = MessageCute
|
|
31
|
-
Update: typing.TypeAlias = UpdateCute
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
def with_caching_translations(func: typing.Callable[..., typing.Any]):
|
|
35
|
-
"""Should be used as decorator for .translate method. Caches rule translations."""
|
|
36
16
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
return translation
|
|
40
|
-
translation = await func(self, translator)
|
|
41
|
-
cache_translation(self, translator.locale, translation)
|
|
42
|
-
return translation
|
|
43
|
-
|
|
44
|
-
return wrapper
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
class ABCRule(ABC, typing.Generic[AdaptTo]):
|
|
48
|
-
adapter: ABCAdapter[UpdateObject, AdaptTo]
|
|
49
|
-
requires: list["ABCRule"] = []
|
|
17
|
+
class ABCRule(ABC):
|
|
18
|
+
requires: deque["ABCRule"] = deque()
|
|
50
19
|
|
|
51
20
|
if typing.TYPE_CHECKING:
|
|
52
21
|
|
|
@@ -54,7 +23,6 @@ class ABCRule(ABC, typing.Generic[AdaptTo]):
|
|
|
54
23
|
def check(self, *args: typing.Any, **kwargs: typing.Any) -> CheckResult:
|
|
55
24
|
pass
|
|
56
25
|
else:
|
|
57
|
-
adapter = RawUpdateAdapter()
|
|
58
26
|
|
|
59
27
|
@abstractmethod
|
|
60
28
|
def check(self, *args, **kwargs):
|
|
@@ -63,59 +31,41 @@ class ABCRule(ABC, typing.Generic[AdaptTo]):
|
|
|
63
31
|
def __init_subclass__(
|
|
64
32
|
cls,
|
|
65
33
|
*,
|
|
66
|
-
requires:
|
|
67
|
-
adapter: ABCAdapter[UpdateObject, AdaptTo] | None = None,
|
|
34
|
+
requires: typing.Iterable["ABCRule"] | None = None,
|
|
68
35
|
) -> None:
|
|
69
36
|
"""Merges requirements from inherited classes and rule-specific requirements."""
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
requirements = []
|
|
74
|
-
for base in inspect.getmro(cls):
|
|
37
|
+
requirements = list[ABCRule]()
|
|
38
|
+
for base in cls.__mro__:
|
|
75
39
|
if issubclass(base, ABCRule) and base != cls:
|
|
76
|
-
requirements.extend(base.requires or ())
|
|
40
|
+
requirements.extend(base.requires or ())
|
|
77
41
|
|
|
78
42
|
requirements.extend(requires or ())
|
|
79
|
-
cls.requires =
|
|
80
|
-
|
|
81
|
-
def __and__(self, other: "ABCRule") -> "AndRule":
|
|
82
|
-
"""And Rule.
|
|
43
|
+
cls.requires = deque(dict.fromkeys(requirements))
|
|
83
44
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
```
|
|
88
|
-
"""
|
|
45
|
+
def __and__(self, other: object, /) -> "AndRule":
|
|
46
|
+
if not isinstance(other, ABCRule):
|
|
47
|
+
return NotImplemented
|
|
89
48
|
return AndRule(self, other)
|
|
90
49
|
|
|
91
|
-
def
|
|
92
|
-
|
|
50
|
+
def __iadd__(self, other: object, /) -> "AndRule":
|
|
51
|
+
return self.__and__(other)
|
|
93
52
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
```
|
|
98
|
-
"""
|
|
53
|
+
def __or__(self, other: object, /) -> "OrRule":
|
|
54
|
+
if not isinstance(other, ABCRule):
|
|
55
|
+
return NotImplemented
|
|
99
56
|
return OrRule(self, other)
|
|
100
57
|
|
|
101
|
-
def
|
|
102
|
-
|
|
58
|
+
def __ior__(self, other: object, /) -> "OrRule":
|
|
59
|
+
return self.__or__(other)
|
|
103
60
|
|
|
104
|
-
|
|
105
|
-
rule = ~HasText()
|
|
106
|
-
rule # NotRule(HasText()) -> True if rule returned False, otherwise False.
|
|
107
|
-
```
|
|
108
|
-
"""
|
|
61
|
+
def __invert__(self) -> "NotRule":
|
|
109
62
|
return NotRule(self)
|
|
110
63
|
|
|
111
64
|
def __repr__(self) -> str:
|
|
112
|
-
return "<{}
|
|
113
|
-
self.__class__.__name__,
|
|
114
|
-
self.adapter,
|
|
115
|
-
)
|
|
65
|
+
return "<{}, requires={!r}>".format(fullname(self), self.requires)
|
|
116
66
|
|
|
117
67
|
@cached_property
|
|
118
|
-
def required_nodes(self) -> dict[str,
|
|
68
|
+
def required_nodes(self) -> dict[str, IsNode]:
|
|
119
69
|
return get_nodes(self.check)
|
|
120
70
|
|
|
121
71
|
def as_optional(self) -> "ABCRule":
|
|
@@ -124,54 +74,18 @@ class ABCRule(ABC, typing.Generic[AdaptTo]):
|
|
|
124
74
|
def should_fail(self) -> "ABCRule":
|
|
125
75
|
return self & Never()
|
|
126
76
|
|
|
127
|
-
async def bounding_check(
|
|
128
|
-
self,
|
|
129
|
-
ctx: Context,
|
|
130
|
-
*,
|
|
131
|
-
adapted_value: AdaptTo,
|
|
132
|
-
node_col: "NodeCollection | None" = None,
|
|
133
|
-
) -> bool:
|
|
134
|
-
bound_check_rule = self.check
|
|
135
|
-
kw = {}
|
|
136
|
-
node_col_values = node_col.values if node_col is not None else {}
|
|
137
|
-
temp_ctx = get_default_args(bound_check_rule) | ctx
|
|
138
|
-
|
|
139
|
-
for i, (k, v) in enumerate(get_annotations(bound_check_rule).items()):
|
|
140
|
-
if (isinstance(adapted_value, Event) and i == 0) or ( # First arg is Event
|
|
141
|
-
isinstance(v, type) and isinstance(adapted_value, v)
|
|
142
|
-
):
|
|
143
|
-
kw[k] = adapted_value if not isinstance(adapted_value, Event) else adapted_value.obj
|
|
144
|
-
elif is_node(v):
|
|
145
|
-
assert k in node_col_values, "Node is undefined, error while bounding."
|
|
146
|
-
kw[k] = node_col_values[k]
|
|
147
|
-
elif k in temp_ctx:
|
|
148
|
-
kw[k] = temp_ctx[k]
|
|
149
|
-
elif v is Context:
|
|
150
|
-
kw[k] = ctx
|
|
151
|
-
else:
|
|
152
|
-
raise LookupError(
|
|
153
|
-
f"Cannot bound {k!r} of type {v!r} to '{self.__class__.__qualname__}.check()', "
|
|
154
|
-
"because it cannot be resolved."
|
|
155
|
-
)
|
|
156
|
-
|
|
157
|
-
result = bound_check_rule(**kw) # type: ignore
|
|
158
|
-
if inspect.isawaitable(result):
|
|
159
|
-
result = await result
|
|
160
|
-
return result
|
|
161
|
-
|
|
162
|
-
async def translate(self, translator: ABCTranslator) -> typing.Self:
|
|
163
|
-
return self
|
|
164
|
-
|
|
165
77
|
|
|
166
78
|
class AndRule(ABCRule):
|
|
167
79
|
def __init__(self, *rules: ABCRule) -> None:
|
|
168
80
|
self.rules = rules
|
|
169
81
|
|
|
170
|
-
async def check(self, event: Update, ctx: Context) -> bool:
|
|
82
|
+
async def check(self, event: Update, api: API, ctx: Context) -> bool:
|
|
171
83
|
ctx_copy = ctx.copy()
|
|
84
|
+
|
|
172
85
|
for rule in self.rules:
|
|
173
|
-
if not await check_rule(
|
|
86
|
+
if not await check_rule(api, rule, event, ctx_copy):
|
|
174
87
|
return False
|
|
88
|
+
|
|
175
89
|
ctx |= ctx_copy
|
|
176
90
|
return True
|
|
177
91
|
|
|
@@ -180,12 +94,14 @@ class OrRule(ABCRule):
|
|
|
180
94
|
def __init__(self, *rules: ABCRule) -> None:
|
|
181
95
|
self.rules = rules
|
|
182
96
|
|
|
183
|
-
async def check(self, event: Update, ctx: Context) -> bool:
|
|
97
|
+
async def check(self, event: Update, api: API, ctx: Context) -> bool:
|
|
184
98
|
for rule in self.rules:
|
|
185
99
|
ctx_copy = ctx.copy()
|
|
186
|
-
|
|
100
|
+
|
|
101
|
+
if await check_rule(api, rule, event, ctx_copy):
|
|
187
102
|
ctx |= ctx_copy
|
|
188
103
|
return True
|
|
104
|
+
|
|
189
105
|
return False
|
|
190
106
|
|
|
191
107
|
|
|
@@ -193,18 +109,22 @@ class NotRule(ABCRule):
|
|
|
193
109
|
def __init__(self, rule: ABCRule) -> None:
|
|
194
110
|
self.rule = rule
|
|
195
111
|
|
|
196
|
-
async def check(self, event: Update, ctx: Context) -> bool:
|
|
112
|
+
async def check(self, event: Update, api: API, ctx: Context) -> bool:
|
|
197
113
|
ctx_copy = ctx.copy()
|
|
198
|
-
return not await check_rule(
|
|
114
|
+
return not await check_rule(api, self.rule, event, ctx_copy)
|
|
199
115
|
|
|
200
116
|
|
|
201
117
|
class Never(ABCRule):
|
|
202
|
-
|
|
118
|
+
"""Neutral element for `|` (OrRule)."""
|
|
119
|
+
|
|
120
|
+
def check(self) -> typing.Literal[False]:
|
|
203
121
|
return False
|
|
204
122
|
|
|
205
123
|
|
|
206
124
|
class Always(ABCRule):
|
|
207
|
-
|
|
125
|
+
"""Neutral element for `&` (AndRule)."""
|
|
126
|
+
|
|
127
|
+
def check(self) -> typing.Literal[True]:
|
|
208
128
|
return True
|
|
209
129
|
|
|
210
130
|
|
|
@@ -216,5 +136,5 @@ __all__ = (
|
|
|
216
136
|
"Never",
|
|
217
137
|
"NotRule",
|
|
218
138
|
"OrRule",
|
|
219
|
-
"
|
|
139
|
+
"check_rule",
|
|
220
140
|
)
|
|
@@ -1,76 +1,56 @@
|
|
|
1
1
|
import abc
|
|
2
|
-
import inspect
|
|
3
2
|
import typing
|
|
4
3
|
from contextlib import suppress
|
|
5
4
|
|
|
6
5
|
from telegrinder.bot.cute_types import CallbackQueryCute
|
|
7
6
|
from telegrinder.bot.dispatch.context import Context
|
|
8
|
-
from telegrinder.bot.rules.abc import ABCRule
|
|
7
|
+
from telegrinder.bot.rules.abc import ABCRule
|
|
9
8
|
from telegrinder.bot.rules.payload import (
|
|
10
9
|
PayloadEqRule,
|
|
11
10
|
PayloadJsonEqRule,
|
|
12
11
|
PayloadMarkupRule,
|
|
13
12
|
PayloadModelRule,
|
|
14
13
|
)
|
|
15
|
-
from telegrinder.tools.
|
|
16
|
-
|
|
14
|
+
from telegrinder.tools.aio import maybe_awaitable
|
|
15
|
+
|
|
16
|
+
type Validator = typing.Callable[[typing.Any], bool | typing.Awaitable[bool]]
|
|
17
|
+
type CallbackMap = dict[str, typing.Any | type[typing.Any] | Validator | CallbackMap]
|
|
18
|
+
type CallbackMapStrict = dict[str, Validator | CallbackMapStrict]
|
|
17
19
|
|
|
18
20
|
CallbackQuery: typing.TypeAlias = CallbackQueryCute
|
|
19
|
-
Validator: typing.TypeAlias = typing.Callable[[typing.Any], bool | typing.Awaitable[bool]]
|
|
20
|
-
MapDict: typing.TypeAlias = dict[str, "typing.Any | type[typing.Any] | Validator | list[MapDict] | MapDict"]
|
|
21
|
-
CallbackMap: typing.TypeAlias = list[tuple[str, "typing.Any | type[typing.Any] | Validator | CallbackMap"]]
|
|
22
|
-
CallbackMapStrict: typing.TypeAlias = list[tuple[str, "Validator | CallbackMapStrict"]]
|
|
23
21
|
CallbackDataEq: typing.TypeAlias = PayloadEqRule
|
|
24
22
|
CallbackDataJsonEq: typing.TypeAlias = PayloadJsonEqRule
|
|
25
23
|
CallbackDataMarkup: typing.TypeAlias = PayloadMarkupRule
|
|
26
24
|
CallbackDataJsonModel: typing.TypeAlias = PayloadModelRule
|
|
27
25
|
|
|
28
26
|
|
|
29
|
-
class
|
|
30
|
-
ABCRule[CallbackQuery],
|
|
31
|
-
abc.ABC,
|
|
32
|
-
adapter=EventAdapter(UpdateType.CALLBACK_QUERY, CallbackQuery),
|
|
33
|
-
):
|
|
34
|
-
@abc.abstractmethod
|
|
35
|
-
def check(self, *args: typing.Any, **kwargs: typing.Any) -> CheckResult: ...
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
class HasData(CallbackQueryRule):
|
|
27
|
+
class HasData(ABCRule):
|
|
39
28
|
def check(self, event: CallbackQuery) -> bool:
|
|
40
29
|
return bool(event.data)
|
|
41
30
|
|
|
42
31
|
|
|
43
|
-
class CallbackQueryDataRule(
|
|
32
|
+
class CallbackQueryDataRule(ABCRule, abc.ABC, requires=[HasData()]):
|
|
44
33
|
pass
|
|
45
34
|
|
|
46
35
|
|
|
47
36
|
class CallbackDataMap(CallbackQueryDataRule):
|
|
48
|
-
def __init__(self, mapping:
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
"""Transforms MapDict to CallbackMap."""
|
|
56
|
-
callback_map = []
|
|
57
|
-
|
|
58
|
-
for k, v in mapping.items():
|
|
59
|
-
if isinstance(v, dict):
|
|
60
|
-
v = cls.transform_to_map(v)
|
|
61
|
-
callback_map.append((k, v))
|
|
62
|
-
|
|
63
|
-
return callback_map
|
|
37
|
+
def __init__(self, mapping: CallbackMap, /, *, allow_extra_fields: bool = False) -> None:
|
|
38
|
+
"""Callback data map validation.
|
|
39
|
+
:param mapping: A callback data mapping with validators.
|
|
40
|
+
:param allow_extra_fields: Allows extra fields in a callback query data.
|
|
41
|
+
"""
|
|
42
|
+
self.mapping = self.transform_to_callbacks(mapping)
|
|
43
|
+
self.allow_extra_fields = allow_extra_fields
|
|
64
44
|
|
|
65
45
|
@classmethod
|
|
66
46
|
def transform_to_callbacks(cls, callback_map: CallbackMap) -> CallbackMapStrict:
|
|
67
47
|
"""Transforms `CallbackMap` to `CallbackMapStrict`."""
|
|
68
48
|
callback_map_result = []
|
|
69
49
|
|
|
70
|
-
for key, value in callback_map:
|
|
50
|
+
for key, value in callback_map.items():
|
|
71
51
|
if isinstance(value, type):
|
|
72
52
|
validator = (lambda tp: lambda v: isinstance(v, tp))(value)
|
|
73
|
-
elif isinstance(value,
|
|
53
|
+
elif isinstance(value, dict):
|
|
74
54
|
validator = cls.transform_to_callbacks(value)
|
|
75
55
|
elif not callable(value):
|
|
76
56
|
validator = (lambda val: lambda v: val == v)(value)
|
|
@@ -78,27 +58,23 @@ class CallbackDataMap(CallbackQueryDataRule):
|
|
|
78
58
|
validator = value
|
|
79
59
|
callback_map_result.append((key, validator))
|
|
80
60
|
|
|
81
|
-
return callback_map_result
|
|
61
|
+
return dict(callback_map_result)
|
|
82
62
|
|
|
83
63
|
@staticmethod
|
|
84
64
|
async def run_validator(value: typing.Any, validator: Validator) -> bool:
|
|
85
|
-
"""
|
|
65
|
+
"""Runs sync/async validator."""
|
|
86
66
|
with suppress(BaseException):
|
|
87
|
-
|
|
88
|
-
if inspect.isawaitable(result):
|
|
89
|
-
result = await result
|
|
90
|
-
return result
|
|
91
|
-
|
|
67
|
+
return await maybe_awaitable(validator(value))
|
|
92
68
|
return False
|
|
93
69
|
|
|
94
70
|
@classmethod
|
|
95
71
|
async def match(cls, callback_data: dict[str, typing.Any], callback_map: CallbackMapStrict) -> bool:
|
|
96
|
-
"""Matches callback_data with callback_map recursively."""
|
|
97
|
-
for key, validator in callback_map:
|
|
72
|
+
"""Matches `callback_data` with `callback_map` recursively."""
|
|
73
|
+
for key, validator in callback_map.items():
|
|
98
74
|
if key not in callback_data:
|
|
99
75
|
return False
|
|
100
76
|
|
|
101
|
-
if isinstance(validator,
|
|
77
|
+
if isinstance(validator, dict):
|
|
102
78
|
if not (isinstance(callback_data[key], dict) and await cls.match(callback_data[key], validator)):
|
|
103
79
|
return False
|
|
104
80
|
|
|
@@ -111,9 +87,14 @@ class CallbackDataMap(CallbackQueryDataRule):
|
|
|
111
87
|
callback_data = event.decode_data().unwrap_or_none()
|
|
112
88
|
if callback_data is None:
|
|
113
89
|
return False
|
|
90
|
+
|
|
91
|
+
if not self.allow_extra_fields and self.mapping.keys() != callback_data.keys():
|
|
92
|
+
return False
|
|
93
|
+
|
|
114
94
|
if await self.match(callback_data, self.mapping):
|
|
115
|
-
ctx.update(callback_data)
|
|
95
|
+
ctx.update({k: callback_data[k] for k in self.mapping})
|
|
116
96
|
return True
|
|
97
|
+
|
|
117
98
|
return False
|
|
118
99
|
|
|
119
100
|
|
|
@@ -124,6 +105,5 @@ __all__ = (
|
|
|
124
105
|
"CallbackDataMap",
|
|
125
106
|
"CallbackDataMarkup",
|
|
126
107
|
"CallbackQueryDataRule",
|
|
127
|
-
"CallbackQueryRule",
|
|
128
108
|
"HasData",
|
|
129
109
|
)
|
|
@@ -1,30 +1,17 @@
|
|
|
1
|
-
import abc
|
|
2
1
|
import typing
|
|
3
2
|
|
|
4
3
|
from telegrinder.bot.cute_types import ChatJoinRequestCute
|
|
5
|
-
from telegrinder.
|
|
6
|
-
from telegrinder.types.enums import UpdateType
|
|
7
|
-
|
|
8
|
-
from .abc import ABCRule, CheckResult
|
|
4
|
+
from telegrinder.bot.rules.abc import ABCRule
|
|
9
5
|
|
|
10
6
|
ChatJoinRequest: typing.TypeAlias = ChatJoinRequestCute
|
|
11
7
|
|
|
12
8
|
|
|
13
|
-
class
|
|
14
|
-
ABCRule[ChatJoinRequest],
|
|
15
|
-
abc.ABC,
|
|
16
|
-
adapter=EventAdapter(UpdateType.CHAT_JOIN_REQUEST, ChatJoinRequest),
|
|
17
|
-
):
|
|
18
|
-
@abc.abstractmethod
|
|
19
|
-
def check(self, *args: typing.Any, **kwargs: typing.Any) -> CheckResult: ...
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class HasInviteLink(ChatJoinRequestRule):
|
|
9
|
+
class HasInviteLink(ABCRule):
|
|
23
10
|
def check(self, event: ChatJoinRequest) -> bool:
|
|
24
11
|
return bool(event.invite_link)
|
|
25
12
|
|
|
26
13
|
|
|
27
|
-
class InviteLinkName(
|
|
14
|
+
class InviteLinkName(ABCRule, requires=[HasInviteLink()]):
|
|
28
15
|
def __init__(self, name: str, /) -> None:
|
|
29
16
|
self.name = name
|
|
30
17
|
|
|
@@ -32,7 +19,7 @@ class InviteLinkName(ChatJoinRequestRule, requires=[HasInviteLink()]):
|
|
|
32
19
|
return event.invite_link.unwrap().name.unwrap_or_none() == self.name
|
|
33
20
|
|
|
34
21
|
|
|
35
|
-
class InviteLinkByCreator(
|
|
22
|
+
class InviteLinkByCreator(ABCRule, requires=[HasInviteLink()]):
|
|
36
23
|
def __init__(self, creator_id: int, /) -> None:
|
|
37
24
|
self.creator_id = creator_id
|
|
38
25
|
|
|
@@ -40,9 +27,4 @@ class InviteLinkByCreator(ChatJoinRequestRule, requires=[HasInviteLink()]):
|
|
|
40
27
|
return event.invite_link.unwrap().creator.id == self.creator_id
|
|
41
28
|
|
|
42
29
|
|
|
43
|
-
__all__ = (
|
|
44
|
-
"ChatJoinRequestRule",
|
|
45
|
-
"HasInviteLink",
|
|
46
|
-
"InviteLinkByCreator",
|
|
47
|
-
"InviteLinkName",
|
|
48
|
-
)
|
|
30
|
+
__all__ = ("HasInviteLink", "InviteLinkByCreator", "InviteLinkName")
|
telegrinder/bot/rules/command.py
CHANGED
|
@@ -2,13 +2,12 @@ import dataclasses
|
|
|
2
2
|
import typing
|
|
3
3
|
|
|
4
4
|
from telegrinder.bot.dispatch.context import Context
|
|
5
|
+
from telegrinder.bot.rules.abc import ABCRule
|
|
5
6
|
from telegrinder.node.command import CommandInfo, single_split
|
|
6
7
|
from telegrinder.node.me import Me
|
|
7
8
|
from telegrinder.node.source import ChatSource
|
|
8
9
|
from telegrinder.types.enums import ChatType
|
|
9
10
|
|
|
10
|
-
from .abc import ABCRule
|
|
11
|
-
|
|
12
11
|
type Validator = typing.Callable[[str], typing.Any | None]
|
|
13
12
|
|
|
14
13
|
|
|
@@ -36,13 +35,16 @@ class Command(ABCRule):
|
|
|
36
35
|
lazy: bool = False,
|
|
37
36
|
validate_mention: bool = True,
|
|
38
37
|
mention_needed_in_chat: bool = False,
|
|
38
|
+
ignore_case: bool = False,
|
|
39
39
|
) -> None:
|
|
40
|
-
|
|
40
|
+
names = [names] if isinstance(names, str) else names
|
|
41
|
+
self.names = [n.lower() for n in names] if ignore_case else names
|
|
41
42
|
self.arguments = arguments
|
|
42
43
|
self.prefixes = prefixes
|
|
43
44
|
self.separator = separator
|
|
44
45
|
self.lazy = lazy
|
|
45
46
|
self.validate_mention = validate_mention
|
|
47
|
+
self.ignore_case = ignore_case
|
|
46
48
|
|
|
47
49
|
# if true then we'll check for mention when message is from a group
|
|
48
50
|
self.mention_needed_in_chat = mention_needed_in_chat
|
|
@@ -102,7 +104,9 @@ class Command(ABCRule):
|
|
|
102
104
|
if name is None:
|
|
103
105
|
return False
|
|
104
106
|
|
|
105
|
-
|
|
107
|
+
target_name = name.lower() if self.ignore_case else name
|
|
108
|
+
|
|
109
|
+
if target_name not in self.names:
|
|
106
110
|
return False
|
|
107
111
|
|
|
108
112
|
if not command.mention and self.mention_needed_in_chat and chat.type is not ChatType.PRIVATE:
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import enum
|
|
2
2
|
|
|
3
3
|
from telegrinder.bot.dispatch.context import Context
|
|
4
|
+
from telegrinder.bot.rules.abc import ABCRule
|
|
4
5
|
from telegrinder.node.text import Text
|
|
5
6
|
|
|
6
|
-
from .abc import ABCRule
|
|
7
|
-
|
|
8
7
|
|
|
9
8
|
class EnumTextRule[T: enum.Enum](ABCRule):
|
|
10
9
|
def __init__(self, enum_t: type[T], *, lower_case: bool = True) -> None:
|
|
11
10
|
self.enum_t = enum_t
|
|
12
|
-
self.texts =
|
|
11
|
+
self.texts = set[str](
|
|
13
12
|
map(
|
|
14
13
|
lambda x: x.value.lower() if lower_case else x.value,
|
|
15
14
|
self.enum_t,
|
|
@@ -23,7 +22,7 @@ class EnumTextRule[T: enum.Enum](ABCRule):
|
|
|
23
22
|
raise KeyError("Enumeration is undefined.")
|
|
24
23
|
|
|
25
24
|
def check(self, text: Text, ctx: Context) -> bool:
|
|
26
|
-
text = text.lower()
|
|
25
|
+
text = text.lower()
|
|
27
26
|
if text not in self.texts:
|
|
28
27
|
return False
|
|
29
28
|
ctx.enum_text = self.find(text)
|
telegrinder/bot/rules/func.py
CHANGED
|
@@ -1,28 +1,21 @@
|
|
|
1
|
-
import inspect
|
|
2
1
|
import typing
|
|
3
2
|
|
|
4
3
|
from telegrinder.bot.dispatch.context import Context
|
|
5
|
-
from telegrinder.
|
|
6
|
-
from telegrinder.tools.
|
|
4
|
+
from telegrinder.bot.rules.abc import ABCRule
|
|
5
|
+
from telegrinder.tools.aio import maybe_awaitable
|
|
7
6
|
from telegrinder.types.objects import Update
|
|
8
7
|
|
|
9
|
-
from .abc import ABCRule, AdaptTo
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
class FuncRule(ABCRule, typing.Generic[AdaptTo]):
|
|
9
|
+
class FuncRule(ABCRule):
|
|
13
10
|
def __init__(
|
|
14
11
|
self,
|
|
15
|
-
func: typing.Callable[[
|
|
16
|
-
|
|
12
|
+
func: typing.Callable[[Update, Context], typing.Awaitable[bool] | bool],
|
|
13
|
+
/,
|
|
17
14
|
) -> None:
|
|
18
15
|
self.func = func
|
|
19
|
-
self.adapter = adapter or RawUpdateAdapter() # type: ignore
|
|
20
16
|
|
|
21
|
-
async def check(self,
|
|
22
|
-
|
|
23
|
-
if inspect.isawaitable(result):
|
|
24
|
-
result = await result
|
|
25
|
-
return result
|
|
17
|
+
async def check(self, update: Update, ctx: Context) -> bool:
|
|
18
|
+
return await maybe_awaitable(self.func(update, ctx))
|
|
26
19
|
|
|
27
20
|
|
|
28
21
|
__all__ = ("FuncRule",)
|
telegrinder/bot/rules/fuzzy.py
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
import difflib
|
|
2
2
|
|
|
3
3
|
from telegrinder.bot.dispatch.context import Context
|
|
4
|
+
from telegrinder.bot.rules.abc import ABCRule
|
|
4
5
|
from telegrinder.node.text import Text
|
|
5
6
|
|
|
6
|
-
from .abc import ABCRule
|
|
7
|
-
|
|
8
7
|
|
|
9
8
|
class FuzzyText(ABCRule):
|
|
10
|
-
def __init__(self, texts: str | list[str], /, min_ratio: float = 0.7) -> None:
|
|
9
|
+
def __init__(self, texts: str | list[str], /, *, min_ratio: float = 0.7) -> None:
|
|
11
10
|
if isinstance(texts, str):
|
|
12
11
|
texts = [texts]
|
|
13
|
-
self.texts = texts
|
|
12
|
+
self.texts = set(texts)
|
|
14
13
|
self.min_ratio = min_ratio
|
|
15
14
|
|
|
16
15
|
def check(self, message_text: Text, ctx: Context) -> bool:
|
telegrinder/bot/rules/inline.py
CHANGED
|
@@ -1,32 +1,21 @@
|
|
|
1
|
-
import abc
|
|
2
1
|
import typing
|
|
3
2
|
|
|
4
3
|
from telegrinder.bot.cute_types import InlineQueryCute
|
|
5
4
|
from telegrinder.bot.dispatch.context import Context
|
|
6
|
-
from telegrinder.bot.rules.abc import ABCRule
|
|
7
|
-
from telegrinder.
|
|
8
|
-
from telegrinder.types.enums import ChatType, UpdateType
|
|
5
|
+
from telegrinder.bot.rules.abc import ABCRule
|
|
6
|
+
from telegrinder.types.enums import ChatType
|
|
9
7
|
|
|
10
8
|
from .markup import Markup, PatternLike, check_string
|
|
11
9
|
|
|
12
10
|
InlineQuery: typing.TypeAlias = InlineQueryCute
|
|
13
11
|
|
|
14
12
|
|
|
15
|
-
class
|
|
16
|
-
ABCRule[InlineQuery],
|
|
17
|
-
abc.ABC,
|
|
18
|
-
adapter=EventAdapter(UpdateType.INLINE_QUERY, InlineQuery),
|
|
19
|
-
):
|
|
20
|
-
@abc.abstractmethod
|
|
21
|
-
def check(self, *args: typing.Any, **kwargs: typing.Any) -> CheckResult: ...
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
class HasLocation(InlineQueryRule):
|
|
13
|
+
class HasLocation(ABCRule):
|
|
25
14
|
def check(self, query: InlineQuery) -> bool:
|
|
26
15
|
return bool(query.location)
|
|
27
16
|
|
|
28
17
|
|
|
29
|
-
class InlineQueryChatType(
|
|
18
|
+
class InlineQueryChatType(ABCRule):
|
|
30
19
|
def __init__(self, chat_type: ChatType, /) -> None:
|
|
31
20
|
self.chat_type = chat_type
|
|
32
21
|
|
|
@@ -34,18 +23,18 @@ class InlineQueryChatType(InlineQueryRule):
|
|
|
34
23
|
return query.chat_type.map(lambda x: x == self.chat_type).unwrap_or(False)
|
|
35
24
|
|
|
36
25
|
|
|
37
|
-
class InlineQueryText(
|
|
26
|
+
class InlineQueryText(ABCRule):
|
|
38
27
|
def __init__(self, texts: str | list[str], *, lower_case: bool = False) -> None:
|
|
39
|
-
self.texts =
|
|
28
|
+
self.texts = {
|
|
40
29
|
text.lower() if lower_case else text for text in ([texts] if isinstance(texts, str) else texts)
|
|
41
|
-
|
|
30
|
+
}
|
|
42
31
|
self.lower_case = lower_case
|
|
43
32
|
|
|
44
33
|
def check(self, query: InlineQuery) -> bool:
|
|
45
34
|
return (query.query.lower() if self.lower_case else query.query) in self.texts
|
|
46
35
|
|
|
47
36
|
|
|
48
|
-
class InlineQueryMarkup(
|
|
37
|
+
class InlineQueryMarkup(ABCRule):
|
|
49
38
|
def __init__(self, patterns: PatternLike | list[PatternLike], /) -> None:
|
|
50
39
|
self.patterns = Markup(patterns).patterns
|
|
51
40
|
|
|
@@ -57,6 +46,5 @@ __all__ = (
|
|
|
57
46
|
"HasLocation",
|
|
58
47
|
"InlineQueryChatType",
|
|
59
48
|
"InlineQueryMarkup",
|
|
60
|
-
"InlineQueryRule",
|
|
61
49
|
"InlineQueryText",
|
|
62
50
|
)
|
telegrinder/bot/rules/integer.py
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
+
from telegrinder.bot.rules.abc import ABCRule
|
|
2
|
+
from telegrinder.bot.rules.node import NodeRule
|
|
1
3
|
from telegrinder.node.base import as_node
|
|
2
4
|
from telegrinder.node.text import TextInteger
|
|
3
5
|
|
|
4
|
-
from .abc import ABCRule
|
|
5
|
-
from .node import NodeRule
|
|
6
|
-
|
|
7
6
|
|
|
8
7
|
class IsInteger(NodeRule):
|
|
9
8
|
def __init__(self) -> None:
|