telegrinder 0.1.dev20__py3-none-any.whl → 0.1.dev159__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 +129 -22
- telegrinder/api/__init__.py +11 -2
- telegrinder/api/abc.py +25 -9
- telegrinder/api/api.py +47 -28
- telegrinder/api/error.py +14 -4
- telegrinder/api/response.py +11 -7
- telegrinder/bot/__init__.py +68 -7
- telegrinder/bot/bot.py +30 -24
- telegrinder/bot/cute_types/__init__.py +11 -1
- telegrinder/bot/cute_types/base.py +138 -0
- telegrinder/bot/cute_types/callback_query.py +458 -15
- telegrinder/bot/cute_types/inline_query.py +30 -24
- telegrinder/bot/cute_types/message.py +2982 -78
- telegrinder/bot/cute_types/update.py +30 -0
- telegrinder/bot/cute_types/utils.py +794 -0
- telegrinder/bot/dispatch/__init__.py +56 -3
- telegrinder/bot/dispatch/abc.py +9 -7
- telegrinder/bot/dispatch/composition.py +74 -0
- telegrinder/bot/dispatch/context.py +71 -0
- telegrinder/bot/dispatch/dispatch.py +86 -49
- telegrinder/bot/dispatch/handler/__init__.py +3 -0
- telegrinder/bot/dispatch/handler/abc.py +11 -5
- telegrinder/bot/dispatch/handler/func.py +41 -32
- telegrinder/bot/dispatch/handler/message_reply.py +46 -0
- telegrinder/bot/dispatch/middleware/__init__.py +2 -0
- telegrinder/bot/dispatch/middleware/abc.py +10 -4
- telegrinder/bot/dispatch/process.py +53 -49
- telegrinder/bot/dispatch/return_manager/__init__.py +19 -0
- telegrinder/bot/dispatch/return_manager/abc.py +95 -0
- telegrinder/bot/dispatch/return_manager/callback_query.py +19 -0
- telegrinder/bot/dispatch/return_manager/inline_query.py +14 -0
- telegrinder/bot/dispatch/return_manager/message.py +25 -0
- telegrinder/bot/dispatch/view/__init__.py +14 -2
- telegrinder/bot/dispatch/view/abc.py +128 -2
- telegrinder/bot/dispatch/view/box.py +38 -0
- telegrinder/bot/dispatch/view/callback_query.py +13 -39
- telegrinder/bot/dispatch/view/inline_query.py +11 -39
- telegrinder/bot/dispatch/view/message.py +11 -47
- telegrinder/bot/dispatch/waiter_machine/__init__.py +9 -0
- telegrinder/bot/dispatch/waiter_machine/machine.py +116 -0
- telegrinder/bot/dispatch/waiter_machine/middleware.py +76 -0
- telegrinder/bot/dispatch/waiter_machine/short_state.py +37 -0
- telegrinder/bot/polling/__init__.py +2 -0
- telegrinder/bot/polling/abc.py +11 -4
- telegrinder/bot/polling/polling.py +89 -40
- telegrinder/bot/rules/__init__.py +91 -5
- telegrinder/bot/rules/abc.py +81 -63
- telegrinder/bot/rules/adapter/__init__.py +11 -0
- telegrinder/bot/rules/adapter/abc.py +21 -0
- telegrinder/bot/rules/adapter/errors.py +5 -0
- telegrinder/bot/rules/adapter/event.py +49 -0
- telegrinder/bot/rules/adapter/raw_update.py +24 -0
- telegrinder/bot/rules/callback_data.py +159 -38
- telegrinder/bot/rules/command.py +116 -0
- telegrinder/bot/rules/enum_text.py +28 -0
- telegrinder/bot/rules/func.py +17 -17
- telegrinder/bot/rules/fuzzy.py +13 -10
- telegrinder/bot/rules/inline.py +61 -0
- telegrinder/bot/rules/integer.py +12 -7
- telegrinder/bot/rules/is_from.py +148 -7
- telegrinder/bot/rules/markup.py +21 -18
- telegrinder/bot/rules/mention.py +17 -0
- telegrinder/bot/rules/message_entities.py +33 -0
- telegrinder/bot/rules/regex.py +27 -19
- telegrinder/bot/rules/rule_enum.py +74 -0
- telegrinder/bot/rules/start.py +25 -13
- telegrinder/bot/rules/text.py +23 -14
- telegrinder/bot/scenario/__init__.py +2 -0
- telegrinder/bot/scenario/abc.py +12 -5
- telegrinder/bot/scenario/checkbox.py +48 -30
- telegrinder/bot/scenario/choice.py +16 -10
- telegrinder/client/__init__.py +3 -1
- telegrinder/client/abc.py +26 -16
- telegrinder/client/aiohttp.py +54 -32
- telegrinder/model.py +119 -40
- telegrinder/modules.py +189 -21
- telegrinder/msgspec_json.py +14 -0
- telegrinder/msgspec_utils.py +227 -0
- telegrinder/node/__init__.py +31 -0
- telegrinder/node/attachment.py +71 -0
- telegrinder/node/base.py +93 -0
- telegrinder/node/composer.py +71 -0
- telegrinder/node/container.py +22 -0
- telegrinder/node/message.py +18 -0
- telegrinder/node/rule.py +56 -0
- telegrinder/node/source.py +31 -0
- telegrinder/node/text.py +13 -0
- telegrinder/node/tools/__init__.py +3 -0
- telegrinder/node/tools/generator.py +40 -0
- telegrinder/node/update.py +12 -0
- telegrinder/rules.py +1 -1
- telegrinder/tools/__init__.py +138 -4
- telegrinder/tools/buttons.py +89 -51
- telegrinder/tools/error_handler/__init__.py +8 -0
- telegrinder/tools/error_handler/abc.py +30 -0
- telegrinder/tools/error_handler/error_handler.py +156 -0
- telegrinder/tools/formatting/__init__.py +81 -3
- telegrinder/tools/formatting/html.py +283 -37
- telegrinder/tools/formatting/links.py +32 -0
- telegrinder/tools/formatting/spec_html_formats.py +121 -0
- telegrinder/tools/global_context/__init__.py +12 -0
- telegrinder/tools/global_context/abc.py +66 -0
- telegrinder/tools/global_context/global_context.py +451 -0
- telegrinder/tools/global_context/telegrinder_ctx.py +25 -0
- telegrinder/tools/i18n/__init__.py +12 -0
- telegrinder/tools/i18n/base.py +31 -0
- telegrinder/tools/i18n/middleware/__init__.py +3 -0
- telegrinder/tools/i18n/middleware/base.py +26 -0
- telegrinder/tools/i18n/simple.py +48 -0
- telegrinder/tools/kb_set/__init__.py +2 -0
- telegrinder/tools/kb_set/base.py +3 -0
- telegrinder/tools/kb_set/yaml.py +28 -17
- telegrinder/tools/keyboard.py +84 -62
- telegrinder/tools/loop_wrapper/__init__.py +4 -0
- telegrinder/tools/loop_wrapper/abc.py +18 -0
- telegrinder/tools/loop_wrapper/loop_wrapper.py +132 -0
- telegrinder/tools/magic.py +48 -23
- telegrinder/tools/parse_mode.py +1 -2
- telegrinder/types/__init__.py +1 -0
- telegrinder/types/enums.py +653 -0
- telegrinder/types/methods.py +4107 -1279
- telegrinder/types/objects.py +4771 -1745
- {telegrinder-0.1.dev20.dist-info → telegrinder-0.1.dev159.dist-info}/LICENSE +2 -1
- telegrinder-0.1.dev159.dist-info/METADATA +109 -0
- telegrinder-0.1.dev159.dist-info/RECORD +126 -0
- {telegrinder-0.1.dev20.dist-info → telegrinder-0.1.dev159.dist-info}/WHEEL +1 -1
- telegrinder/bot/dispatch/waiter.py +0 -38
- telegrinder/result.py +0 -38
- telegrinder/tools/formatting/abc.py +0 -52
- telegrinder/tools/formatting/markdown.py +0 -57
- telegrinder-0.1.dev20.dist-info/METADATA +0 -22
- telegrinder-0.1.dev20.dist-info/RECORD +0 -71
telegrinder/bot/rules/func.py
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
|
|
2
|
-
from telegrinder.types import Update
|
|
1
|
+
import inspect
|
|
3
2
|
import typing
|
|
4
3
|
|
|
4
|
+
from telegrinder.bot.dispatch.context import Context
|
|
5
|
+
from telegrinder.types import Update
|
|
6
|
+
|
|
7
|
+
from .abc import ABCAdapter, ABCRule, RawUpdateAdapter, T
|
|
8
|
+
|
|
5
9
|
|
|
6
10
|
class FuncRule(ABCRule, typing.Generic[T]):
|
|
7
11
|
def __init__(
|
|
8
12
|
self,
|
|
9
|
-
func: typing.Callable[[T,
|
|
10
|
-
|
|
11
|
-
typing.Union[EventScheme, typing.Tuple[str, typing.Type[T]]]
|
|
12
|
-
] = None,
|
|
13
|
+
func: typing.Callable[[T, Context], typing.Awaitable[bool] | bool],
|
|
14
|
+
adapter: ABCAdapter[Update, T] | None = None,
|
|
13
15
|
):
|
|
14
16
|
self.func = func
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
self.adapter = adapter or RawUpdateAdapter()
|
|
18
|
+
|
|
19
|
+
async def check(self, event: T, ctx: Context) -> bool:
|
|
20
|
+
result = self.func(event, ctx)
|
|
21
|
+
if inspect.isawaitable(result):
|
|
22
|
+
return await result
|
|
23
|
+
return result # type: ignore
|
|
24
|
+
|
|
18
25
|
|
|
19
|
-
|
|
20
|
-
if self.event_scheme:
|
|
21
|
-
if self.event_scheme.name not in event:
|
|
22
|
-
return False
|
|
23
|
-
event = self.event_scheme.dataclass(
|
|
24
|
-
**getattr(event, self.event_scheme.name).to_dict()
|
|
25
|
-
)
|
|
26
|
-
return self.func(event, ctx)
|
|
26
|
+
__all__ = ("FuncRule",)
|
telegrinder/bot/rules/fuzzy.py
CHANGED
|
@@ -1,24 +1,27 @@
|
|
|
1
|
-
from .abc import Message
|
|
2
|
-
from .text import ABCTextMessageRule
|
|
3
1
|
import difflib
|
|
4
|
-
|
|
2
|
+
|
|
3
|
+
from telegrinder.bot.dispatch.context import Context
|
|
4
|
+
|
|
5
|
+
from .abc import Message
|
|
6
|
+
from .text import TextMessageRule
|
|
5
7
|
|
|
6
8
|
|
|
7
|
-
class FuzzyText(
|
|
8
|
-
def __init__(
|
|
9
|
-
self, texts: typing.Union[str, typing.List[str]], min_ratio: float = 0.7
|
|
10
|
-
):
|
|
9
|
+
class FuzzyText(TextMessageRule):
|
|
10
|
+
def __init__(self, texts: str | list[str], min_ratio: float = 0.7):
|
|
11
11
|
if isinstance(texts, str):
|
|
12
12
|
texts = [texts]
|
|
13
13
|
self.texts = texts
|
|
14
14
|
self.min_ratio = min_ratio
|
|
15
15
|
|
|
16
|
-
async def check(self, message: Message, ctx:
|
|
16
|
+
async def check(self, message: Message, ctx: Context) -> bool:
|
|
17
17
|
match = max(
|
|
18
|
-
difflib.SequenceMatcher(a=message.text, b=text).ratio()
|
|
18
|
+
difflib.SequenceMatcher(a=message.text.unwrap(), b=text).ratio()
|
|
19
19
|
for text in self.texts
|
|
20
20
|
)
|
|
21
21
|
if match < self.min_ratio:
|
|
22
22
|
return False
|
|
23
|
-
ctx
|
|
23
|
+
ctx.fuzzy_ratio = match
|
|
24
24
|
return True
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
__all__ = ("FuzzyText",)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
|
|
3
|
+
from telegrinder.bot.cute_types import InlineQueryCute
|
|
4
|
+
from telegrinder.bot.dispatch.context import Context
|
|
5
|
+
from telegrinder.bot.rules.abc import ABCRule
|
|
6
|
+
from telegrinder.bot.rules.adapter import EventAdapter
|
|
7
|
+
from telegrinder.types.enums import ChatType
|
|
8
|
+
|
|
9
|
+
from .markup import Markup, PatternLike, check_string
|
|
10
|
+
|
|
11
|
+
InlineQuery = InlineQueryCute
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class InlineQueryRule(ABCRule[InlineQuery], abc.ABC):
|
|
15
|
+
adapter = EventAdapter("inline_query", InlineQuery)
|
|
16
|
+
|
|
17
|
+
@abc.abstractmethod
|
|
18
|
+
async def check(self, query: InlineQuery, ctx: Context) -> bool:
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class HasLocation(InlineQueryRule):
|
|
23
|
+
async def check(self, query: InlineQuery, ctx: Context) -> bool:
|
|
24
|
+
return bool(query.location)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class InlineQueryChatType(InlineQueryRule):
|
|
28
|
+
def __init__(self, chat_type: ChatType, /) -> None:
|
|
29
|
+
self.chat_type = chat_type
|
|
30
|
+
|
|
31
|
+
async def check(self, query: InlineQuery, ctx: Context) -> bool:
|
|
32
|
+
return query.chat_type.map(lambda x: x == self.chat_type).unwrap_or(False)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class InlineQueryText(InlineQueryRule):
|
|
36
|
+
def __init__(self, texts: str | list[str], *, lower_case: bool = False) -> None:
|
|
37
|
+
self.texts = [
|
|
38
|
+
text.lower() if lower_case else text
|
|
39
|
+
for text in ([texts] if isinstance(texts, str) else texts)
|
|
40
|
+
]
|
|
41
|
+
self.lower_case = lower_case
|
|
42
|
+
|
|
43
|
+
async def check(self, query: InlineQuery, ctx: Context) -> bool:
|
|
44
|
+
return (query.query.lower() if self.lower_case else query.query) in self.texts
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class InlineQueryMarkup(InlineQueryRule):
|
|
48
|
+
def __init__(self, patterns: PatternLike | list[PatternLike], /) -> None:
|
|
49
|
+
self.patterns = Markup(patterns).patterns
|
|
50
|
+
|
|
51
|
+
async def check(self, query: InlineQuery, ctx: Context) -> bool:
|
|
52
|
+
return check_string(self.patterns, query.query, ctx)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
__all__ = (
|
|
56
|
+
"HasLocation",
|
|
57
|
+
"InlineQueryRule",
|
|
58
|
+
"InlineQueryText",
|
|
59
|
+
"InlineQueryMarkup",
|
|
60
|
+
"InlineQueryChatType",
|
|
61
|
+
)
|
telegrinder/bot/rules/integer.py
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
|
-
from .
|
|
1
|
+
from telegrinder.bot.dispatch.context import Context
|
|
2
2
|
|
|
3
|
+
from .text import Message, TextMessageRule
|
|
3
4
|
|
|
4
|
-
class Integer(ABCTextMessageRule):
|
|
5
|
-
async def check(self, message: Message, ctx: dict) -> bool:
|
|
6
|
-
return message.text.isdigit()
|
|
7
5
|
|
|
6
|
+
class Integer(TextMessageRule):
|
|
7
|
+
async def check(self, message: Message, ctx: Context) -> bool:
|
|
8
|
+
return message.text.unwrap().isdigit()
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
|
|
11
|
+
class IntegerInRange(TextMessageRule, requires=[Integer()]):
|
|
10
12
|
def __init__(self, rng: range):
|
|
11
13
|
self.rng = rng
|
|
12
14
|
|
|
13
|
-
async def check(self, message: Message, ctx:
|
|
14
|
-
return int(message.text) in self.rng
|
|
15
|
+
async def check(self, message: Message, ctx: Context) -> bool:
|
|
16
|
+
return int(message.text.unwrap()) in self.rng
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
__all__ = ("Integer", "IntegerInRange")
|
telegrinder/bot/rules/is_from.py
CHANGED
|
@@ -1,11 +1,152 @@
|
|
|
1
|
-
|
|
1
|
+
import typing
|
|
2
2
|
|
|
3
|
+
from telegrinder.bot.cute_types.base import BaseCute
|
|
4
|
+
from telegrinder.bot.dispatch.context import Context
|
|
5
|
+
from telegrinder.msgspec_utils import Option
|
|
6
|
+
from telegrinder.types.enums import ChatType, DiceEmoji
|
|
7
|
+
from telegrinder.types.objects import User
|
|
3
8
|
|
|
4
|
-
|
|
5
|
-
async def check(self, message: Message, ctx: dict) -> bool:
|
|
6
|
-
return message.chat.id > 0
|
|
9
|
+
from .abc import ABCRule, Message, MessageRule
|
|
7
10
|
|
|
11
|
+
T = typing.TypeVar("T", bound=BaseCute)
|
|
8
12
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
13
|
+
|
|
14
|
+
@typing.runtime_checkable
|
|
15
|
+
class HasFromProto(typing.Protocol):
|
|
16
|
+
from_: User | Option[User]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class HasFrom(ABCRule[T]):
|
|
20
|
+
async def check(self, event: T, ctx: Context) -> bool:
|
|
21
|
+
return isinstance(event, HasFromProto) and bool(event.from_)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class HasDice(MessageRule):
|
|
25
|
+
async def check(self, message: Message, ctx: Context) -> bool:
|
|
26
|
+
return bool(message.dice)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class IsReply(MessageRule):
|
|
30
|
+
async def check(self, message: Message, ctx: Context) -> bool:
|
|
31
|
+
return bool(message.reply_to_message)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class IsSticker(MessageRule):
|
|
35
|
+
async def check(self, message: Message, ctx: Context) -> bool:
|
|
36
|
+
return bool(message.sticker)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class IsBot(MessageRule, requires=[HasFrom()]):
|
|
40
|
+
async def check(self, message: Message, ctx: Context) -> bool:
|
|
41
|
+
return message.from_user.is_bot
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class IsUser(MessageRule, requires=[HasFrom()]):
|
|
45
|
+
async def check(self, message: Message, ctx: Context) -> bool:
|
|
46
|
+
return not message.from_user.is_bot
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class IsPremium(MessageRule, requires=[HasFrom()]):
|
|
50
|
+
async def check(self, message: Message, ctx: Context) -> bool:
|
|
51
|
+
return message.from_user.is_premium.unwrap_or(False)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class IsLanguageCode(MessageRule, requires=[HasFrom()]):
|
|
55
|
+
def __init__(self, lang_codes: str | list[str], /) -> None:
|
|
56
|
+
self.lang_codes = [lang_codes] if isinstance(lang_codes, str) else lang_codes
|
|
57
|
+
|
|
58
|
+
async def check(self, message: Message, ctx: Context) -> bool:
|
|
59
|
+
if not message.from_user.language_code:
|
|
60
|
+
return False
|
|
61
|
+
return message.from_user.language_code.unwrap_or_none() in self.lang_codes
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class IsForum(MessageRule):
|
|
65
|
+
async def check(self, message: Message, ctx: Context) -> bool:
|
|
66
|
+
return message.chat.is_forum.unwrap_or(False)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class IsUserId(MessageRule, requires=[HasFrom()]):
|
|
70
|
+
def __init__(self, user_ids: int | list[int], /) -> None:
|
|
71
|
+
self.user_ids = [user_ids] if isinstance(user_ids, int) else user_ids
|
|
72
|
+
|
|
73
|
+
async def check(self, message: Message, ctx: Context) -> bool:
|
|
74
|
+
return message.from_user.id in self.user_ids
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class IsChatId(MessageRule):
|
|
78
|
+
def __init__(self, chat_ids: int | list[int], /) -> None:
|
|
79
|
+
self.chat_ids = [chat_ids] if isinstance(chat_ids, int) else chat_ids
|
|
80
|
+
|
|
81
|
+
async def check(self, message: Message, ctx: Context) -> bool:
|
|
82
|
+
return message.chat.id in self.chat_ids
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class IsPrivate(MessageRule):
|
|
86
|
+
async def check(self, message: Message, ctx: Context) -> bool:
|
|
87
|
+
return message.chat.type == ChatType.PRIVATE
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class IsGroup(MessageRule):
|
|
91
|
+
async def check(self, message: Message, ctx: Context) -> bool:
|
|
92
|
+
return message.chat.type == ChatType.GROUP
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class IsSuperGroup(MessageRule):
|
|
96
|
+
async def check(self, message: Message, ctx: Context) -> bool:
|
|
97
|
+
return message.chat.type == ChatType.SUPERGROUP
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class IsChat(MessageRule):
|
|
101
|
+
async def check(self, message: Message, ctx: Context) -> bool:
|
|
102
|
+
return message.chat.type in (ChatType.GROUP, ChatType.SUPERGROUP)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class IsDice(MessageRule, requires=[HasDice()]):
|
|
106
|
+
async def check(self, message: Message, ctx: Context) -> bool:
|
|
107
|
+
return message.dice.unwrap().emoji == DiceEmoji.DICE
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class IsDartDice(MessageRule, requires=[HasDice()]):
|
|
111
|
+
async def check(self, message: Message, ctx: Context) -> bool:
|
|
112
|
+
return message.dice.unwrap().emoji == DiceEmoji.DART
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class IsBasketballDice(MessageRule, requires=[HasDice()]):
|
|
116
|
+
async def check(self, message: Message, ctx: Context) -> bool:
|
|
117
|
+
return message.dice.unwrap().emoji == DiceEmoji.BASKETBALL
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class IsFootballDice(MessageRule, requires=[HasDice()]):
|
|
121
|
+
async def check(self, message: Message, ctx: Context) -> bool:
|
|
122
|
+
return message.dice.unwrap().emoji == DiceEmoji.FOOTBALL
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class IsSlotMachineDice(MessageRule, requires=[HasDice()]):
|
|
126
|
+
async def check(self, message: Message, ctx: Context) -> bool:
|
|
127
|
+
return message.dice.unwrap().emoji == DiceEmoji.SLOT_MACHINE
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class IsBowlingDice(MessageRule, requires=[HasDice()]):
|
|
131
|
+
async def check(self, message: Message, ctx: Context) -> bool:
|
|
132
|
+
return message.dice.unwrap().emoji == DiceEmoji.BOWLING
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
__all__ = (
|
|
136
|
+
"IsBasketballDice",
|
|
137
|
+
"IsBot",
|
|
138
|
+
"IsBowlingDice",
|
|
139
|
+
"IsChat",
|
|
140
|
+
"IsChatId",
|
|
141
|
+
"IsDartDice",
|
|
142
|
+
"IsDice",
|
|
143
|
+
"IsForum",
|
|
144
|
+
"IsGroup",
|
|
145
|
+
"IsLanguageCode",
|
|
146
|
+
"IsPremium",
|
|
147
|
+
"IsPrivate",
|
|
148
|
+
"IsReply",
|
|
149
|
+
"IsSuperGroup",
|
|
150
|
+
"IsUser",
|
|
151
|
+
"IsUserId",
|
|
152
|
+
)
|
telegrinder/bot/rules/markup.py
CHANGED
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
from .abc import Message, patcher
|
|
2
|
-
from .text import ABCTextMessageRule
|
|
3
|
-
import typing
|
|
4
1
|
import vbml
|
|
5
2
|
|
|
6
|
-
|
|
3
|
+
from telegrinder.bot.dispatch.context import Context
|
|
4
|
+
from telegrinder.tools.global_context import TelegrinderCtx
|
|
7
5
|
|
|
6
|
+
from .abc import Message
|
|
7
|
+
from .text import TextMessageRule
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
PatternLike = str | vbml.Pattern
|
|
10
|
+
global_ctx = TelegrinderCtx()
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def check_string(patterns: list[vbml.Pattern], s: str, ctx: Context) -> bool:
|
|
14
14
|
for pattern in patterns:
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
match global_ctx.vbml_patcher.check(pattern, s):
|
|
16
|
+
case None | False:
|
|
17
|
+
continue
|
|
18
|
+
case {**response}:
|
|
19
|
+
ctx |= response
|
|
20
20
|
return True
|
|
21
21
|
return False
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
class Markup(
|
|
25
|
-
def __init__(self, patterns:
|
|
24
|
+
class Markup(TextMessageRule):
|
|
25
|
+
def __init__(self, patterns: PatternLike | list[PatternLike]):
|
|
26
26
|
if not isinstance(patterns, list):
|
|
27
27
|
patterns = [patterns]
|
|
28
28
|
self.patterns = [
|
|
@@ -30,5 +30,8 @@ class Markup(ABCTextMessageRule):
|
|
|
30
30
|
for pattern in patterns
|
|
31
31
|
]
|
|
32
32
|
|
|
33
|
-
async def check(self, message: Message, ctx:
|
|
34
|
-
return check_string(self.patterns, message.text, ctx)
|
|
33
|
+
async def check(self, message: Message, ctx: Context) -> bool:
|
|
34
|
+
return check_string(self.patterns, message.text.unwrap(), ctx)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
__all__ = ("Markup", "check_string")
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from telegrinder.bot.dispatch.context import Context
|
|
2
|
+
from telegrinder.types.enums import MessageEntityType
|
|
3
|
+
|
|
4
|
+
from .text import Message, TextMessageRule
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class HasMention(TextMessageRule):
|
|
8
|
+
async def check(self, message: Message, ctx: Context) -> bool:
|
|
9
|
+
if not message.entities.unwrap_or_none():
|
|
10
|
+
return False
|
|
11
|
+
return any(
|
|
12
|
+
entity.type == MessageEntityType.MENTION
|
|
13
|
+
for entity in message.entities.unwrap()
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
__all__ = ("HasMention",)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from telegrinder.bot.dispatch.context import Context
|
|
2
|
+
from telegrinder.types.enums import MessageEntityType
|
|
3
|
+
from telegrinder.types.objects import MessageEntity
|
|
4
|
+
|
|
5
|
+
from .abc import Message, MessageRule
|
|
6
|
+
|
|
7
|
+
Entity = str | MessageEntityType
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class HasEntities(MessageRule):
|
|
11
|
+
async def check(self, message: Message, ctx: Context) -> bool:
|
|
12
|
+
return bool(message.entities)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class MessageEntities(MessageRule, requires=[HasEntities()]):
|
|
16
|
+
def __init__(self, entities: Entity | list[Entity]):
|
|
17
|
+
self.entities = [entities] if not isinstance(entities, list) else entities
|
|
18
|
+
|
|
19
|
+
async def check(self, message: Message, ctx: Context) -> bool:
|
|
20
|
+
message_entities: list[MessageEntity] = []
|
|
21
|
+
for entity in message.entities.unwrap():
|
|
22
|
+
for entity_type in self.entities:
|
|
23
|
+
if entity_type == entity.type:
|
|
24
|
+
message_entities.append(entity)
|
|
25
|
+
|
|
26
|
+
if not message_entities:
|
|
27
|
+
return False
|
|
28
|
+
|
|
29
|
+
ctx.message_entities = message_entities
|
|
30
|
+
return True
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
__all__ = ("HasEntities", "MessageEntities")
|
telegrinder/bot/rules/regex.py
CHANGED
|
@@ -1,30 +1,38 @@
|
|
|
1
1
|
import re
|
|
2
2
|
import typing
|
|
3
3
|
|
|
4
|
-
from .
|
|
5
|
-
from .text import ABCTextMessageRule
|
|
4
|
+
from telegrinder.bot.dispatch.context import Context
|
|
6
5
|
|
|
7
|
-
|
|
6
|
+
from .abc import Message
|
|
7
|
+
from .text import TextMessageRule
|
|
8
8
|
|
|
9
|
+
PatternLike = str | typing.Pattern[str]
|
|
9
10
|
|
|
10
|
-
class Regex(ABCTextMessageRule):
|
|
11
|
-
def __init__(self, regexp: typing.Union[PatternLike, typing.List[PatternLike]]):
|
|
12
|
-
if isinstance(regexp, re.Pattern):
|
|
13
|
-
regexp = [regexp]
|
|
14
|
-
elif isinstance(regexp, str):
|
|
15
|
-
regexp = [re.compile(regexp)]
|
|
16
|
-
else:
|
|
17
|
-
regexp = [
|
|
18
|
-
re.compile(regexp) if isinstance(regexp, str) else regexp
|
|
19
|
-
for regexp in regexp
|
|
20
|
-
]
|
|
21
11
|
|
|
22
|
-
|
|
12
|
+
class Regex(TextMessageRule):
|
|
13
|
+
def __init__(self, regexp: PatternLike | list[PatternLike]):
|
|
14
|
+
self.regexp: list[re.Pattern[str]] = []
|
|
15
|
+
match regexp:
|
|
16
|
+
case re.Pattern() as pattern:
|
|
17
|
+
self.regexp.append(pattern)
|
|
18
|
+
case str(regex):
|
|
19
|
+
self.regexp.append(re.compile(regex))
|
|
20
|
+
case _:
|
|
21
|
+
self.regexp.extend(
|
|
22
|
+
re.compile(regexp) if isinstance(regexp, str) else regexp
|
|
23
|
+
for regexp in regexp
|
|
24
|
+
)
|
|
23
25
|
|
|
24
|
-
async def check(self, message: Message, ctx:
|
|
26
|
+
async def check(self, message: Message, ctx: Context) -> bool:
|
|
25
27
|
for regexp in self.regexp:
|
|
26
|
-
|
|
27
|
-
if
|
|
28
|
-
|
|
28
|
+
response = re.match(regexp, message.text.unwrap())
|
|
29
|
+
if response is not None:
|
|
30
|
+
if matches := response.groupdict():
|
|
31
|
+
ctx |= matches
|
|
32
|
+
else:
|
|
33
|
+
ctx |= {"matches": response.groups() or (response.group(),)}
|
|
29
34
|
return True
|
|
30
35
|
return False
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
__all__ = ("Regex",)
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
import typing
|
|
3
|
+
|
|
4
|
+
from telegrinder.bot.dispatch.context import Context
|
|
5
|
+
|
|
6
|
+
from .abc import ABCRule, T, Update, check_rule
|
|
7
|
+
from .func import FuncRule
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclasses.dataclass
|
|
11
|
+
class RuleEnumState:
|
|
12
|
+
name: str
|
|
13
|
+
rule: ABCRule
|
|
14
|
+
cls: type["RuleEnum"]
|
|
15
|
+
|
|
16
|
+
def __eq__(self, other: typing.Self) -> bool:
|
|
17
|
+
return self.cls == other.cls and self.name == other.name
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class RuleEnum(ABCRule[T]):
|
|
21
|
+
__enum__: list[RuleEnumState]
|
|
22
|
+
|
|
23
|
+
def __init_subclass__(cls, *args, **kwargs):
|
|
24
|
+
new_attributes = (
|
|
25
|
+
set(cls.__dict__) - set(RuleEnum.__dict__) - {"__enum__", "__init__"}
|
|
26
|
+
)
|
|
27
|
+
enum_lst: list[RuleEnumState] = []
|
|
28
|
+
|
|
29
|
+
self = cls.__new__(cls)
|
|
30
|
+
self.__init__()
|
|
31
|
+
|
|
32
|
+
for attribute_name in new_attributes:
|
|
33
|
+
rules = getattr(cls, attribute_name)
|
|
34
|
+
attribute = RuleEnumState(attribute_name, rules, cls)
|
|
35
|
+
|
|
36
|
+
setattr(
|
|
37
|
+
self,
|
|
38
|
+
attribute.name,
|
|
39
|
+
self & FuncRule(lambda _, ctx: self.must_be_state(ctx, attribute)),
|
|
40
|
+
)
|
|
41
|
+
enum_lst.append(attribute)
|
|
42
|
+
|
|
43
|
+
setattr(cls, "__enum__", enum_lst)
|
|
44
|
+
|
|
45
|
+
@classmethod
|
|
46
|
+
def save_state(cls, ctx: Context, enum: RuleEnumState) -> None:
|
|
47
|
+
ctx.update({cls.__class__.__name__ + "_state": enum})
|
|
48
|
+
|
|
49
|
+
@classmethod
|
|
50
|
+
def check_state(cls, ctx: Context) -> RuleEnumState | None:
|
|
51
|
+
return ctx.get(cls.__class__.__name__ + "_state")
|
|
52
|
+
|
|
53
|
+
@classmethod
|
|
54
|
+
def must_be_state(cls, ctx: Context, state: RuleEnumState) -> bool:
|
|
55
|
+
real_state = cls.check_state(ctx)
|
|
56
|
+
if not real_state:
|
|
57
|
+
return False
|
|
58
|
+
return real_state == state
|
|
59
|
+
|
|
60
|
+
async def check(self, event: Update, ctx: Context) -> bool:
|
|
61
|
+
if self.check_state(ctx):
|
|
62
|
+
return True
|
|
63
|
+
|
|
64
|
+
for enum in self.__enum__:
|
|
65
|
+
ctx_copy = ctx.copy()
|
|
66
|
+
if await check_rule(event.ctx_api, enum.rule, event, ctx_copy):
|
|
67
|
+
ctx.update(ctx_copy)
|
|
68
|
+
self.save_state(ctx, enum)
|
|
69
|
+
return True
|
|
70
|
+
|
|
71
|
+
return False
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
__all__ = ("RuleEnum", "RuleEnumState")
|
telegrinder/bot/rules/start.py
CHANGED
|
@@ -1,30 +1,42 @@
|
|
|
1
|
-
from .abc import ABCMessageRule
|
|
2
|
-
from .markup import Markup, Message
|
|
3
1
|
import typing
|
|
4
2
|
|
|
3
|
+
from telegrinder.bot.dispatch.context import Context
|
|
4
|
+
from telegrinder.types.enums import MessageEntityType
|
|
5
|
+
|
|
6
|
+
from .abc import MessageRule
|
|
7
|
+
from .is_from import IsPrivate
|
|
8
|
+
from .markup import Markup, Message
|
|
9
|
+
from .message_entities import MessageEntities
|
|
10
|
+
|
|
5
11
|
|
|
6
12
|
class StartCommand(
|
|
7
|
-
|
|
8
|
-
|
|
13
|
+
MessageRule, requires=[
|
|
14
|
+
IsPrivate() & MessageEntities(MessageEntityType.BOT_COMMAND)
|
|
15
|
+
& Markup(["/start <param>", "/start"]),
|
|
16
|
+
]
|
|
9
17
|
):
|
|
10
18
|
def __init__(
|
|
11
|
-
self,
|
|
12
|
-
validator: typing.Callable[str, typing.Any | None] | None = None,
|
|
19
|
+
self,
|
|
20
|
+
validator: typing.Callable[[str], typing.Any | None] | None = None,
|
|
21
|
+
*,
|
|
13
22
|
param_required: bool = False,
|
|
23
|
+
alias: str | None = None,
|
|
14
24
|
) -> None:
|
|
15
25
|
self.param_required = param_required
|
|
16
26
|
self.validator = validator
|
|
27
|
+
self.alias = alias
|
|
17
28
|
|
|
18
|
-
async def check(self,
|
|
19
|
-
param: str | None = ctx.
|
|
29
|
+
async def check(self, _: Message, ctx: Context) -> bool:
|
|
30
|
+
param: str | None = ctx.pop("param", None)
|
|
20
31
|
validated_param = (
|
|
21
|
-
self.validator(param)
|
|
22
|
-
if self.validator and param is not None
|
|
23
|
-
else param
|
|
32
|
+
self.validator(param) if self.validator and param is not None else param
|
|
24
33
|
)
|
|
25
34
|
|
|
26
35
|
if self.param_required and validated_param is None:
|
|
27
36
|
return False
|
|
28
|
-
|
|
29
|
-
ctx
|
|
37
|
+
|
|
38
|
+
ctx.set(self.alias or "param", validated_param)
|
|
30
39
|
return True
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
__all__ = ("StartCommand",)
|
telegrinder/bot/rules/text.py
CHANGED
|
@@ -1,26 +1,35 @@
|
|
|
1
|
-
from .
|
|
2
|
-
import
|
|
1
|
+
from telegrinder.bot.dispatch.context import Context
|
|
2
|
+
from telegrinder.tools.i18n.base import ABCTranslator
|
|
3
3
|
|
|
4
|
+
from .abc import ABC, Message, MessageRule, with_caching_translations
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
|
|
7
|
+
class HasText(MessageRule):
|
|
8
|
+
async def check(self, message: Message, ctx: Context) -> bool:
|
|
7
9
|
return bool(message.text)
|
|
8
10
|
|
|
9
11
|
|
|
10
|
-
class
|
|
12
|
+
class TextMessageRule(MessageRule, ABC, requires=[HasText()]):
|
|
11
13
|
pass
|
|
12
14
|
|
|
13
15
|
|
|
14
|
-
class Text(
|
|
15
|
-
def __init__(
|
|
16
|
-
self, texts: typing.Union[str, typing.List[str]], ignore_case: bool = False
|
|
17
|
-
):
|
|
16
|
+
class Text(TextMessageRule):
|
|
17
|
+
def __init__(self, texts: str | list[str], ignore_case: bool = False):
|
|
18
18
|
if not isinstance(texts, list):
|
|
19
19
|
texts = [texts]
|
|
20
|
-
self.texts = texts
|
|
20
|
+
self.texts = texts if not ignore_case else list(map(str.lower, texts))
|
|
21
21
|
self.ignore_case = ignore_case
|
|
22
22
|
|
|
23
|
-
async def check(self, message: Message, ctx:
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
async def check(self, message: Message, ctx: Context) -> bool:
|
|
24
|
+
text = message.text.unwrap()
|
|
25
|
+
return (text if not self.ignore_case else text.lower()) in self.texts
|
|
26
|
+
|
|
27
|
+
@with_caching_translations
|
|
28
|
+
async def translate(self, translator: ABCTranslator) -> "Text":
|
|
29
|
+
return Text(
|
|
30
|
+
texts=[translator.get(text) for text in self.texts],
|
|
31
|
+
ignore_case=self.ignore_case,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
__all__ = ("HasText", "Text", "TextMessageRule")
|