telegrinder 1.0.0rc1__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.
- telegrinder/__init__.py +258 -0
- telegrinder/__meta__.py +1 -0
- telegrinder/api/__init__.py +15 -0
- telegrinder/api/api.py +175 -0
- telegrinder/api/error.py +50 -0
- telegrinder/api/response.py +23 -0
- telegrinder/api/token.py +30 -0
- telegrinder/api/validators.py +30 -0
- telegrinder/bot/__init__.py +144 -0
- telegrinder/bot/bot.py +70 -0
- telegrinder/bot/cute_types/__init__.py +41 -0
- telegrinder/bot/cute_types/base.py +228 -0
- telegrinder/bot/cute_types/base.pyi +49 -0
- telegrinder/bot/cute_types/business_connection.py +9 -0
- telegrinder/bot/cute_types/business_messages_deleted.py +9 -0
- telegrinder/bot/cute_types/callback_query.py +248 -0
- telegrinder/bot/cute_types/chat_boost_removed.py +9 -0
- telegrinder/bot/cute_types/chat_boost_updated.py +9 -0
- telegrinder/bot/cute_types/chat_join_request.py +59 -0
- telegrinder/bot/cute_types/chat_member_updated.py +158 -0
- telegrinder/bot/cute_types/chosen_inline_result.py +11 -0
- telegrinder/bot/cute_types/inline_query.py +41 -0
- telegrinder/bot/cute_types/message.py +2809 -0
- telegrinder/bot/cute_types/message_reaction_count_updated.py +9 -0
- telegrinder/bot/cute_types/message_reaction_updated.py +9 -0
- telegrinder/bot/cute_types/paid_media_purchased.py +11 -0
- telegrinder/bot/cute_types/poll.py +9 -0
- telegrinder/bot/cute_types/poll_answer.py +9 -0
- telegrinder/bot/cute_types/pre_checkout_query.py +36 -0
- telegrinder/bot/cute_types/shipping_query.py +11 -0
- telegrinder/bot/cute_types/update.py +209 -0
- telegrinder/bot/cute_types/utils.py +141 -0
- telegrinder/bot/dispatch/__init__.py +99 -0
- telegrinder/bot/dispatch/abc.py +74 -0
- telegrinder/bot/dispatch/action.py +99 -0
- telegrinder/bot/dispatch/context.py +162 -0
- telegrinder/bot/dispatch/dispatch.py +362 -0
- telegrinder/bot/dispatch/handler/__init__.py +23 -0
- telegrinder/bot/dispatch/handler/abc.py +25 -0
- telegrinder/bot/dispatch/handler/audio_reply.py +43 -0
- telegrinder/bot/dispatch/handler/base.py +34 -0
- telegrinder/bot/dispatch/handler/document_reply.py +43 -0
- telegrinder/bot/dispatch/handler/func.py +73 -0
- telegrinder/bot/dispatch/handler/media_group_reply.py +43 -0
- telegrinder/bot/dispatch/handler/message_reply.py +35 -0
- telegrinder/bot/dispatch/handler/photo_reply.py +43 -0
- telegrinder/bot/dispatch/handler/sticker_reply.py +36 -0
- telegrinder/bot/dispatch/handler/video_reply.py +43 -0
- telegrinder/bot/dispatch/middleware/__init__.py +13 -0
- telegrinder/bot/dispatch/middleware/abc.py +112 -0
- telegrinder/bot/dispatch/middleware/box.py +32 -0
- telegrinder/bot/dispatch/middleware/filter.py +88 -0
- telegrinder/bot/dispatch/middleware/media_group.py +69 -0
- telegrinder/bot/dispatch/process.py +93 -0
- telegrinder/bot/dispatch/return_manager/__init__.py +21 -0
- telegrinder/bot/dispatch/return_manager/abc.py +107 -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 +34 -0
- telegrinder/bot/dispatch/return_manager/pre_checkout_query.py +19 -0
- telegrinder/bot/dispatch/return_manager/utils.py +20 -0
- telegrinder/bot/dispatch/router/__init__.py +4 -0
- telegrinder/bot/dispatch/router/abc.py +15 -0
- telegrinder/bot/dispatch/router/base.py +154 -0
- telegrinder/bot/dispatch/view/__init__.py +15 -0
- telegrinder/bot/dispatch/view/abc.py +15 -0
- telegrinder/bot/dispatch/view/base.py +226 -0
- telegrinder/bot/dispatch/view/box.py +207 -0
- telegrinder/bot/dispatch/view/media_group.py +25 -0
- telegrinder/bot/dispatch/waiter_machine/__init__.py +25 -0
- telegrinder/bot/dispatch/waiter_machine/actions.py +16 -0
- telegrinder/bot/dispatch/waiter_machine/hasher/__init__.py +13 -0
- telegrinder/bot/dispatch/waiter_machine/hasher/callback.py +53 -0
- telegrinder/bot/dispatch/waiter_machine/hasher/hasher.py +61 -0
- telegrinder/bot/dispatch/waiter_machine/hasher/message.py +49 -0
- telegrinder/bot/dispatch/waiter_machine/machine.py +264 -0
- telegrinder/bot/dispatch/waiter_machine/middleware.py +77 -0
- telegrinder/bot/dispatch/waiter_machine/short_state.py +105 -0
- telegrinder/bot/polling/__init__.py +4 -0
- telegrinder/bot/polling/abc.py +25 -0
- telegrinder/bot/polling/error_handler.py +93 -0
- telegrinder/bot/polling/polling.py +167 -0
- telegrinder/bot/polling/utils.py +12 -0
- telegrinder/bot/rules/__init__.py +166 -0
- telegrinder/bot/rules/abc.py +150 -0
- telegrinder/bot/rules/button.py +20 -0
- telegrinder/bot/rules/callback_data.py +109 -0
- telegrinder/bot/rules/chat_join.py +28 -0
- telegrinder/bot/rules/chat_member_updated.py +145 -0
- telegrinder/bot/rules/command.py +137 -0
- telegrinder/bot/rules/enum_text.py +29 -0
- telegrinder/bot/rules/func.py +21 -0
- telegrinder/bot/rules/fuzzy.py +21 -0
- telegrinder/bot/rules/inline.py +45 -0
- telegrinder/bot/rules/integer.py +19 -0
- telegrinder/bot/rules/is_from.py +213 -0
- telegrinder/bot/rules/logic.py +22 -0
- telegrinder/bot/rules/magic.py +60 -0
- telegrinder/bot/rules/markup.py +51 -0
- telegrinder/bot/rules/media.py +13 -0
- telegrinder/bot/rules/mention.py +15 -0
- telegrinder/bot/rules/message_entities.py +37 -0
- telegrinder/bot/rules/node.py +43 -0
- telegrinder/bot/rules/payload.py +89 -0
- telegrinder/bot/rules/payment_invoice.py +14 -0
- telegrinder/bot/rules/regex.py +34 -0
- telegrinder/bot/rules/rule_enum.py +71 -0
- telegrinder/bot/rules/start.py +73 -0
- telegrinder/bot/rules/state.py +35 -0
- telegrinder/bot/rules/text.py +27 -0
- telegrinder/bot/rules/update.py +14 -0
- telegrinder/bot/scenario/__init__.py +5 -0
- telegrinder/bot/scenario/abc.py +16 -0
- telegrinder/bot/scenario/checkbox.py +183 -0
- telegrinder/bot/scenario/choice.py +44 -0
- telegrinder/client/__init__.py +11 -0
- telegrinder/client/abc.py +136 -0
- telegrinder/client/form_data.py +34 -0
- telegrinder/client/rnet.py +198 -0
- telegrinder/model.py +133 -0
- telegrinder/model.pyi +57 -0
- telegrinder/modules.py +1081 -0
- telegrinder/msgspec_utils/__init__.py +42 -0
- telegrinder/msgspec_utils/abc.py +16 -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 +61 -0
- telegrinder/msgspec_utils/custom_types/literal.py +25 -0
- telegrinder/msgspec_utils/custom_types/option.py +17 -0
- telegrinder/msgspec_utils/decoder.py +388 -0
- telegrinder/msgspec_utils/encoder.py +204 -0
- telegrinder/msgspec_utils/json.py +15 -0
- telegrinder/msgspec_utils/tools.py +80 -0
- telegrinder/node/__init__.py +80 -0
- telegrinder/node/compose.py +193 -0
- telegrinder/node/nodes/__init__.py +96 -0
- telegrinder/node/nodes/attachment.py +169 -0
- telegrinder/node/nodes/callback_query.py +25 -0
- telegrinder/node/nodes/channel.py +97 -0
- telegrinder/node/nodes/command.py +33 -0
- telegrinder/node/nodes/error.py +43 -0
- telegrinder/node/nodes/event.py +70 -0
- telegrinder/node/nodes/file.py +39 -0
- telegrinder/node/nodes/global_node.py +66 -0
- telegrinder/node/nodes/i18n.py +110 -0
- telegrinder/node/nodes/me.py +26 -0
- telegrinder/node/nodes/message_entities.py +15 -0
- telegrinder/node/nodes/payload.py +84 -0
- telegrinder/node/nodes/reply_message.py +14 -0
- telegrinder/node/nodes/source.py +172 -0
- telegrinder/node/nodes/state_mutator.py +71 -0
- telegrinder/node/nodes/text.py +62 -0
- telegrinder/node/scope.py +88 -0
- telegrinder/node/utils.py +38 -0
- telegrinder/py.typed +0 -0
- telegrinder/rules.py +1 -0
- telegrinder/tools/__init__.py +183 -0
- telegrinder/tools/aio.py +147 -0
- telegrinder/tools/final.py +21 -0
- telegrinder/tools/formatting/__init__.py +85 -0
- telegrinder/tools/formatting/deep_links/__init__.py +39 -0
- telegrinder/tools/formatting/deep_links/links.py +468 -0
- telegrinder/tools/formatting/deep_links/parsing.py +88 -0
- telegrinder/tools/formatting/deep_links/validators.py +8 -0
- telegrinder/tools/formatting/html.py +241 -0
- telegrinder/tools/fullname.py +82 -0
- telegrinder/tools/global_context/__init__.py +13 -0
- telegrinder/tools/global_context/abc.py +63 -0
- telegrinder/tools/global_context/builtin_context.py +45 -0
- telegrinder/tools/global_context/global_context.py +614 -0
- telegrinder/tools/input_file_directory.py +30 -0
- telegrinder/tools/keyboard/__init__.py +6 -0
- telegrinder/tools/keyboard/abc.py +84 -0
- telegrinder/tools/keyboard/base.py +108 -0
- telegrinder/tools/keyboard/button.py +181 -0
- telegrinder/tools/keyboard/data.py +31 -0
- telegrinder/tools/keyboard/keyboard.py +160 -0
- telegrinder/tools/keyboard/utils.py +95 -0
- telegrinder/tools/lifespan.py +188 -0
- telegrinder/tools/limited_dict.py +35 -0
- telegrinder/tools/loop_wrapper.py +271 -0
- telegrinder/tools/magic/__init__.py +29 -0
- telegrinder/tools/magic/annotations.py +172 -0
- telegrinder/tools/magic/descriptors.py +57 -0
- telegrinder/tools/magic/function.py +254 -0
- telegrinder/tools/magic/inspect.py +16 -0
- telegrinder/tools/magic/shortcut.py +107 -0
- telegrinder/tools/member_descriptor_proxy.py +95 -0
- telegrinder/tools/parse_mode.py +12 -0
- telegrinder/tools/serialization/__init__.py +5 -0
- telegrinder/tools/serialization/abc.py +34 -0
- telegrinder/tools/serialization/json_ser.py +60 -0
- telegrinder/tools/serialization/msgpack_ser.py +197 -0
- telegrinder/tools/serialization/utils.py +18 -0
- telegrinder/tools/singleton/__init__.py +4 -0
- telegrinder/tools/singleton/abc.py +14 -0
- telegrinder/tools/singleton/singleton.py +18 -0
- telegrinder/tools/state_mutator/__init__.py +4 -0
- telegrinder/tools/state_mutator/mutation.py +85 -0
- telegrinder/tools/state_storage/__init__.py +4 -0
- telegrinder/tools/state_storage/abc.py +38 -0
- telegrinder/tools/state_storage/memory.py +27 -0
- telegrinder/tools/strings.py +22 -0
- telegrinder/types/__init__.py +323 -0
- telegrinder/types/enums.py +754 -0
- telegrinder/types/input_file.py +51 -0
- telegrinder/types/methods.py +6143 -0
- telegrinder/types/methods_utils.py +66 -0
- telegrinder/types/objects.py +8184 -0
- telegrinder/types/webapp.py +129 -0
- telegrinder/verification_utils.py +35 -0
- telegrinder-1.0.0rc1.dist-info/METADATA +166 -0
- telegrinder-1.0.0rc1.dist-info/RECORD +215 -0
- telegrinder-1.0.0rc1.dist-info/WHEEL +4 -0
- telegrinder-1.0.0rc1.dist-info/licenses/LICENSE +22 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
import enum
|
|
3
|
+
import typing
|
|
4
|
+
|
|
5
|
+
from telegrinder.bot.cute_types.chat_member_updated import ChatMemberUpdatedCute
|
|
6
|
+
from telegrinder.bot.rules.abc import ABCRule
|
|
7
|
+
from telegrinder.types.objects import (
|
|
8
|
+
ChatMemberAdministrator,
|
|
9
|
+
ChatMemberBanned,
|
|
10
|
+
ChatMemberLeft,
|
|
11
|
+
ChatMemberMember,
|
|
12
|
+
ChatMemberOwner,
|
|
13
|
+
ChatMemberRestricted,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
type _MemberStatus = MemberStatusFlag | MemberStatusUpdating
|
|
17
|
+
type ChatMember = typing.Union[
|
|
18
|
+
ChatMemberAdministrator,
|
|
19
|
+
ChatMemberBanned,
|
|
20
|
+
ChatMemberLeft,
|
|
21
|
+
ChatMemberMember,
|
|
22
|
+
ChatMemberOwner,
|
|
23
|
+
ChatMemberRestricted,
|
|
24
|
+
]
|
|
25
|
+
type ChatMemberUpdated = ChatMemberUpdatedCute
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclasses.dataclass(frozen=True, slots=True)
|
|
29
|
+
class MemberStatusUpdating:
|
|
30
|
+
old: MemberStatusFlag
|
|
31
|
+
new: MemberStatusFlag
|
|
32
|
+
|
|
33
|
+
def __invert__(self) -> typing.Self:
|
|
34
|
+
return dataclasses.replace(self, old=self.new, new=self.old)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class MemberStatusFlag(enum.IntFlag):
|
|
38
|
+
CREATOR = enum.auto()
|
|
39
|
+
ADMINISTRATOR = enum.auto()
|
|
40
|
+
MEMBER = enum.auto()
|
|
41
|
+
LEFT = enum.auto()
|
|
42
|
+
KICKED = enum.auto()
|
|
43
|
+
RESTRICTED = enum.auto()
|
|
44
|
+
RESTRICTED_NOT_MEMBER = enum.auto()
|
|
45
|
+
|
|
46
|
+
RESTRICTED_MEMBER = MEMBER | RESTRICTED
|
|
47
|
+
|
|
48
|
+
IS_MEMBER = CREATOR | ADMINISTRATOR | MEMBER | RESTRICTED
|
|
49
|
+
IS_NOT_MEMBER = LEFT | KICKED | RESTRICTED_NOT_MEMBER
|
|
50
|
+
|
|
51
|
+
IS_ADMIN = CREATOR | ADMINISTRATOR
|
|
52
|
+
IS_NOT_ADMIN = RESTRICTED_MEMBER | IS_NOT_MEMBER
|
|
53
|
+
|
|
54
|
+
def __rshift__(self, other: object, /) -> MemberStatusUpdating:
|
|
55
|
+
if not isinstance(other, type(self)):
|
|
56
|
+
return NotImplemented
|
|
57
|
+
return MemberStatusUpdating(old=self, new=other)
|
|
58
|
+
|
|
59
|
+
def __lshift__(self, other: object, /) -> MemberStatusUpdating:
|
|
60
|
+
if not isinstance(other, type(self)):
|
|
61
|
+
return NotImplemented
|
|
62
|
+
return MemberStatusUpdating(old=other, new=self)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class MemberStatus:
|
|
66
|
+
CREATOR = MemberStatusFlag.CREATOR
|
|
67
|
+
ADMINISTRATOR = MemberStatusFlag.ADMINISTRATOR
|
|
68
|
+
MEMBER = MemberStatusFlag.MEMBER
|
|
69
|
+
LEFT = MemberStatusFlag.LEFT
|
|
70
|
+
KICKED = MemberStatusFlag.KICKED
|
|
71
|
+
RESTRICTED = MemberStatusFlag.RESTRICTED_MEMBER | MemberStatusFlag.RESTRICTED_NOT_MEMBER
|
|
72
|
+
|
|
73
|
+
RESTRICTED_MEMBER = MemberStatusFlag.RESTRICTED_MEMBER
|
|
74
|
+
RESTRICTED_NOT_MEMBER = MemberStatusFlag.RESTRICTED_NOT_MEMBER
|
|
75
|
+
|
|
76
|
+
IS_MEMBER = MemberStatusFlag.IS_MEMBER
|
|
77
|
+
IS_NOT_MEMBER = ~IS_MEMBER
|
|
78
|
+
|
|
79
|
+
IS_ADMIN = MemberStatusFlag.IS_ADMIN
|
|
80
|
+
IS_NOT_ADMIN = ~IS_ADMIN
|
|
81
|
+
|
|
82
|
+
JOIN = IS_NOT_MEMBER >> IS_MEMBER
|
|
83
|
+
LEAVE = ~JOIN
|
|
84
|
+
PROMOTED = (RESTRICTED_MEMBER | IS_NOT_MEMBER) >> ADMINISTRATOR
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class ChatMemberUpdatedRule(ABCRule):
|
|
88
|
+
def __init__(self, status: _MemberStatus, /) -> None:
|
|
89
|
+
self.status = status
|
|
90
|
+
|
|
91
|
+
def check_status(
|
|
92
|
+
self,
|
|
93
|
+
member: ChatMember,
|
|
94
|
+
status: MemberStatusFlag,
|
|
95
|
+
checking_status: MemberStatusFlag,
|
|
96
|
+
) -> bool:
|
|
97
|
+
if (
|
|
98
|
+
MemberStatusFlag.RESTRICTED_MEMBER in checking_status
|
|
99
|
+
or MemberStatusFlag.RESTRICTED_NOT_MEMBER in checking_status
|
|
100
|
+
) and status == MemberStatusFlag.RESTRICTED:
|
|
101
|
+
return (
|
|
102
|
+
True
|
|
103
|
+
if MemberStatus.RESTRICTED in checking_status
|
|
104
|
+
else (getattr(member, "is_member", None) is (MemberStatusFlag.RESTRICTED_MEMBER in checking_status))
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
return status == checking_status or status in checking_status
|
|
108
|
+
|
|
109
|
+
def check(self, event: ChatMemberUpdated) -> bool:
|
|
110
|
+
checking_new_status, checking_old_status = (
|
|
111
|
+
(
|
|
112
|
+
self.status.new,
|
|
113
|
+
self.status.old,
|
|
114
|
+
)
|
|
115
|
+
if isinstance(self.status, MemberStatusUpdating)
|
|
116
|
+
else (self.status, None)
|
|
117
|
+
)
|
|
118
|
+
new_member, old_member = event.new_chat_member.v, event.old_chat_member.v
|
|
119
|
+
new_member_status, old_member_status = (
|
|
120
|
+
MEMBER_STATUS_MAP[new_member.status],
|
|
121
|
+
MEMBER_STATUS_MAP[old_member.status],
|
|
122
|
+
)
|
|
123
|
+
return (
|
|
124
|
+
all(
|
|
125
|
+
(
|
|
126
|
+
self.check_status(old_member, old_member_status, checking_old_status),
|
|
127
|
+
self.check_status(new_member, new_member_status, checking_new_status),
|
|
128
|
+
),
|
|
129
|
+
)
|
|
130
|
+
if checking_old_status is not None
|
|
131
|
+
else self.check_status(new_member, new_member_status, checking_new_status)
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
MEMBER_STATUS_MAP: typing.Final = {
|
|
136
|
+
"creator": MemberStatusFlag.CREATOR,
|
|
137
|
+
"administrator": MemberStatusFlag.ADMINISTRATOR,
|
|
138
|
+
"member": MemberStatusFlag.MEMBER,
|
|
139
|
+
"left": MemberStatusFlag.LEFT,
|
|
140
|
+
"kicked": MemberStatusFlag.KICKED,
|
|
141
|
+
"restricted": MemberStatusFlag.RESTRICTED,
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
__all__ = ("ChatMemberUpdatedRule", "MemberStatus")
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
import typing
|
|
3
|
+
|
|
4
|
+
from telegrinder.bot.dispatch.context import Context
|
|
5
|
+
from telegrinder.bot.rules.abc import ABCRule
|
|
6
|
+
from telegrinder.node.nodes.command import CommandInfo, single_split
|
|
7
|
+
from telegrinder.node.nodes.me import Me
|
|
8
|
+
from telegrinder.node.nodes.source import ChatSource
|
|
9
|
+
from telegrinder.types.enums import ChatType
|
|
10
|
+
|
|
11
|
+
type Validator = typing.Callable[[str], typing.Any | None] | typing.Callable[[typing.Any], typing.Any | None]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclasses.dataclass(frozen=True, slots=True)
|
|
15
|
+
class Argument:
|
|
16
|
+
name: str
|
|
17
|
+
validators: list[Validator] = dataclasses.field(default_factory=lambda: [])
|
|
18
|
+
optional: bool = dataclasses.field(default=False, kw_only=True)
|
|
19
|
+
|
|
20
|
+
def check(self, data: str) -> typing.Any | None:
|
|
21
|
+
if not data:
|
|
22
|
+
return None
|
|
23
|
+
|
|
24
|
+
result = data
|
|
25
|
+
for validator in self.validators:
|
|
26
|
+
result = validator(result)
|
|
27
|
+
if result is None:
|
|
28
|
+
return None
|
|
29
|
+
|
|
30
|
+
return result
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class Command(ABCRule):
|
|
34
|
+
names: typing.Iterable[str]
|
|
35
|
+
|
|
36
|
+
def __init__(
|
|
37
|
+
self,
|
|
38
|
+
names: str | typing.Iterable[str],
|
|
39
|
+
*arguments: Argument,
|
|
40
|
+
prefixes: tuple[str, ...] = ("/",),
|
|
41
|
+
separator: str = " ",
|
|
42
|
+
lazy: bool = False,
|
|
43
|
+
validate_mention: bool = True,
|
|
44
|
+
mention_needed_in_chat: bool = False,
|
|
45
|
+
ignore_case: bool = False,
|
|
46
|
+
) -> None:
|
|
47
|
+
names = [names] if isinstance(names, str) else names
|
|
48
|
+
self.names = [n.lower() for n in names] if ignore_case else names
|
|
49
|
+
self.arguments = arguments
|
|
50
|
+
self.prefixes = prefixes
|
|
51
|
+
self.separator = separator
|
|
52
|
+
self.lazy = lazy
|
|
53
|
+
self.validate_mention = validate_mention
|
|
54
|
+
self.ignore_case = ignore_case
|
|
55
|
+
|
|
56
|
+
# if true then we'll check for mention when message is from a group
|
|
57
|
+
self.mention_needed_in_chat = mention_needed_in_chat
|
|
58
|
+
|
|
59
|
+
def remove_prefix(self, text: str) -> str | None:
|
|
60
|
+
for prefix in self.prefixes:
|
|
61
|
+
if text.startswith(prefix):
|
|
62
|
+
return text.removeprefix(prefix)
|
|
63
|
+
return None
|
|
64
|
+
|
|
65
|
+
def parse_argument(
|
|
66
|
+
self,
|
|
67
|
+
arguments: list[Argument],
|
|
68
|
+
data_s: str,
|
|
69
|
+
new_s: str,
|
|
70
|
+
s: str,
|
|
71
|
+
) -> dict[str, typing.Any] | None:
|
|
72
|
+
argument = arguments[0]
|
|
73
|
+
data = argument.check(data_s)
|
|
74
|
+
if data is None and not argument.optional:
|
|
75
|
+
return None
|
|
76
|
+
|
|
77
|
+
if data is None:
|
|
78
|
+
return self.parse_arguments(arguments[1:], s)
|
|
79
|
+
|
|
80
|
+
with_argument = self.parse_arguments(arguments[1:], new_s)
|
|
81
|
+
if with_argument is not None:
|
|
82
|
+
return {argument.name: data, **with_argument}
|
|
83
|
+
|
|
84
|
+
if not argument.optional:
|
|
85
|
+
return None
|
|
86
|
+
|
|
87
|
+
return self.parse_arguments(arguments[1:], s)
|
|
88
|
+
|
|
89
|
+
def parse_arguments(self, arguments: list[Argument], s: str) -> dict[str, typing.Any] | None:
|
|
90
|
+
if not arguments:
|
|
91
|
+
return {} if not s else None
|
|
92
|
+
|
|
93
|
+
if self.lazy:
|
|
94
|
+
return self.parse_argument(arguments, *single_split(s, self.separator), s)
|
|
95
|
+
|
|
96
|
+
all_split = s.split(self.separator)
|
|
97
|
+
for i in range(1, len(all_split) + 1):
|
|
98
|
+
ctx = self.parse_argument(
|
|
99
|
+
arguments,
|
|
100
|
+
self.separator.join(all_split[:i]),
|
|
101
|
+
self.separator.join(all_split[i:]),
|
|
102
|
+
s,
|
|
103
|
+
)
|
|
104
|
+
if ctx is not None:
|
|
105
|
+
return ctx
|
|
106
|
+
|
|
107
|
+
return None
|
|
108
|
+
|
|
109
|
+
def check(self, command: CommandInfo, me: Me, chat: ChatSource, ctx: Context) -> bool:
|
|
110
|
+
name = self.remove_prefix(command.name)
|
|
111
|
+
if name is None:
|
|
112
|
+
return False
|
|
113
|
+
|
|
114
|
+
target_name = name.lower() if self.ignore_case else name
|
|
115
|
+
|
|
116
|
+
if target_name not in self.names:
|
|
117
|
+
return False
|
|
118
|
+
|
|
119
|
+
if not command.mention and self.mention_needed_in_chat and chat.type is not ChatType.PRIVATE:
|
|
120
|
+
return False
|
|
121
|
+
|
|
122
|
+
if command.mention and self.validate_mention: # noqa
|
|
123
|
+
if command.mention.unwrap().lower() != me.username.unwrap().lower():
|
|
124
|
+
return False
|
|
125
|
+
|
|
126
|
+
if not self.arguments:
|
|
127
|
+
return not command.arguments
|
|
128
|
+
|
|
129
|
+
result = self.parse_arguments(list(self.arguments), command.arguments)
|
|
130
|
+
if result is None:
|
|
131
|
+
return False
|
|
132
|
+
|
|
133
|
+
ctx |= result
|
|
134
|
+
return True
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
__all__ = ("Argument", "Command", "single_split")
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import enum
|
|
2
|
+
|
|
3
|
+
from telegrinder.bot.dispatch.context import Context
|
|
4
|
+
from telegrinder.bot.rules.abc import ABCRule
|
|
5
|
+
from telegrinder.node.nodes.text import Text
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class EnumTextRule[T: enum.Enum](ABCRule):
|
|
9
|
+
def __init__(self, enum_t: type[T], *, lower_case: bool = True) -> None:
|
|
10
|
+
self.lower_case = lower_case
|
|
11
|
+
self.enum_t = enum_t
|
|
12
|
+
self.enumerations_map = {
|
|
13
|
+
(enumeration.value.lower() if lower_case else enumeration.value): enumeration for enumeration in self.enum_t
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
def find(self, s: str) -> T | None:
|
|
17
|
+
s = s.lower() if self.lower_case else s
|
|
18
|
+
return self.enumerations_map.get(s)
|
|
19
|
+
|
|
20
|
+
def check(self, text: Text, ctx: Context) -> bool:
|
|
21
|
+
match self.find(text):
|
|
22
|
+
case enum.Enum() as enumeration:
|
|
23
|
+
ctx.enum_text = enumeration
|
|
24
|
+
return True
|
|
25
|
+
case _:
|
|
26
|
+
return False
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
__all__ = ("EnumTextRule",)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
|
|
3
|
+
from telegrinder.bot.dispatch.context import Context
|
|
4
|
+
from telegrinder.bot.rules.abc import ABCRule
|
|
5
|
+
from telegrinder.tools.aio import maybe_awaitable
|
|
6
|
+
from telegrinder.types.objects import Update
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class FuncRule(ABCRule):
|
|
10
|
+
def __init__(
|
|
11
|
+
self,
|
|
12
|
+
func: typing.Callable[[Update, Context], typing.Awaitable[bool] | bool],
|
|
13
|
+
/,
|
|
14
|
+
) -> None:
|
|
15
|
+
self.func = func
|
|
16
|
+
|
|
17
|
+
async def check(self, update: Update, ctx: Context) -> bool:
|
|
18
|
+
return await maybe_awaitable(self.func(update, ctx))
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
__all__ = ("FuncRule",)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import difflib
|
|
2
|
+
|
|
3
|
+
from telegrinder.bot.dispatch.context import Context
|
|
4
|
+
from telegrinder.bot.rules.abc import ABCRule
|
|
5
|
+
from telegrinder.node.nodes.text import Text
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class FuzzyText(ABCRule):
|
|
9
|
+
def __init__(self, texts: str | list[str], /, *, min_ratio: float = 0.7) -> None:
|
|
10
|
+
self.texts = (texts,) if isinstance(texts, str) else tuple(texts)
|
|
11
|
+
self.min_ratio = min_ratio
|
|
12
|
+
|
|
13
|
+
def check(self, message_text: Text, ctx: Context) -> bool:
|
|
14
|
+
match = max(difflib.SequenceMatcher(a=message_text, b=text).ratio() for text in self.texts)
|
|
15
|
+
if match < self.min_ratio:
|
|
16
|
+
return False
|
|
17
|
+
ctx.fuzzy_ratio = match
|
|
18
|
+
return True
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
__all__ = ("FuzzyText",)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from telegrinder.bot.cute_types.inline_query import InlineQueryCute
|
|
2
|
+
from telegrinder.bot.dispatch.context import Context
|
|
3
|
+
from telegrinder.bot.rules.abc import ABCRule
|
|
4
|
+
from telegrinder.bot.rules.markup import Markup, PatternLike, check_string
|
|
5
|
+
from telegrinder.types.enums import ChatType
|
|
6
|
+
|
|
7
|
+
type InlineQuery = InlineQueryCute
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class HasLocation(ABCRule):
|
|
11
|
+
def check(self, query: InlineQuery) -> bool:
|
|
12
|
+
return bool(query.location)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class InlineQueryChatType(ABCRule):
|
|
16
|
+
def __init__(self, chat_type: ChatType, /) -> None:
|
|
17
|
+
self.chat_type = chat_type
|
|
18
|
+
|
|
19
|
+
def check(self, query: InlineQuery) -> bool:
|
|
20
|
+
return query.chat_type.map(lambda x: x == self.chat_type).unwrap_or(False)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class InlineQueryText(ABCRule):
|
|
24
|
+
def __init__(self, texts: str | list[str], *, lower_case: bool = False) -> None:
|
|
25
|
+
self.texts = {text.lower() if lower_case else text for text in ([texts] if isinstance(texts, str) else texts)}
|
|
26
|
+
self.lower_case = lower_case
|
|
27
|
+
|
|
28
|
+
def check(self, query: InlineQuery) -> bool:
|
|
29
|
+
return (query.query.lower() if self.lower_case else query.query) in self.texts
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class InlineQueryMarkup(ABCRule):
|
|
33
|
+
def __init__(self, patterns: PatternLike | list[PatternLike], /) -> None:
|
|
34
|
+
self.patterns = Markup(patterns).patterns
|
|
35
|
+
|
|
36
|
+
def check(self, query: InlineQuery, ctx: Context) -> bool:
|
|
37
|
+
return check_string(self.patterns, query.query, ctx)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
__all__ = (
|
|
41
|
+
"HasLocation",
|
|
42
|
+
"InlineQueryChatType",
|
|
43
|
+
"InlineQueryMarkup",
|
|
44
|
+
"InlineQueryText",
|
|
45
|
+
)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from telegrinder.bot.rules.abc import ABCRule
|
|
2
|
+
from telegrinder.bot.rules.node import NodeRule
|
|
3
|
+
from telegrinder.node.nodes.text import TextInteger
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class IsInteger(NodeRule):
|
|
7
|
+
def __init__(self) -> None:
|
|
8
|
+
super().__init__(TextInteger)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class IntegerInRange(ABCRule):
|
|
12
|
+
def __init__(self, rng: range) -> None:
|
|
13
|
+
self.rng = rng
|
|
14
|
+
|
|
15
|
+
def check(self, integer: TextInteger) -> bool:
|
|
16
|
+
return integer in self.rng
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
__all__ = ("IntegerInRange", "IsInteger")
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
|
|
3
|
+
from telegrinder.bot.cute_types.message import MessageCute
|
|
4
|
+
from telegrinder.bot.rules.abc import ABCRule, Always
|
|
5
|
+
from telegrinder.node.nodes.source import ChatSource, UserSource
|
|
6
|
+
from telegrinder.types.enums import ChatType, DiceEmoji
|
|
7
|
+
|
|
8
|
+
type Message = MessageCute
|
|
9
|
+
type ForwardType = typing.Literal["user", "hidden_user", "chat", "channel"]
|
|
10
|
+
|
|
11
|
+
TELEGRAM_ID: typing.Final = 777000
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class IsBot(ABCRule):
|
|
15
|
+
def check(self, user: UserSource) -> bool:
|
|
16
|
+
return user.is_bot
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class IsUser(ABCRule):
|
|
20
|
+
def check(self, user: UserSource) -> bool:
|
|
21
|
+
return not user.is_bot
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class IsPremium(ABCRule):
|
|
25
|
+
def check(self, user: UserSource) -> bool:
|
|
26
|
+
return user.is_premium.unwrap_or(False)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class IsLanguageCode(ABCRule):
|
|
30
|
+
def __init__(self, lang_codes: str | list[str], /) -> None:
|
|
31
|
+
self.lang_codes = [lang_codes] if isinstance(lang_codes, str) else lang_codes
|
|
32
|
+
|
|
33
|
+
def check(self, user: UserSource) -> bool:
|
|
34
|
+
return user.language_code.unwrap_or_none() in self.lang_codes
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class IsUserId(ABCRule):
|
|
38
|
+
def __init__(self, user_ids: int | list[int], /) -> None:
|
|
39
|
+
self.user_ids = [user_ids] if isinstance(user_ids, int) else user_ids
|
|
40
|
+
|
|
41
|
+
def check(self, user: UserSource) -> bool:
|
|
42
|
+
return user.id in self.user_ids
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class IsTelegram(Always, requires=[IsUserId(TELEGRAM_ID)]):
|
|
46
|
+
pass
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class IsForum(ABCRule):
|
|
50
|
+
def check(self, chat: ChatSource) -> bool:
|
|
51
|
+
return chat.is_forum.unwrap_or(False)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class IsChatId(ABCRule):
|
|
55
|
+
def __init__(self, chat_ids: int | list[int], /) -> None:
|
|
56
|
+
self.chat_ids = [chat_ids] if isinstance(chat_ids, int) else chat_ids
|
|
57
|
+
|
|
58
|
+
def check(self, chat: ChatSource) -> bool:
|
|
59
|
+
return chat.id in self.chat_ids
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class IsPrivate(ABCRule):
|
|
63
|
+
def check(self, chat: ChatSource) -> bool:
|
|
64
|
+
return chat.type == ChatType.PRIVATE
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class IsGroup(ABCRule):
|
|
68
|
+
def check(self, chat: ChatSource) -> bool:
|
|
69
|
+
return chat.type == ChatType.GROUP
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class IsSuperGroup(ABCRule):
|
|
73
|
+
def check(self, chat: ChatSource) -> bool:
|
|
74
|
+
return chat.type == ChatType.SUPERGROUP
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class IsChat(ABCRule):
|
|
78
|
+
def check(self, chat: ChatSource) -> bool:
|
|
79
|
+
return chat.type in (ChatType.GROUP, ChatType.SUPERGROUP)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class IsDice(ABCRule):
|
|
83
|
+
def check(self, message: Message) -> bool:
|
|
84
|
+
return bool(message.dice)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class IsDiceEmoji(ABCRule, requires=[IsDice()]):
|
|
88
|
+
def __init__(self, dice_emoji: DiceEmoji, /) -> None:
|
|
89
|
+
self.dice_emoji = dice_emoji
|
|
90
|
+
|
|
91
|
+
def check(self, message: Message) -> bool:
|
|
92
|
+
return message.dice.unwrap().emoji == self.dice_emoji
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class IsForward(ABCRule):
|
|
96
|
+
def check(self, message: Message) -> bool:
|
|
97
|
+
return bool(message.forward_origin)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class IsForwardType(ABCRule, requires=[IsForward()]):
|
|
101
|
+
def __init__(self, fwd_type: ForwardType, /) -> None:
|
|
102
|
+
self.fwd_type = fwd_type
|
|
103
|
+
|
|
104
|
+
def check(self, message: Message) -> bool:
|
|
105
|
+
return message.forward_origin.unwrap().v.type == self.fwd_type
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class IsReply(ABCRule):
|
|
109
|
+
def check(self, message: Message) -> bool:
|
|
110
|
+
return bool(message.reply_to_message)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class IsSticker(ABCRule):
|
|
114
|
+
def check(self, message: Message) -> bool:
|
|
115
|
+
return bool(message.sticker)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class IsVideoNote(ABCRule):
|
|
119
|
+
def check(self, message: Message) -> bool:
|
|
120
|
+
return bool(message.video_note)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
class IsDocument(ABCRule):
|
|
124
|
+
def check(self, message: Message) -> bool:
|
|
125
|
+
return bool(message.document)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
class IsPhoto(ABCRule):
|
|
129
|
+
def check(self, message: Message) -> bool:
|
|
130
|
+
return bool(message.photo)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class IsContact(ABCRule):
|
|
134
|
+
def check(self, message: Message) -> bool:
|
|
135
|
+
return bool(message.contact)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class IsLocation(ABCRule):
|
|
139
|
+
def check(self, message: Message) -> bool:
|
|
140
|
+
return bool(message.location)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class IsChecklist(ABCRule):
|
|
144
|
+
def check(self, message: Message) -> bool:
|
|
145
|
+
return bool(message.checklist)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
class IsGame(ABCRule):
|
|
149
|
+
def check(self, message: Message) -> bool:
|
|
150
|
+
return bool(message.game)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class IsPoll(ABCRule):
|
|
154
|
+
def check(self, message: Message) -> bool:
|
|
155
|
+
return bool(message.poll)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
class IsVenue(ABCRule):
|
|
159
|
+
def check(self, message: Message) -> bool:
|
|
160
|
+
return bool(message.venue)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
class IsNewChatMembers(ABCRule):
|
|
164
|
+
def check(self, message: Message) -> bool:
|
|
165
|
+
return bool(message.new_chat_members)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
class IsLeftChatMember(ABCRule):
|
|
169
|
+
def check(self, message: Message) -> bool:
|
|
170
|
+
return bool(message.left_chat_member)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
class IsNewChatTitle(ABCRule):
|
|
174
|
+
def check(self, message: Message) -> bool:
|
|
175
|
+
return bool(message.new_chat_title)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
class IsNewChatPhoto(ABCRule):
|
|
179
|
+
def check(self, message: Message) -> bool:
|
|
180
|
+
return bool(message.new_chat_photo)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
__all__ = (
|
|
184
|
+
"IsBot",
|
|
185
|
+
"IsChat",
|
|
186
|
+
"IsChatId",
|
|
187
|
+
"IsChecklist",
|
|
188
|
+
"IsDice",
|
|
189
|
+
"IsDiceEmoji",
|
|
190
|
+
"IsDocument",
|
|
191
|
+
"IsForum",
|
|
192
|
+
"IsForward",
|
|
193
|
+
"IsForwardType",
|
|
194
|
+
"IsGame",
|
|
195
|
+
"IsGroup",
|
|
196
|
+
"IsLanguageCode",
|
|
197
|
+
"IsLeftChatMember",
|
|
198
|
+
"IsNewChatMembers",
|
|
199
|
+
"IsNewChatPhoto",
|
|
200
|
+
"IsNewChatTitle",
|
|
201
|
+
"IsPhoto",
|
|
202
|
+
"IsPoll",
|
|
203
|
+
"IsPremium",
|
|
204
|
+
"IsPrivate",
|
|
205
|
+
"IsReply",
|
|
206
|
+
"IsSticker",
|
|
207
|
+
"IsSuperGroup",
|
|
208
|
+
"IsTelegram",
|
|
209
|
+
"IsUser",
|
|
210
|
+
"IsUserId",
|
|
211
|
+
"IsVenue",
|
|
212
|
+
"IsVideoNote",
|
|
213
|
+
)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
|
|
3
|
+
from telegrinder.bot.rules.abc import ABCRule, Context, check_rule
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class If(ABCRule):
|
|
7
|
+
def __init__(self, condition: ABCRule) -> None:
|
|
8
|
+
self.conditions = [condition]
|
|
9
|
+
|
|
10
|
+
async def check(self, context: Context) -> bool:
|
|
11
|
+
for condition in self.conditions[:-1]:
|
|
12
|
+
if not await check_rule(condition, context):
|
|
13
|
+
return True
|
|
14
|
+
|
|
15
|
+
return await check_rule(self.conditions[-1], context)
|
|
16
|
+
|
|
17
|
+
def then(self, condition: ABCRule, /) -> typing.Self:
|
|
18
|
+
self.conditions.append(condition)
|
|
19
|
+
return self
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
__all__ = ("If",)
|