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.
Files changed (215) hide show
  1. telegrinder/__init__.py +258 -0
  2. telegrinder/__meta__.py +1 -0
  3. telegrinder/api/__init__.py +15 -0
  4. telegrinder/api/api.py +175 -0
  5. telegrinder/api/error.py +50 -0
  6. telegrinder/api/response.py +23 -0
  7. telegrinder/api/token.py +30 -0
  8. telegrinder/api/validators.py +30 -0
  9. telegrinder/bot/__init__.py +144 -0
  10. telegrinder/bot/bot.py +70 -0
  11. telegrinder/bot/cute_types/__init__.py +41 -0
  12. telegrinder/bot/cute_types/base.py +228 -0
  13. telegrinder/bot/cute_types/base.pyi +49 -0
  14. telegrinder/bot/cute_types/business_connection.py +9 -0
  15. telegrinder/bot/cute_types/business_messages_deleted.py +9 -0
  16. telegrinder/bot/cute_types/callback_query.py +248 -0
  17. telegrinder/bot/cute_types/chat_boost_removed.py +9 -0
  18. telegrinder/bot/cute_types/chat_boost_updated.py +9 -0
  19. telegrinder/bot/cute_types/chat_join_request.py +59 -0
  20. telegrinder/bot/cute_types/chat_member_updated.py +158 -0
  21. telegrinder/bot/cute_types/chosen_inline_result.py +11 -0
  22. telegrinder/bot/cute_types/inline_query.py +41 -0
  23. telegrinder/bot/cute_types/message.py +2809 -0
  24. telegrinder/bot/cute_types/message_reaction_count_updated.py +9 -0
  25. telegrinder/bot/cute_types/message_reaction_updated.py +9 -0
  26. telegrinder/bot/cute_types/paid_media_purchased.py +11 -0
  27. telegrinder/bot/cute_types/poll.py +9 -0
  28. telegrinder/bot/cute_types/poll_answer.py +9 -0
  29. telegrinder/bot/cute_types/pre_checkout_query.py +36 -0
  30. telegrinder/bot/cute_types/shipping_query.py +11 -0
  31. telegrinder/bot/cute_types/update.py +209 -0
  32. telegrinder/bot/cute_types/utils.py +141 -0
  33. telegrinder/bot/dispatch/__init__.py +99 -0
  34. telegrinder/bot/dispatch/abc.py +74 -0
  35. telegrinder/bot/dispatch/action.py +99 -0
  36. telegrinder/bot/dispatch/context.py +162 -0
  37. telegrinder/bot/dispatch/dispatch.py +362 -0
  38. telegrinder/bot/dispatch/handler/__init__.py +23 -0
  39. telegrinder/bot/dispatch/handler/abc.py +25 -0
  40. telegrinder/bot/dispatch/handler/audio_reply.py +43 -0
  41. telegrinder/bot/dispatch/handler/base.py +34 -0
  42. telegrinder/bot/dispatch/handler/document_reply.py +43 -0
  43. telegrinder/bot/dispatch/handler/func.py +73 -0
  44. telegrinder/bot/dispatch/handler/media_group_reply.py +43 -0
  45. telegrinder/bot/dispatch/handler/message_reply.py +35 -0
  46. telegrinder/bot/dispatch/handler/photo_reply.py +43 -0
  47. telegrinder/bot/dispatch/handler/sticker_reply.py +36 -0
  48. telegrinder/bot/dispatch/handler/video_reply.py +43 -0
  49. telegrinder/bot/dispatch/middleware/__init__.py +13 -0
  50. telegrinder/bot/dispatch/middleware/abc.py +112 -0
  51. telegrinder/bot/dispatch/middleware/box.py +32 -0
  52. telegrinder/bot/dispatch/middleware/filter.py +88 -0
  53. telegrinder/bot/dispatch/middleware/media_group.py +69 -0
  54. telegrinder/bot/dispatch/process.py +93 -0
  55. telegrinder/bot/dispatch/return_manager/__init__.py +21 -0
  56. telegrinder/bot/dispatch/return_manager/abc.py +107 -0
  57. telegrinder/bot/dispatch/return_manager/callback_query.py +19 -0
  58. telegrinder/bot/dispatch/return_manager/inline_query.py +14 -0
  59. telegrinder/bot/dispatch/return_manager/message.py +34 -0
  60. telegrinder/bot/dispatch/return_manager/pre_checkout_query.py +19 -0
  61. telegrinder/bot/dispatch/return_manager/utils.py +20 -0
  62. telegrinder/bot/dispatch/router/__init__.py +4 -0
  63. telegrinder/bot/dispatch/router/abc.py +15 -0
  64. telegrinder/bot/dispatch/router/base.py +154 -0
  65. telegrinder/bot/dispatch/view/__init__.py +15 -0
  66. telegrinder/bot/dispatch/view/abc.py +15 -0
  67. telegrinder/bot/dispatch/view/base.py +226 -0
  68. telegrinder/bot/dispatch/view/box.py +207 -0
  69. telegrinder/bot/dispatch/view/media_group.py +25 -0
  70. telegrinder/bot/dispatch/waiter_machine/__init__.py +25 -0
  71. telegrinder/bot/dispatch/waiter_machine/actions.py +16 -0
  72. telegrinder/bot/dispatch/waiter_machine/hasher/__init__.py +13 -0
  73. telegrinder/bot/dispatch/waiter_machine/hasher/callback.py +53 -0
  74. telegrinder/bot/dispatch/waiter_machine/hasher/hasher.py +61 -0
  75. telegrinder/bot/dispatch/waiter_machine/hasher/message.py +49 -0
  76. telegrinder/bot/dispatch/waiter_machine/machine.py +264 -0
  77. telegrinder/bot/dispatch/waiter_machine/middleware.py +77 -0
  78. telegrinder/bot/dispatch/waiter_machine/short_state.py +105 -0
  79. telegrinder/bot/polling/__init__.py +4 -0
  80. telegrinder/bot/polling/abc.py +25 -0
  81. telegrinder/bot/polling/error_handler.py +93 -0
  82. telegrinder/bot/polling/polling.py +167 -0
  83. telegrinder/bot/polling/utils.py +12 -0
  84. telegrinder/bot/rules/__init__.py +166 -0
  85. telegrinder/bot/rules/abc.py +150 -0
  86. telegrinder/bot/rules/button.py +20 -0
  87. telegrinder/bot/rules/callback_data.py +109 -0
  88. telegrinder/bot/rules/chat_join.py +28 -0
  89. telegrinder/bot/rules/chat_member_updated.py +145 -0
  90. telegrinder/bot/rules/command.py +137 -0
  91. telegrinder/bot/rules/enum_text.py +29 -0
  92. telegrinder/bot/rules/func.py +21 -0
  93. telegrinder/bot/rules/fuzzy.py +21 -0
  94. telegrinder/bot/rules/inline.py +45 -0
  95. telegrinder/bot/rules/integer.py +19 -0
  96. telegrinder/bot/rules/is_from.py +213 -0
  97. telegrinder/bot/rules/logic.py +22 -0
  98. telegrinder/bot/rules/magic.py +60 -0
  99. telegrinder/bot/rules/markup.py +51 -0
  100. telegrinder/bot/rules/media.py +13 -0
  101. telegrinder/bot/rules/mention.py +15 -0
  102. telegrinder/bot/rules/message_entities.py +37 -0
  103. telegrinder/bot/rules/node.py +43 -0
  104. telegrinder/bot/rules/payload.py +89 -0
  105. telegrinder/bot/rules/payment_invoice.py +14 -0
  106. telegrinder/bot/rules/regex.py +34 -0
  107. telegrinder/bot/rules/rule_enum.py +71 -0
  108. telegrinder/bot/rules/start.py +73 -0
  109. telegrinder/bot/rules/state.py +35 -0
  110. telegrinder/bot/rules/text.py +27 -0
  111. telegrinder/bot/rules/update.py +14 -0
  112. telegrinder/bot/scenario/__init__.py +5 -0
  113. telegrinder/bot/scenario/abc.py +16 -0
  114. telegrinder/bot/scenario/checkbox.py +183 -0
  115. telegrinder/bot/scenario/choice.py +44 -0
  116. telegrinder/client/__init__.py +11 -0
  117. telegrinder/client/abc.py +136 -0
  118. telegrinder/client/form_data.py +34 -0
  119. telegrinder/client/rnet.py +198 -0
  120. telegrinder/model.py +133 -0
  121. telegrinder/model.pyi +57 -0
  122. telegrinder/modules.py +1081 -0
  123. telegrinder/msgspec_utils/__init__.py +42 -0
  124. telegrinder/msgspec_utils/abc.py +16 -0
  125. telegrinder/msgspec_utils/custom_types/__init__.py +6 -0
  126. telegrinder/msgspec_utils/custom_types/datetime.py +24 -0
  127. telegrinder/msgspec_utils/custom_types/enum_meta.py +61 -0
  128. telegrinder/msgspec_utils/custom_types/literal.py +25 -0
  129. telegrinder/msgspec_utils/custom_types/option.py +17 -0
  130. telegrinder/msgspec_utils/decoder.py +388 -0
  131. telegrinder/msgspec_utils/encoder.py +204 -0
  132. telegrinder/msgspec_utils/json.py +15 -0
  133. telegrinder/msgspec_utils/tools.py +80 -0
  134. telegrinder/node/__init__.py +80 -0
  135. telegrinder/node/compose.py +193 -0
  136. telegrinder/node/nodes/__init__.py +96 -0
  137. telegrinder/node/nodes/attachment.py +169 -0
  138. telegrinder/node/nodes/callback_query.py +25 -0
  139. telegrinder/node/nodes/channel.py +97 -0
  140. telegrinder/node/nodes/command.py +33 -0
  141. telegrinder/node/nodes/error.py +43 -0
  142. telegrinder/node/nodes/event.py +70 -0
  143. telegrinder/node/nodes/file.py +39 -0
  144. telegrinder/node/nodes/global_node.py +66 -0
  145. telegrinder/node/nodes/i18n.py +110 -0
  146. telegrinder/node/nodes/me.py +26 -0
  147. telegrinder/node/nodes/message_entities.py +15 -0
  148. telegrinder/node/nodes/payload.py +84 -0
  149. telegrinder/node/nodes/reply_message.py +14 -0
  150. telegrinder/node/nodes/source.py +172 -0
  151. telegrinder/node/nodes/state_mutator.py +71 -0
  152. telegrinder/node/nodes/text.py +62 -0
  153. telegrinder/node/scope.py +88 -0
  154. telegrinder/node/utils.py +38 -0
  155. telegrinder/py.typed +0 -0
  156. telegrinder/rules.py +1 -0
  157. telegrinder/tools/__init__.py +183 -0
  158. telegrinder/tools/aio.py +147 -0
  159. telegrinder/tools/final.py +21 -0
  160. telegrinder/tools/formatting/__init__.py +85 -0
  161. telegrinder/tools/formatting/deep_links/__init__.py +39 -0
  162. telegrinder/tools/formatting/deep_links/links.py +468 -0
  163. telegrinder/tools/formatting/deep_links/parsing.py +88 -0
  164. telegrinder/tools/formatting/deep_links/validators.py +8 -0
  165. telegrinder/tools/formatting/html.py +241 -0
  166. telegrinder/tools/fullname.py +82 -0
  167. telegrinder/tools/global_context/__init__.py +13 -0
  168. telegrinder/tools/global_context/abc.py +63 -0
  169. telegrinder/tools/global_context/builtin_context.py +45 -0
  170. telegrinder/tools/global_context/global_context.py +614 -0
  171. telegrinder/tools/input_file_directory.py +30 -0
  172. telegrinder/tools/keyboard/__init__.py +6 -0
  173. telegrinder/tools/keyboard/abc.py +84 -0
  174. telegrinder/tools/keyboard/base.py +108 -0
  175. telegrinder/tools/keyboard/button.py +181 -0
  176. telegrinder/tools/keyboard/data.py +31 -0
  177. telegrinder/tools/keyboard/keyboard.py +160 -0
  178. telegrinder/tools/keyboard/utils.py +95 -0
  179. telegrinder/tools/lifespan.py +188 -0
  180. telegrinder/tools/limited_dict.py +35 -0
  181. telegrinder/tools/loop_wrapper.py +271 -0
  182. telegrinder/tools/magic/__init__.py +29 -0
  183. telegrinder/tools/magic/annotations.py +172 -0
  184. telegrinder/tools/magic/descriptors.py +57 -0
  185. telegrinder/tools/magic/function.py +254 -0
  186. telegrinder/tools/magic/inspect.py +16 -0
  187. telegrinder/tools/magic/shortcut.py +107 -0
  188. telegrinder/tools/member_descriptor_proxy.py +95 -0
  189. telegrinder/tools/parse_mode.py +12 -0
  190. telegrinder/tools/serialization/__init__.py +5 -0
  191. telegrinder/tools/serialization/abc.py +34 -0
  192. telegrinder/tools/serialization/json_ser.py +60 -0
  193. telegrinder/tools/serialization/msgpack_ser.py +197 -0
  194. telegrinder/tools/serialization/utils.py +18 -0
  195. telegrinder/tools/singleton/__init__.py +4 -0
  196. telegrinder/tools/singleton/abc.py +14 -0
  197. telegrinder/tools/singleton/singleton.py +18 -0
  198. telegrinder/tools/state_mutator/__init__.py +4 -0
  199. telegrinder/tools/state_mutator/mutation.py +85 -0
  200. telegrinder/tools/state_storage/__init__.py +4 -0
  201. telegrinder/tools/state_storage/abc.py +38 -0
  202. telegrinder/tools/state_storage/memory.py +27 -0
  203. telegrinder/tools/strings.py +22 -0
  204. telegrinder/types/__init__.py +323 -0
  205. telegrinder/types/enums.py +754 -0
  206. telegrinder/types/input_file.py +51 -0
  207. telegrinder/types/methods.py +6143 -0
  208. telegrinder/types/methods_utils.py +66 -0
  209. telegrinder/types/objects.py +8184 -0
  210. telegrinder/types/webapp.py +129 -0
  211. telegrinder/verification_utils.py +35 -0
  212. telegrinder-1.0.0rc1.dist-info/METADATA +166 -0
  213. telegrinder-1.0.0rc1.dist-info/RECORD +215 -0
  214. telegrinder-1.0.0rc1.dist-info/WHEEL +4 -0
  215. 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",)