telegrinder 0.1.dev167__py3-none-any.whl → 0.1.dev169__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 +55 -44
- 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 +33 -30
- telegrinder/bot/dispatch/handler/func.py +33 -12
- 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 +74 -31
- 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 +86 -50
- telegrinder/bot/dispatch/waiter_machine/middleware.py +31 -7
- telegrinder/bot/dispatch/waiter_machine/short_state.py +26 -7
- 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 +13 -15
- 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/checkbox.py +5 -5
- telegrinder/bot/scenario/choice.py +5 -5
- telegrinder/model.py +49 -15
- telegrinder/modules.py +14 -6
- telegrinder/msgspec_utils.py +8 -17
- 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 +9 -14
- 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 +13 -3
- 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 +613 -401
- telegrinder/types/objects.py +1151 -757
- {telegrinder-0.1.dev167.dist-info → telegrinder-0.1.dev169.dist-info}/LICENSE +1 -1
- {telegrinder-0.1.dev167.dist-info → telegrinder-0.1.dev169.dist-info}/METADATA +9 -8
- telegrinder-0.1.dev169.dist-info/RECORD +143 -0
- telegrinder/bot/dispatch/composition.py +0 -88
- telegrinder-0.1.dev167.dist-info/RECORD +0 -137
- {telegrinder-0.1.dev167.dist-info → telegrinder-0.1.dev169.dist-info}/WHEEL +0 -0
|
@@ -4,14 +4,15 @@ import typing
|
|
|
4
4
|
from telegrinder.bot.cute_types import ChatJoinRequestCute
|
|
5
5
|
from telegrinder.bot.dispatch.context import Context
|
|
6
6
|
from telegrinder.bot.rules.adapter import EventAdapter
|
|
7
|
+
from telegrinder.types.enums import UpdateType
|
|
7
8
|
|
|
8
9
|
from .abc import ABCRule
|
|
9
10
|
|
|
10
11
|
ChatJoinRequest: typing.TypeAlias = ChatJoinRequestCute
|
|
11
12
|
|
|
12
13
|
|
|
13
|
-
class ChatJoinRequestRule(ABCRule[
|
|
14
|
-
adapter = EventAdapter(
|
|
14
|
+
class ChatJoinRequestRule(ABCRule[ChatJoinRequest], requires=[]):
|
|
15
|
+
adapter: EventAdapter[ChatJoinRequest] = EventAdapter(UpdateType.CHAT_JOIN_REQUEST, ChatJoinRequest)
|
|
15
16
|
|
|
16
17
|
@abc.abstractmethod
|
|
17
18
|
async def check(self, event: ChatJoinRequest, ctx: Context) -> bool:
|
telegrinder/bot/rules/command.py
CHANGED
|
@@ -2,24 +2,24 @@ import dataclasses
|
|
|
2
2
|
import typing
|
|
3
3
|
|
|
4
4
|
from telegrinder.bot.dispatch.context import Context
|
|
5
|
+
from telegrinder.node import Source
|
|
6
|
+
from telegrinder.node.command import CommandInfo, single_split
|
|
7
|
+
from telegrinder.node.me import Me
|
|
5
8
|
|
|
6
|
-
from
|
|
7
|
-
from .
|
|
9
|
+
from ...types import ChatType
|
|
10
|
+
from .abc import ABCRule
|
|
8
11
|
|
|
9
12
|
Validator = typing.Callable[[str], typing.Any | None]
|
|
10
13
|
|
|
11
14
|
|
|
12
|
-
def single_split(s: str, separator: str) -> tuple[str, str]:
|
|
13
|
-
left, *right = s.split(separator, 1)
|
|
14
|
-
return left, (right[0] if right else "")
|
|
15
|
-
|
|
16
|
-
|
|
17
15
|
@dataclasses.dataclass(frozen=True)
|
|
18
16
|
class Argument:
|
|
19
17
|
name: str
|
|
20
18
|
validators: list[Validator] = dataclasses.field(default_factory=lambda: [])
|
|
21
19
|
optional: bool = dataclasses.field(default=False, kw_only=True)
|
|
22
20
|
|
|
21
|
+
# NOTE: add optional param `description`
|
|
22
|
+
|
|
23
23
|
def check(self, data: str) -> typing.Any | None:
|
|
24
24
|
for validator in self.validators:
|
|
25
25
|
data = validator(data) # type: ignore
|
|
@@ -28,7 +28,7 @@ class Argument:
|
|
|
28
28
|
return data
|
|
29
29
|
|
|
30
30
|
|
|
31
|
-
class Command(
|
|
31
|
+
class Command(ABCRule):
|
|
32
32
|
def __init__(
|
|
33
33
|
self,
|
|
34
34
|
names: str | typing.Iterable[str],
|
|
@@ -36,12 +36,18 @@ class Command(TextMessageRule):
|
|
|
36
36
|
prefixes: tuple[str, ...] = ("/",),
|
|
37
37
|
separator: str = " ",
|
|
38
38
|
lazy: bool = False,
|
|
39
|
+
validate_mention: bool = True,
|
|
40
|
+
mention_needed_in_chat: bool = False,
|
|
39
41
|
) -> None:
|
|
40
42
|
self.names = [names] if isinstance(names, str) else names
|
|
41
43
|
self.arguments = arguments
|
|
42
44
|
self.prefixes = prefixes
|
|
43
45
|
self.separator = separator
|
|
44
46
|
self.lazy = lazy
|
|
47
|
+
self.validate_mention = validate_mention
|
|
48
|
+
|
|
49
|
+
# if true then we'll check for mention when message is from a group
|
|
50
|
+
self.mention_needed_in_chat = mention_needed_in_chat
|
|
45
51
|
|
|
46
52
|
def remove_prefix(self, text: str) -> str | None:
|
|
47
53
|
for prefix in self.prefixes:
|
|
@@ -93,19 +99,25 @@ class Command(TextMessageRule):
|
|
|
93
99
|
|
|
94
100
|
return None
|
|
95
101
|
|
|
96
|
-
async def check(self,
|
|
97
|
-
|
|
98
|
-
if
|
|
102
|
+
async def check(self, command: CommandInfo, me: Me, src: Source, ctx: Context) -> bool:
|
|
103
|
+
name = self.remove_prefix(command.name)
|
|
104
|
+
if name is None:
|
|
99
105
|
return False
|
|
100
106
|
|
|
101
|
-
name, arguments = single_split(text, self.separator)
|
|
102
107
|
if name not in self.names:
|
|
103
108
|
return False
|
|
104
109
|
|
|
110
|
+
if not command.mention and self.mention_needed_in_chat and src.chat.type is not ChatType.PRIVATE:
|
|
111
|
+
return False
|
|
112
|
+
|
|
113
|
+
if command.mention and self.validate_mention: # noqa
|
|
114
|
+
if command.mention.unwrap().lower() != me.username.unwrap().lower():
|
|
115
|
+
return False
|
|
116
|
+
|
|
105
117
|
if not self.arguments:
|
|
106
|
-
return not arguments
|
|
118
|
+
return not command.arguments
|
|
107
119
|
|
|
108
|
-
result = self.parse_arguments(list(self.arguments), arguments)
|
|
120
|
+
result = self.parse_arguments(list(self.arguments), command.arguments)
|
|
109
121
|
if result is None:
|
|
110
122
|
return False
|
|
111
123
|
|
|
@@ -2,14 +2,14 @@ import enum
|
|
|
2
2
|
import typing
|
|
3
3
|
|
|
4
4
|
from telegrinder.bot.dispatch.context import Context
|
|
5
|
+
from telegrinder.node.text import Text
|
|
5
6
|
|
|
6
|
-
from .abc import
|
|
7
|
-
from .text import TextMessageRule
|
|
7
|
+
from .abc import ABCRule
|
|
8
8
|
|
|
9
9
|
T = typing.TypeVar("T", bound=enum.Enum)
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
class EnumTextRule(
|
|
12
|
+
class EnumTextRule(ABCRule, typing.Generic[T]):
|
|
13
13
|
def __init__(self, enum_t: type[T], *, lower_case: bool = True) -> None:
|
|
14
14
|
self.enum_t = enum_t
|
|
15
15
|
self.texts = list(
|
|
@@ -25,8 +25,8 @@ class EnumTextRule(TextMessageRule, typing.Generic[T]):
|
|
|
25
25
|
return enumeration
|
|
26
26
|
raise KeyError("Enumeration is undefined.")
|
|
27
27
|
|
|
28
|
-
async def check(self,
|
|
29
|
-
text =
|
|
28
|
+
async def check(self, text: Text, ctx: Context) -> bool:
|
|
29
|
+
text = text.lower() # type: ignore
|
|
30
30
|
if text not in self.texts:
|
|
31
31
|
return False
|
|
32
32
|
ctx.enum_text = self.find(text)
|
telegrinder/bot/rules/func.py
CHANGED
|
@@ -4,19 +4,19 @@ import typing
|
|
|
4
4
|
from telegrinder.bot.dispatch.context import Context
|
|
5
5
|
from telegrinder.types import Update
|
|
6
6
|
|
|
7
|
-
from .abc import ABCAdapter, ABCRule,
|
|
7
|
+
from .abc import ABCAdapter, ABCRule, AdaptTo, RawUpdateAdapter
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
class FuncRule(ABCRule, typing.Generic[
|
|
10
|
+
class FuncRule(ABCRule, typing.Generic[AdaptTo]):
|
|
11
11
|
def __init__(
|
|
12
12
|
self,
|
|
13
|
-
func: typing.Callable[[
|
|
14
|
-
adapter: ABCAdapter[Update,
|
|
13
|
+
func: typing.Callable[[AdaptTo, Context], typing.Awaitable[bool] | bool],
|
|
14
|
+
adapter: ABCAdapter[Update, AdaptTo] | None = None,
|
|
15
15
|
):
|
|
16
16
|
self.func = func
|
|
17
|
-
self.adapter = adapter or RawUpdateAdapter()
|
|
17
|
+
self.adapter = adapter or RawUpdateAdapter() # type: ignore
|
|
18
18
|
|
|
19
|
-
async def check(self, event:
|
|
19
|
+
async def check(self, event: AdaptTo, ctx: Context) -> bool:
|
|
20
20
|
result = self.func(event, ctx)
|
|
21
21
|
if inspect.isawaitable(result):
|
|
22
22
|
return await result
|
telegrinder/bot/rules/fuzzy.py
CHANGED
|
@@ -1,22 +1,20 @@
|
|
|
1
1
|
import difflib
|
|
2
2
|
|
|
3
3
|
from telegrinder.bot.dispatch.context import Context
|
|
4
|
+
from telegrinder.node.text import Text
|
|
4
5
|
|
|
5
|
-
from .abc import
|
|
6
|
-
from .text import TextMessageRule
|
|
6
|
+
from .abc import ABCRule
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
class FuzzyText(
|
|
9
|
+
class FuzzyText(ABCRule):
|
|
10
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,
|
|
17
|
-
match = max(
|
|
18
|
-
difflib.SequenceMatcher(a=message.text.unwrap(), b=text).ratio() for text in self.texts
|
|
19
|
-
)
|
|
16
|
+
async def check(self, message_text: Text, ctx: Context) -> bool:
|
|
17
|
+
match = max(difflib.SequenceMatcher(a=message_text, b=text).ratio() for text in self.texts)
|
|
20
18
|
if match < self.min_ratio:
|
|
21
19
|
return False
|
|
22
20
|
ctx.fuzzy_ratio = match
|
telegrinder/bot/rules/inline.py
CHANGED
|
@@ -5,7 +5,7 @@ from telegrinder.bot.cute_types import InlineQueryCute
|
|
|
5
5
|
from telegrinder.bot.dispatch.context import Context
|
|
6
6
|
from telegrinder.bot.rules.abc import ABCRule
|
|
7
7
|
from telegrinder.bot.rules.adapter import EventAdapter
|
|
8
|
-
from telegrinder.types.enums import ChatType
|
|
8
|
+
from telegrinder.types.enums import ChatType, UpdateType
|
|
9
9
|
|
|
10
10
|
from .markup import Markup, PatternLike, check_string
|
|
11
11
|
|
|
@@ -13,11 +13,11 @@ InlineQuery: typing.TypeAlias = InlineQueryCute
|
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
class InlineQueryRule(ABCRule[InlineQuery], abc.ABC):
|
|
16
|
-
adapter = EventAdapter(
|
|
16
|
+
adapter: EventAdapter[InlineQuery] = EventAdapter(UpdateType.INLINE_QUERY, InlineQuery)
|
|
17
17
|
|
|
18
18
|
@abc.abstractmethod
|
|
19
19
|
async def check(self, query: InlineQuery, ctx: Context) -> bool:
|
|
20
|
-
|
|
20
|
+
...
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
class HasLocation(InlineQueryRule):
|
|
@@ -36,8 +36,7 @@ class InlineQueryChatType(InlineQueryRule):
|
|
|
36
36
|
class InlineQueryText(InlineQueryRule):
|
|
37
37
|
def __init__(self, texts: str | list[str], *, lower_case: bool = False) -> None:
|
|
38
38
|
self.texts = [
|
|
39
|
-
text.lower() if lower_case else text
|
|
40
|
-
for text in ([texts] if isinstance(texts, str) else texts)
|
|
39
|
+
text.lower() if lower_case else text for text in ([texts] if isinstance(texts, str) else texts)
|
|
41
40
|
]
|
|
42
41
|
self.lower_case = lower_case
|
|
43
42
|
|
telegrinder/bot/rules/integer.py
CHANGED
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
from telegrinder.bot.dispatch.context import Context
|
|
2
|
+
from telegrinder.node.text import TextInteger
|
|
2
3
|
|
|
3
|
-
from .
|
|
4
|
+
from .abc import ABCRule
|
|
5
|
+
from .node import NodeRule
|
|
4
6
|
|
|
5
7
|
|
|
6
|
-
class
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
class IsInteger(NodeRule):
|
|
9
|
+
def __init__(self) -> None:
|
|
10
|
+
super().__init__(TextInteger)
|
|
9
11
|
|
|
10
12
|
|
|
11
|
-
class IntegerInRange(
|
|
13
|
+
class IntegerInRange(ABCRule):
|
|
12
14
|
def __init__(self, rng: range):
|
|
13
15
|
self.rng = rng
|
|
14
16
|
|
|
15
|
-
async def check(self,
|
|
16
|
-
return
|
|
17
|
+
async def check(self, integer: TextInteger) -> bool:
|
|
18
|
+
return integer in self.rng
|
|
17
19
|
|
|
18
20
|
|
|
19
|
-
__all__ = ("
|
|
21
|
+
__all__ = ("IsInteger", "IntegerInRange")
|
telegrinder/bot/rules/is_from.py
CHANGED
|
@@ -1,145 +1,117 @@
|
|
|
1
1
|
import typing
|
|
2
2
|
|
|
3
|
-
from
|
|
4
|
-
|
|
5
|
-
from telegrinder.bot.cute_types.base import BaseCute
|
|
6
|
-
from telegrinder.bot.cute_types.update import UpdateCute
|
|
7
|
-
from telegrinder.bot.dispatch.context import Context
|
|
8
|
-
from telegrinder.msgspec_utils import Option
|
|
3
|
+
from telegrinder.node.source import ChatSource, UserSource
|
|
9
4
|
from telegrinder.types.enums import ChatType, DiceEmoji
|
|
10
|
-
from telegrinder.types.objects import User
|
|
11
5
|
|
|
12
6
|
from .abc import ABCRule, Message
|
|
13
7
|
from .message import MessageRule
|
|
14
8
|
|
|
15
|
-
T = typing.TypeVar("T", bound=BaseCute)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def get_from_user(obj: typing.Any) -> User:
|
|
19
|
-
assert isinstance(obj, FromUserProto)
|
|
20
|
-
return obj.from_.unwrap() if isinstance(obj.from_, Some | Nothing) else obj.from_
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
@typing.runtime_checkable
|
|
24
|
-
class FromUserProto(typing.Protocol):
|
|
25
|
-
from_: User | Option[User]
|
|
26
|
-
|
|
27
9
|
|
|
28
|
-
class
|
|
29
|
-
async def check(self,
|
|
30
|
-
|
|
31
|
-
return isinstance(event_model, FromUserProto) and bool(event_model.from_)
|
|
10
|
+
class IsBot(ABCRule):
|
|
11
|
+
async def check(self, user: UserSource) -> bool:
|
|
12
|
+
return user.is_bot
|
|
32
13
|
|
|
33
14
|
|
|
34
|
-
class
|
|
35
|
-
async def check(self,
|
|
36
|
-
return
|
|
37
|
-
|
|
15
|
+
class IsUser(ABCRule):
|
|
16
|
+
async def check(self, user: UserSource) -> bool:
|
|
17
|
+
return not user.is_bot
|
|
38
18
|
|
|
39
|
-
class IsForward(MessageRule):
|
|
40
|
-
async def check(self, message: Message, ctx: Context) -> bool:
|
|
41
|
-
return bool(message.forward_origin)
|
|
42
19
|
|
|
20
|
+
class IsPremium(ABCRule):
|
|
21
|
+
async def check(self, user: UserSource) -> bool:
|
|
22
|
+
return user.is_premium.unwrap_or(False)
|
|
43
23
|
|
|
44
|
-
class IsForwardType(MessageRule, requires=[IsForward()]):
|
|
45
|
-
def __init__(
|
|
46
|
-
self, fwd_type: typing.Literal["user", "hidden_user", "chat", "channel"], /
|
|
47
|
-
) -> None:
|
|
48
|
-
self.fwd_type = fwd_type
|
|
49
|
-
|
|
50
|
-
async def check(self, message: Message, ctx: Context) -> bool:
|
|
51
|
-
return message.forward_origin.unwrap().v.type == self.fwd_type
|
|
52
24
|
|
|
25
|
+
class IsLanguageCode(ABCRule):
|
|
26
|
+
def __init__(self, lang_codes: str | list[str], /) -> None:
|
|
27
|
+
self.lang_codes = [lang_codes] if isinstance(lang_codes, str) else lang_codes
|
|
53
28
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
return bool(message.reply_to_message)
|
|
29
|
+
async def check(self, user: UserSource) -> bool:
|
|
30
|
+
return user.language_code.unwrap_or_none() in self.lang_codes
|
|
57
31
|
|
|
58
32
|
|
|
59
|
-
class
|
|
60
|
-
|
|
61
|
-
|
|
33
|
+
class IsUserId(ABCRule):
|
|
34
|
+
def __init__(self, user_ids: int | list[int], /) -> None:
|
|
35
|
+
self.user_ids = [user_ids] if isinstance(user_ids, int) else user_ids
|
|
62
36
|
|
|
37
|
+
async def check(self, user: UserSource) -> bool:
|
|
38
|
+
return user.id in self.user_ids
|
|
63
39
|
|
|
64
|
-
class IsBot(ABCRule[T], requires=[HasFrom()]):
|
|
65
|
-
async def check(self, event: UpdateCute, ctx: Context) -> bool:
|
|
66
|
-
return get_from_user(event.incoming_update.unwrap()).is_bot
|
|
67
40
|
|
|
41
|
+
class IsForum(ABCRule):
|
|
42
|
+
async def check(self, chat: ChatSource) -> bool:
|
|
43
|
+
return chat.is_forum.unwrap_or(False)
|
|
68
44
|
|
|
69
|
-
class IsUser(ABCRule[T], requires=[HasFrom()]):
|
|
70
|
-
async def check(self, event: UpdateCute, ctx: Context) -> bool:
|
|
71
|
-
return not get_from_user(event.incoming_update.unwrap()).is_bot
|
|
72
45
|
|
|
46
|
+
class IsChatId(ABCRule):
|
|
47
|
+
def __init__(self, chat_ids: int | list[int], /) -> None:
|
|
48
|
+
self.chat_ids = [chat_ids] if isinstance(chat_ids, int) else chat_ids
|
|
73
49
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
return get_from_user(event.incoming_update.unwrap()).is_premium.unwrap_or(False)
|
|
50
|
+
async def check(self, chat: ChatSource) -> bool:
|
|
51
|
+
return chat.id in self.chat_ids
|
|
77
52
|
|
|
78
53
|
|
|
79
|
-
class
|
|
80
|
-
def
|
|
81
|
-
|
|
54
|
+
class IsPrivate(ABCRule):
|
|
55
|
+
async def check(self, chat: ChatSource) -> bool:
|
|
56
|
+
return chat.type == ChatType.PRIVATE
|
|
82
57
|
|
|
83
|
-
async def check(self, event: UpdateCute, ctx: Context) -> bool:
|
|
84
|
-
return (
|
|
85
|
-
get_from_user(event.incoming_update.unwrap()).language_code.unwrap_or_none()
|
|
86
|
-
in self.lang_codes
|
|
87
|
-
)
|
|
88
58
|
|
|
59
|
+
class IsGroup(ABCRule):
|
|
60
|
+
async def check(self, chat: ChatSource) -> bool:
|
|
61
|
+
return chat.type == ChatType.GROUP
|
|
89
62
|
|
|
90
|
-
class IsForum(MessageRule):
|
|
91
|
-
async def check(self, message: Message, ctx: Context) -> bool:
|
|
92
|
-
return message.chat.is_forum.unwrap_or(False)
|
|
93
63
|
|
|
64
|
+
class IsSuperGroup(ABCRule):
|
|
65
|
+
async def check(self, chat: ChatSource) -> bool:
|
|
66
|
+
return chat.type == ChatType.SUPERGROUP
|
|
94
67
|
|
|
95
|
-
class IsUserId(ABCRule[T], requires=[HasFrom()]):
|
|
96
|
-
def __init__(self, user_ids: int | list[int], /) -> None:
|
|
97
|
-
self.user_ids = [user_ids] if isinstance(user_ids, int) else user_ids
|
|
98
68
|
|
|
99
|
-
|
|
100
|
-
|
|
69
|
+
class IsChat(ABCRule):
|
|
70
|
+
async def check(self, chat: ChatSource) -> bool:
|
|
71
|
+
return chat.type in (ChatType.GROUP, ChatType.SUPERGROUP)
|
|
101
72
|
|
|
102
73
|
|
|
103
|
-
class
|
|
104
|
-
def
|
|
105
|
-
|
|
74
|
+
class IsDice(MessageRule):
|
|
75
|
+
async def check(self, message: Message) -> bool:
|
|
76
|
+
return bool(message.dice)
|
|
106
77
|
|
|
107
|
-
async def check(self, message: Message, ctx: Context) -> bool:
|
|
108
|
-
return message.chat.id in self.chat_ids
|
|
109
78
|
|
|
79
|
+
class IsDiceEmoji(MessageRule, requires=[IsDice()]):
|
|
80
|
+
def __init__(self, dice_emoji: DiceEmoji, /) -> None:
|
|
81
|
+
self.dice_emoji = dice_emoji
|
|
110
82
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
return message.chat.type == ChatType.PRIVATE
|
|
83
|
+
async def check(self, message: Message) -> bool:
|
|
84
|
+
return message.dice.unwrap().emoji == self.dice_emoji
|
|
114
85
|
|
|
115
86
|
|
|
116
|
-
class
|
|
117
|
-
async def check(self, message: Message
|
|
118
|
-
return message.
|
|
87
|
+
class IsForward(MessageRule):
|
|
88
|
+
async def check(self, message: Message) -> bool:
|
|
89
|
+
return bool(message.forward_origin)
|
|
119
90
|
|
|
120
91
|
|
|
121
|
-
class
|
|
122
|
-
|
|
123
|
-
|
|
92
|
+
class IsForwardType(MessageRule, requires=[IsForward()]):
|
|
93
|
+
def __init__(self, fwd_type: typing.Literal["user", "hidden_user", "chat", "channel"], /) -> None:
|
|
94
|
+
self.fwd_type = fwd_type
|
|
124
95
|
|
|
96
|
+
async def check(self, message: Message) -> bool:
|
|
97
|
+
return message.forward_origin.unwrap().v.type == self.fwd_type
|
|
125
98
|
|
|
126
|
-
class IsChat(MessageRule):
|
|
127
|
-
async def check(self, message: Message, ctx: Context) -> bool:
|
|
128
|
-
return message.chat.type in (ChatType.GROUP, ChatType.SUPERGROUP)
|
|
129
99
|
|
|
100
|
+
class IsReply(MessageRule):
|
|
101
|
+
async def check(self, message: Message) -> bool:
|
|
102
|
+
return bool(message.reply_to_message)
|
|
130
103
|
|
|
131
|
-
class IsDiceEmoji(MessageRule, requires=[HasDice()]):
|
|
132
|
-
def __init__(self, dice_emoji: DiceEmoji, /) -> None:
|
|
133
|
-
self.dice_emoji = dice_emoji
|
|
134
104
|
|
|
135
|
-
|
|
136
|
-
|
|
105
|
+
class IsSticker(MessageRule):
|
|
106
|
+
async def check(self, message: Message) -> bool:
|
|
107
|
+
return bool(message.sticker)
|
|
137
108
|
|
|
138
109
|
|
|
139
110
|
__all__ = (
|
|
140
111
|
"IsBot",
|
|
141
112
|
"IsChat",
|
|
142
113
|
"IsChatId",
|
|
114
|
+
"IsDice",
|
|
143
115
|
"IsDiceEmoji",
|
|
144
116
|
"IsForum",
|
|
145
117
|
"IsForward",
|
telegrinder/bot/rules/markup.py
CHANGED
|
@@ -3,10 +3,10 @@ import typing
|
|
|
3
3
|
import vbml
|
|
4
4
|
|
|
5
5
|
from telegrinder.bot.dispatch.context import Context
|
|
6
|
+
from telegrinder.node.text import Text
|
|
6
7
|
from telegrinder.tools.global_context import TelegrinderCtx
|
|
7
8
|
|
|
8
|
-
from .abc import
|
|
9
|
-
from .text import TextMessageRule
|
|
9
|
+
from .abc import ABCRule
|
|
10
10
|
|
|
11
11
|
PatternLike: typing.TypeAlias = str | vbml.Pattern
|
|
12
12
|
global_ctx = TelegrinderCtx()
|
|
@@ -23,7 +23,7 @@ def check_string(patterns: list[vbml.Pattern], s: str, ctx: Context) -> bool:
|
|
|
23
23
|
return False
|
|
24
24
|
|
|
25
25
|
|
|
26
|
-
class Markup(
|
|
26
|
+
class Markup(ABCRule):
|
|
27
27
|
def __init__(self, patterns: PatternLike | list[PatternLike], /):
|
|
28
28
|
if not isinstance(patterns, list):
|
|
29
29
|
patterns = [patterns]
|
|
@@ -31,8 +31,8 @@ class Markup(TextMessageRule):
|
|
|
31
31
|
vbml.Pattern(pattern) if isinstance(pattern, str) else pattern for pattern in patterns
|
|
32
32
|
]
|
|
33
33
|
|
|
34
|
-
async def check(self,
|
|
35
|
-
return check_string(self.patterns,
|
|
34
|
+
async def check(self, text: Text, ctx: Context) -> bool:
|
|
35
|
+
return check_string(self.patterns, text, ctx)
|
|
36
36
|
|
|
37
37
|
|
|
38
38
|
__all__ = ("Markup", "check_string")
|
telegrinder/bot/rules/mention.py
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
from telegrinder.bot.dispatch.context import Context
|
|
2
1
|
from telegrinder.types.enums import MessageEntityType
|
|
3
2
|
|
|
4
|
-
from .
|
|
3
|
+
from .message import Message, MessageRule
|
|
4
|
+
from .text import HasText
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
class HasMention(
|
|
8
|
-
async def check(self, message: Message
|
|
7
|
+
class HasMention(MessageRule, requires=[HasText()]):
|
|
8
|
+
async def check(self, message: Message) -> bool:
|
|
9
9
|
if not message.entities.unwrap_or_none():
|
|
10
10
|
return False
|
|
11
11
|
return any(entity.type == MessageEntityType.MENTION for entity in message.entities.unwrap())
|
telegrinder/bot/rules/message.py
CHANGED
|
@@ -8,7 +8,7 @@ from .adapter import EventAdapter
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class MessageRule(ABCRule[Message], abc.ABC):
|
|
11
|
-
adapter = EventAdapter(MessageEvent, Message)
|
|
11
|
+
adapter: EventAdapter[Message] = EventAdapter(MessageEvent, Message)
|
|
12
12
|
|
|
13
13
|
@abc.abstractmethod
|
|
14
14
|
async def check(self, message: Message, ctx: Context) -> bool: ...
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
|
|
3
|
+
from telegrinder.bot.dispatch.context import Context
|
|
4
|
+
from telegrinder.node import Node
|
|
5
|
+
|
|
6
|
+
from .abc import ABCRule
|
|
7
|
+
from .adapter.node import NodeAdapter
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class NodeRule(ABCRule[tuple[Node, ...]]):
|
|
11
|
+
def __init__(self, *nodes: type[Node] | tuple[str, type[Node]]) -> None:
|
|
12
|
+
bindings = [binding if isinstance(binding, tuple) else (None, binding) for binding in nodes]
|
|
13
|
+
self.nodes = [binding[1] for binding in bindings]
|
|
14
|
+
self.node_keys = [binding[0] for binding in bindings]
|
|
15
|
+
|
|
16
|
+
@property
|
|
17
|
+
def adapter(self) -> NodeAdapter:
|
|
18
|
+
return NodeAdapter(*self.nodes)
|
|
19
|
+
|
|
20
|
+
async def check(self, resolved_nodes: tuple[Node, ...], ctx: Context) -> typing.Literal[True]:
|
|
21
|
+
for i, node in enumerate(resolved_nodes):
|
|
22
|
+
if key := self.node_keys[i]:
|
|
23
|
+
ctx[key] = node
|
|
24
|
+
return True
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
__all__ = ("NodeRule",)
|
telegrinder/bot/rules/regex.py
CHANGED
|
@@ -2,14 +2,14 @@ import re
|
|
|
2
2
|
import typing
|
|
3
3
|
|
|
4
4
|
from telegrinder.bot.dispatch.context import Context
|
|
5
|
+
from telegrinder.node.text import Text
|
|
5
6
|
|
|
6
|
-
from .abc import
|
|
7
|
-
from .text import TextMessageRule
|
|
7
|
+
from .abc import ABCRule
|
|
8
8
|
|
|
9
9
|
PatternLike: typing.TypeAlias = str | typing.Pattern[str]
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
class Regex(
|
|
12
|
+
class Regex(ABCRule):
|
|
13
13
|
def __init__(self, regexp: PatternLike | list[PatternLike]):
|
|
14
14
|
self.regexp: list[re.Pattern[str]] = []
|
|
15
15
|
match regexp:
|
|
@@ -22,9 +22,9 @@ class Regex(TextMessageRule):
|
|
|
22
22
|
re.compile(regexp) if isinstance(regexp, str) else regexp for regexp in regexp
|
|
23
23
|
)
|
|
24
24
|
|
|
25
|
-
async def check(self,
|
|
25
|
+
async def check(self, text: Text, ctx: Context) -> bool:
|
|
26
26
|
for regexp in self.regexp:
|
|
27
|
-
response = re.match(regexp,
|
|
27
|
+
response = re.match(regexp, text)
|
|
28
28
|
if response is not None:
|
|
29
29
|
if matches := response.groupdict():
|
|
30
30
|
ctx |= matches
|
|
@@ -3,7 +3,7 @@ import typing
|
|
|
3
3
|
|
|
4
4
|
from telegrinder.bot.dispatch.context import Context
|
|
5
5
|
|
|
6
|
-
from .abc import ABCRule,
|
|
6
|
+
from .abc import ABCRule, Update, check_rule
|
|
7
7
|
from .func import FuncRule
|
|
8
8
|
|
|
9
9
|
|
|
@@ -17,10 +17,10 @@ class RuleEnumState:
|
|
|
17
17
|
return self.cls == other.cls and self.name == other.name
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
class RuleEnum(ABCRule
|
|
20
|
+
class RuleEnum(ABCRule):
|
|
21
21
|
__enum__: list[RuleEnumState]
|
|
22
22
|
|
|
23
|
-
def __init_subclass__(cls, *args, **kwargs):
|
|
23
|
+
def __init_subclass__(cls, *args: typing.Any, **kwargs: typing.Any) -> None:
|
|
24
24
|
new_attributes = set(cls.__dict__) - set(RuleEnum.__dict__) - {"__enum__", "__init__"}
|
|
25
25
|
enum_lst: list[RuleEnumState] = []
|
|
26
26
|
|
|
@@ -34,7 +34,7 @@ class RuleEnum(ABCRule[T]):
|
|
|
34
34
|
setattr(
|
|
35
35
|
self,
|
|
36
36
|
attribute.name,
|
|
37
|
-
self & FuncRule(lambda _, ctx: self.must_be_state(ctx, attribute)),
|
|
37
|
+
self & FuncRule(lambda _, ctx: self.must_be_state(ctx, attribute)), # type: ignore
|
|
38
38
|
)
|
|
39
39
|
enum_lst.append(attribute)
|
|
40
40
|
|
telegrinder/bot/rules/start.py
CHANGED
|
@@ -4,7 +4,7 @@ from telegrinder.bot.dispatch.context import Context
|
|
|
4
4
|
from telegrinder.types.enums import MessageEntityType
|
|
5
5
|
|
|
6
6
|
from .is_from import IsPrivate
|
|
7
|
-
from .markup import Markup
|
|
7
|
+
from .markup import Markup
|
|
8
8
|
from .message import MessageRule
|
|
9
9
|
from .message_entities import MessageEntities
|
|
10
10
|
|
|
@@ -12,9 +12,9 @@ from .message_entities import MessageEntities
|
|
|
12
12
|
class StartCommand(
|
|
13
13
|
MessageRule,
|
|
14
14
|
requires=[
|
|
15
|
-
IsPrivate()
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
IsPrivate(),
|
|
16
|
+
MessageEntities(MessageEntityType.BOT_COMMAND),
|
|
17
|
+
Markup(["/start <param>", "/start"]),
|
|
18
18
|
],
|
|
19
19
|
):
|
|
20
20
|
def __init__(
|
|
@@ -28,7 +28,7 @@ class StartCommand(
|
|
|
28
28
|
self.validator = validator
|
|
29
29
|
self.alias = alias
|
|
30
30
|
|
|
31
|
-
async def check(self,
|
|
31
|
+
async def check(self, ctx: Context) -> bool:
|
|
32
32
|
param: str | None = ctx.pop("param", None)
|
|
33
33
|
validated_param = self.validator(param) if self.validator and param is not None else param
|
|
34
34
|
|