telegrinder 0.3.3.post1__py3-none-any.whl → 0.3.4.post1__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.

Files changed (165) hide show
  1. telegrinder/__init__.py +144 -144
  2. telegrinder/api/__init__.py +8 -8
  3. telegrinder/api/api.py +93 -93
  4. telegrinder/api/error.py +16 -16
  5. telegrinder/api/response.py +20 -20
  6. telegrinder/api/token.py +36 -36
  7. telegrinder/bot/__init__.py +66 -66
  8. telegrinder/bot/bot.py +76 -76
  9. telegrinder/bot/cute_types/__init__.py +17 -17
  10. telegrinder/bot/cute_types/base.py +258 -258
  11. telegrinder/bot/cute_types/callback_query.py +385 -385
  12. telegrinder/bot/cute_types/chat_join_request.py +61 -61
  13. telegrinder/bot/cute_types/chat_member_updated.py +160 -160
  14. telegrinder/bot/cute_types/inline_query.py +43 -43
  15. telegrinder/bot/cute_types/message.py +2637 -2637
  16. telegrinder/bot/cute_types/update.py +104 -109
  17. telegrinder/bot/cute_types/utils.py +95 -95
  18. telegrinder/bot/dispatch/__init__.py +55 -55
  19. telegrinder/bot/dispatch/abc.py +77 -77
  20. telegrinder/bot/dispatch/context.py +98 -98
  21. telegrinder/bot/dispatch/dispatch.py +202 -202
  22. telegrinder/bot/dispatch/handler/__init__.py +13 -13
  23. telegrinder/bot/dispatch/handler/abc.py +24 -24
  24. telegrinder/bot/dispatch/handler/audio_reply.py +44 -44
  25. telegrinder/bot/dispatch/handler/base.py +57 -57
  26. telegrinder/bot/dispatch/handler/document_reply.py +44 -44
  27. telegrinder/bot/dispatch/handler/func.py +135 -135
  28. telegrinder/bot/dispatch/handler/media_group_reply.py +43 -43
  29. telegrinder/bot/dispatch/handler/message_reply.py +36 -36
  30. telegrinder/bot/dispatch/handler/photo_reply.py +44 -44
  31. telegrinder/bot/dispatch/handler/sticker_reply.py +37 -37
  32. telegrinder/bot/dispatch/handler/video_reply.py +44 -44
  33. telegrinder/bot/dispatch/middleware/__init__.py +3 -3
  34. telegrinder/bot/dispatch/middleware/abc.py +22 -16
  35. telegrinder/bot/dispatch/process.py +157 -132
  36. telegrinder/bot/dispatch/return_manager/__init__.py +13 -13
  37. telegrinder/bot/dispatch/return_manager/abc.py +108 -108
  38. telegrinder/bot/dispatch/return_manager/callback_query.py +20 -20
  39. telegrinder/bot/dispatch/return_manager/inline_query.py +15 -15
  40. telegrinder/bot/dispatch/return_manager/message.py +36 -36
  41. telegrinder/bot/dispatch/view/__init__.py +13 -13
  42. telegrinder/bot/dispatch/view/abc.py +41 -41
  43. telegrinder/bot/dispatch/view/base.py +200 -200
  44. telegrinder/bot/dispatch/view/box.py +129 -129
  45. telegrinder/bot/dispatch/view/callback_query.py +17 -17
  46. telegrinder/bot/dispatch/view/chat_join_request.py +16 -16
  47. telegrinder/bot/dispatch/view/chat_member.py +39 -39
  48. telegrinder/bot/dispatch/view/inline_query.py +17 -17
  49. telegrinder/bot/dispatch/view/message.py +44 -44
  50. telegrinder/bot/dispatch/view/raw.py +114 -114
  51. telegrinder/bot/dispatch/waiter_machine/__init__.py +17 -17
  52. telegrinder/bot/dispatch/waiter_machine/actions.py +13 -13
  53. telegrinder/bot/dispatch/waiter_machine/hasher/__init__.py +8 -8
  54. telegrinder/bot/dispatch/waiter_machine/hasher/callback.py +55 -55
  55. telegrinder/bot/dispatch/waiter_machine/hasher/hasher.py +57 -57
  56. telegrinder/bot/dispatch/waiter_machine/hasher/message.py +51 -51
  57. telegrinder/bot/dispatch/waiter_machine/hasher/state.py +19 -19
  58. telegrinder/bot/dispatch/waiter_machine/machine.py +172 -167
  59. telegrinder/bot/dispatch/waiter_machine/middleware.py +89 -89
  60. telegrinder/bot/dispatch/waiter_machine/short_state.py +68 -68
  61. telegrinder/bot/polling/__init__.py +4 -4
  62. telegrinder/bot/polling/abc.py +25 -25
  63. telegrinder/bot/polling/polling.py +131 -131
  64. telegrinder/bot/rules/__init__.py +62 -62
  65. telegrinder/bot/rules/abc.py +213 -213
  66. telegrinder/bot/rules/adapter/__init__.py +12 -9
  67. telegrinder/bot/rules/adapter/abc.py +31 -29
  68. telegrinder/bot/rules/adapter/errors.py +5 -5
  69. telegrinder/bot/rules/adapter/event.py +65 -67
  70. telegrinder/bot/rules/adapter/node.py +48 -48
  71. telegrinder/bot/rules/adapter/raw_event.py +27 -0
  72. telegrinder/bot/rules/adapter/raw_update.py +30 -30
  73. telegrinder/bot/rules/callback_data.py +170 -170
  74. telegrinder/bot/rules/chat_join.py +46 -46
  75. telegrinder/bot/rules/command.py +126 -126
  76. telegrinder/bot/rules/enum_text.py +36 -36
  77. telegrinder/bot/rules/func.py +26 -26
  78. telegrinder/bot/rules/fuzzy.py +24 -24
  79. telegrinder/bot/rules/inline.py +60 -60
  80. telegrinder/bot/rules/integer.py +20 -20
  81. telegrinder/bot/rules/is_from.py +127 -127
  82. telegrinder/bot/rules/markup.py +43 -43
  83. telegrinder/bot/rules/mention.py +14 -14
  84. telegrinder/bot/rules/message.py +17 -17
  85. telegrinder/bot/rules/message_entities.py +35 -35
  86. telegrinder/bot/rules/node.py +27 -27
  87. telegrinder/bot/rules/regex.py +37 -37
  88. telegrinder/bot/rules/rule_enum.py +72 -72
  89. telegrinder/bot/rules/start.py +42 -42
  90. telegrinder/bot/rules/state.py +37 -37
  91. telegrinder/bot/rules/text.py +33 -33
  92. telegrinder/bot/rules/update.py +15 -15
  93. telegrinder/bot/scenario/__init__.py +5 -5
  94. telegrinder/bot/scenario/abc.py +19 -19
  95. telegrinder/bot/scenario/checkbox.py +176 -167
  96. telegrinder/bot/scenario/choice.py +51 -46
  97. telegrinder/client/__init__.py +4 -4
  98. telegrinder/client/abc.py +75 -75
  99. telegrinder/client/aiohttp.py +130 -130
  100. telegrinder/model.py +320 -295
  101. telegrinder/modules.py +237 -237
  102. telegrinder/msgspec_json.py +14 -14
  103. telegrinder/msgspec_utils.py +410 -410
  104. telegrinder/node/__init__.py +0 -0
  105. telegrinder/node/attachment.py +87 -87
  106. telegrinder/node/base.py +166 -166
  107. telegrinder/node/callback_query.py +53 -53
  108. telegrinder/node/command.py +33 -33
  109. telegrinder/node/composer.py +198 -198
  110. telegrinder/node/container.py +27 -27
  111. telegrinder/node/event.py +65 -65
  112. telegrinder/node/me.py +16 -16
  113. telegrinder/node/message.py +14 -14
  114. telegrinder/node/polymorphic.py +48 -48
  115. telegrinder/node/rule.py +76 -76
  116. telegrinder/node/scope.py +38 -38
  117. telegrinder/node/source.py +71 -71
  118. telegrinder/node/text.py +41 -41
  119. telegrinder/node/tools/__init__.py +3 -3
  120. telegrinder/node/tools/generator.py +40 -40
  121. telegrinder/node/update.py +15 -15
  122. telegrinder/rules.py +0 -0
  123. telegrinder/tools/__init__.py +74 -74
  124. telegrinder/tools/buttons.py +79 -79
  125. telegrinder/tools/error_handler/__init__.py +7 -7
  126. telegrinder/tools/error_handler/abc.py +33 -33
  127. telegrinder/tools/error_handler/error.py +9 -9
  128. telegrinder/tools/error_handler/error_handler.py +193 -193
  129. telegrinder/tools/formatting/__init__.py +46 -46
  130. telegrinder/tools/formatting/html.py +283 -283
  131. telegrinder/tools/formatting/links.py +33 -33
  132. telegrinder/tools/formatting/spec_html_formats.py +111 -111
  133. telegrinder/tools/functional.py +12 -12
  134. telegrinder/tools/global_context/__init__.py +7 -7
  135. telegrinder/tools/global_context/abc.py +63 -63
  136. telegrinder/tools/global_context/global_context.py +412 -412
  137. telegrinder/tools/global_context/telegrinder_ctx.py +27 -27
  138. telegrinder/tools/i18n/__init__.py +7 -7
  139. telegrinder/tools/i18n/abc.py +30 -30
  140. telegrinder/tools/i18n/middleware/__init__.py +3 -3
  141. telegrinder/tools/i18n/middleware/abc.py +25 -25
  142. telegrinder/tools/i18n/simple.py +43 -43
  143. telegrinder/tools/kb_set/__init__.py +4 -4
  144. telegrinder/tools/kb_set/base.py +15 -15
  145. telegrinder/tools/kb_set/yaml.py +63 -63
  146. telegrinder/tools/keyboard.py +132 -132
  147. telegrinder/tools/limited_dict.py +37 -37
  148. telegrinder/tools/loop_wrapper/__init__.py +4 -4
  149. telegrinder/tools/loop_wrapper/abc.py +15 -15
  150. telegrinder/tools/loop_wrapper/loop_wrapper.py +224 -224
  151. telegrinder/tools/magic.py +157 -157
  152. telegrinder/tools/parse_mode.py +6 -6
  153. telegrinder/tools/state_storage/__init__.py +4 -4
  154. telegrinder/tools/state_storage/abc.py +35 -35
  155. telegrinder/tools/state_storage/memory.py +25 -25
  156. telegrinder/types/__init__.py +260 -260
  157. telegrinder/types/enums.py +701 -701
  158. telegrinder/types/methods.py +4633 -4633
  159. telegrinder/types/objects.py +6950 -8561
  160. telegrinder/verification_utils.py +32 -32
  161. {telegrinder-0.3.3.post1.dist-info → telegrinder-0.3.4.post1.dist-info}/LICENSE +22 -22
  162. {telegrinder-0.3.3.post1.dist-info → telegrinder-0.3.4.post1.dist-info}/METADATA +1 -1
  163. telegrinder-0.3.4.post1.dist-info/RECORD +165 -0
  164. telegrinder-0.3.3.post1.dist-info/RECORD +0 -164
  165. {telegrinder-0.3.3.post1.dist-info → telegrinder-0.3.4.post1.dist-info}/WHEEL +0 -0
@@ -1,126 +1,126 @@
1
- import dataclasses
2
- import typing
3
-
4
- from telegrinder.bot.dispatch.context import Context
5
- from telegrinder.node.command import CommandInfo, single_split
6
- from telegrinder.node.me import Me
7
- from telegrinder.node.source import Source
8
- from telegrinder.types.enums import ChatType
9
-
10
- from .abc import ABCRule
11
-
12
- Validator: typing.TypeAlias = typing.Callable[[str], typing.Any | None]
13
-
14
-
15
- @dataclasses.dataclass(frozen=True, slots=True)
16
- class Argument:
17
- name: str
18
- validators: list[Validator] = dataclasses.field(default_factory=lambda: [])
19
- optional: bool = dataclasses.field(default=False, kw_only=True)
20
-
21
- def check(self, data: str) -> typing.Any | None:
22
- for validator in self.validators:
23
- data = validator(data) # type: ignore
24
- if data is None:
25
- return None
26
- return data
27
-
28
-
29
- class Command(ABCRule):
30
- def __init__(
31
- self,
32
- names: str | typing.Iterable[str],
33
- *arguments: Argument,
34
- prefixes: tuple[str, ...] = ("/",),
35
- separator: str = " ",
36
- lazy: bool = False,
37
- validate_mention: bool = True,
38
- mention_needed_in_chat: bool = False,
39
- ) -> None:
40
- self.names = [names] if isinstance(names, str) else names
41
- self.arguments = arguments
42
- self.prefixes = prefixes
43
- self.separator = separator
44
- self.lazy = lazy
45
- self.validate_mention = validate_mention
46
-
47
- # if true then we'll check for mention when message is from a group
48
- self.mention_needed_in_chat = mention_needed_in_chat
49
-
50
- def remove_prefix(self, text: str) -> str | None:
51
- for prefix in self.prefixes:
52
- if text.startswith(prefix):
53
- return text.removeprefix(prefix)
54
- return None
55
-
56
- def parse_argument(
57
- self,
58
- arguments: list[Argument],
59
- data_s: str,
60
- new_s: str,
61
- s: str,
62
- ) -> dict | None:
63
- argument = arguments[0]
64
- data = argument.check(data_s)
65
- if data is None and not argument.optional:
66
- return None
67
-
68
- if data is None:
69
- return self.parse_arguments(arguments[1:], s)
70
-
71
- with_argument = self.parse_arguments(arguments[1:], new_s)
72
- if with_argument is not None:
73
- return {argument.name: data, **with_argument}
74
-
75
- if not argument.optional:
76
- return None
77
-
78
- return self.parse_arguments(arguments[1:], s)
79
-
80
- def parse_arguments(self, arguments: list[Argument], s: str) -> dict[str, typing.Any] | None:
81
- if not arguments:
82
- return {} if not s else None
83
-
84
- if self.lazy:
85
- return self.parse_argument(arguments, *single_split(s, self.separator), s)
86
-
87
- all_split = s.split(self.separator)
88
- for i in range(1, len(all_split) + 1):
89
- ctx = self.parse_argument(
90
- arguments,
91
- self.separator.join(all_split[:i]),
92
- self.separator.join(all_split[i:]),
93
- s,
94
- )
95
- if ctx is not None:
96
- return ctx
97
-
98
- return None
99
-
100
- def check(self, command: CommandInfo, me: Me, src: Source, ctx: Context) -> bool:
101
- name = self.remove_prefix(command.name)
102
- if name is None:
103
- return False
104
-
105
- if name not in self.names:
106
- return False
107
-
108
- if not command.mention and self.mention_needed_in_chat and src.chat.type is not ChatType.PRIVATE:
109
- return False
110
-
111
- if command.mention and self.validate_mention: # noqa
112
- if command.mention.unwrap().lower() != me.username.unwrap().lower():
113
- return False
114
-
115
- if not self.arguments:
116
- return not command.arguments
117
-
118
- result = self.parse_arguments(list(self.arguments), command.arguments)
119
- if result is None:
120
- return False
121
-
122
- ctx.update(result)
123
- return True
124
-
125
-
126
- __all__ = ("Argument", "Command", "single_split")
1
+ import dataclasses
2
+ import typing
3
+
4
+ from telegrinder.bot.dispatch.context import Context
5
+ from telegrinder.node.command import CommandInfo, single_split
6
+ from telegrinder.node.me import Me
7
+ from telegrinder.node.source import Source
8
+ from telegrinder.types.enums import ChatType
9
+
10
+ from .abc import ABCRule
11
+
12
+ Validator: typing.TypeAlias = typing.Callable[[str], typing.Any | None]
13
+
14
+
15
+ @dataclasses.dataclass(frozen=True, slots=True)
16
+ class Argument:
17
+ name: str
18
+ validators: list[Validator] = dataclasses.field(default_factory=lambda: [])
19
+ optional: bool = dataclasses.field(default=False, kw_only=True)
20
+
21
+ def check(self, data: str) -> typing.Any | None:
22
+ for validator in self.validators:
23
+ data = validator(data) # type: ignore
24
+ if data is None:
25
+ return None
26
+ return data
27
+
28
+
29
+ class Command(ABCRule):
30
+ def __init__(
31
+ self,
32
+ names: str | typing.Iterable[str],
33
+ *arguments: Argument,
34
+ prefixes: tuple[str, ...] = ("/",),
35
+ separator: str = " ",
36
+ lazy: bool = False,
37
+ validate_mention: bool = True,
38
+ mention_needed_in_chat: bool = False,
39
+ ) -> None:
40
+ self.names = [names] if isinstance(names, str) else names
41
+ self.arguments = arguments
42
+ self.prefixes = prefixes
43
+ self.separator = separator
44
+ self.lazy = lazy
45
+ self.validate_mention = validate_mention
46
+
47
+ # if true then we'll check for mention when message is from a group
48
+ self.mention_needed_in_chat = mention_needed_in_chat
49
+
50
+ def remove_prefix(self, text: str) -> str | None:
51
+ for prefix in self.prefixes:
52
+ if text.startswith(prefix):
53
+ return text.removeprefix(prefix)
54
+ return None
55
+
56
+ def parse_argument(
57
+ self,
58
+ arguments: list[Argument],
59
+ data_s: str,
60
+ new_s: str,
61
+ s: str,
62
+ ) -> dict | None:
63
+ argument = arguments[0]
64
+ data = argument.check(data_s)
65
+ if data is None and not argument.optional:
66
+ return None
67
+
68
+ if data is None:
69
+ return self.parse_arguments(arguments[1:], s)
70
+
71
+ with_argument = self.parse_arguments(arguments[1:], new_s)
72
+ if with_argument is not None:
73
+ return {argument.name: data, **with_argument}
74
+
75
+ if not argument.optional:
76
+ return None
77
+
78
+ return self.parse_arguments(arguments[1:], s)
79
+
80
+ def parse_arguments(self, arguments: list[Argument], s: str) -> dict[str, typing.Any] | None:
81
+ if not arguments:
82
+ return {} if not s else None
83
+
84
+ if self.lazy:
85
+ return self.parse_argument(arguments, *single_split(s, self.separator), s)
86
+
87
+ all_split = s.split(self.separator)
88
+ for i in range(1, len(all_split) + 1):
89
+ ctx = self.parse_argument(
90
+ arguments,
91
+ self.separator.join(all_split[:i]),
92
+ self.separator.join(all_split[i:]),
93
+ s,
94
+ )
95
+ if ctx is not None:
96
+ return ctx
97
+
98
+ return None
99
+
100
+ def check(self, command: CommandInfo, me: Me, src: Source, ctx: Context) -> bool:
101
+ name = self.remove_prefix(command.name)
102
+ if name is None:
103
+ return False
104
+
105
+ if name not in self.names:
106
+ return False
107
+
108
+ if not command.mention and self.mention_needed_in_chat and src.chat.type is not ChatType.PRIVATE:
109
+ return False
110
+
111
+ if command.mention and self.validate_mention: # noqa
112
+ if command.mention.unwrap().lower() != me.username.unwrap().lower():
113
+ return False
114
+
115
+ if not self.arguments:
116
+ return not command.arguments
117
+
118
+ result = self.parse_arguments(list(self.arguments), command.arguments)
119
+ if result is None:
120
+ return False
121
+
122
+ ctx.update(result)
123
+ return True
124
+
125
+
126
+ __all__ = ("Argument", "Command", "single_split")
@@ -1,36 +1,36 @@
1
- import enum
2
- import typing
3
-
4
- from telegrinder.bot.dispatch.context import Context
5
- from telegrinder.node.text import Text
6
-
7
- from .abc import ABCRule
8
-
9
- T = typing.TypeVar("T", bound=enum.Enum)
10
-
11
-
12
- class EnumTextRule(ABCRule, typing.Generic[T]):
13
- def __init__(self, enum_t: type[T], *, lower_case: bool = True) -> None:
14
- self.enum_t = enum_t
15
- self.texts = list(
16
- map(
17
- lambda x: x.value.lower() if lower_case else x.value,
18
- self.enum_t,
19
- )
20
- )
21
-
22
- def find(self, s: str) -> T:
23
- for enumeration in self.enum_t:
24
- if enumeration.value.lower() == s:
25
- return enumeration
26
- raise KeyError("Enumeration is undefined.")
27
-
28
- def check(self, text: Text, ctx: Context) -> bool:
29
- text = text.lower() # type: ignore
30
- if text not in self.texts:
31
- return False
32
- ctx.enum_text = self.find(text)
33
- return True
34
-
35
-
36
- __all__ = ("EnumTextRule",)
1
+ import enum
2
+ import typing
3
+
4
+ from telegrinder.bot.dispatch.context import Context
5
+ from telegrinder.node.text import Text
6
+
7
+ from .abc import ABCRule
8
+
9
+ T = typing.TypeVar("T", bound=enum.Enum)
10
+
11
+
12
+ class EnumTextRule(ABCRule, typing.Generic[T]):
13
+ def __init__(self, enum_t: type[T], *, lower_case: bool = True) -> None:
14
+ self.enum_t = enum_t
15
+ self.texts = list(
16
+ map(
17
+ lambda x: x.value.lower() if lower_case else x.value,
18
+ self.enum_t,
19
+ )
20
+ )
21
+
22
+ def find(self, s: str) -> T:
23
+ for enumeration in self.enum_t:
24
+ if enumeration.value.lower() == s:
25
+ return enumeration
26
+ raise KeyError("Enumeration is undefined.")
27
+
28
+ def check(self, text: Text, ctx: Context) -> bool:
29
+ text = text.lower() # type: ignore
30
+ if text not in self.texts:
31
+ return False
32
+ ctx.enum_text = self.find(text)
33
+ return True
34
+
35
+
36
+ __all__ = ("EnumTextRule",)
@@ -1,26 +1,26 @@
1
- import inspect
2
- import typing
3
-
4
- from telegrinder.bot.dispatch.context import Context
5
- from telegrinder.types.objects import Update
6
-
7
- from .abc import ABCAdapter, ABCRule, AdaptTo, RawUpdateAdapter
8
-
9
-
10
- class FuncRule(ABCRule, typing.Generic[AdaptTo]):
11
- def __init__(
12
- self,
13
- func: typing.Callable[[AdaptTo, Context], typing.Awaitable[bool] | bool],
14
- adapter: ABCAdapter[Update, AdaptTo] | None = None,
15
- ) -> None:
16
- self.func = func
17
- self.adapter = adapter or RawUpdateAdapter() # type: ignore
18
-
19
- async def check(self, event: AdaptTo, ctx: Context) -> bool:
20
- result = self.func(event, ctx)
21
- if inspect.isawaitable(result):
22
- return await result
23
- return result # type: ignore
24
-
25
-
26
- __all__ = ("FuncRule",)
1
+ import inspect
2
+ import typing
3
+
4
+ from telegrinder.bot.dispatch.context import Context
5
+ from telegrinder.types.objects import Update
6
+
7
+ from .abc import ABCAdapter, ABCRule, AdaptTo, RawUpdateAdapter
8
+
9
+
10
+ class FuncRule(ABCRule, typing.Generic[AdaptTo]):
11
+ def __init__(
12
+ self,
13
+ func: typing.Callable[[AdaptTo, Context], typing.Awaitable[bool] | bool],
14
+ adapter: ABCAdapter[Update, AdaptTo] | None = None,
15
+ ) -> None:
16
+ self.func = func
17
+ self.adapter = adapter or RawUpdateAdapter() # type: ignore
18
+
19
+ async def check(self, event: AdaptTo, ctx: Context) -> bool:
20
+ result = self.func(event, ctx)
21
+ if inspect.isawaitable(result):
22
+ return await result
23
+ return result # type: ignore
24
+
25
+
26
+ __all__ = ("FuncRule",)
@@ -1,24 +1,24 @@
1
- import difflib
2
-
3
- from telegrinder.bot.dispatch.context import Context
4
- from telegrinder.node.text import Text
5
-
6
- from .abc import ABCRule
7
-
8
-
9
- class FuzzyText(ABCRule):
10
- def __init__(self, texts: str | list[str], min_ratio: float = 0.7) -> None:
11
- if isinstance(texts, str):
12
- texts = [texts]
13
- self.texts = texts
14
- self.min_ratio = min_ratio
15
-
16
- 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)
18
- if match < self.min_ratio:
19
- return False
20
- ctx.fuzzy_ratio = match
21
- return True
22
-
23
-
24
- __all__ = ("FuzzyText",)
1
+ import difflib
2
+
3
+ from telegrinder.bot.dispatch.context import Context
4
+ from telegrinder.node.text import Text
5
+
6
+ from .abc import ABCRule
7
+
8
+
9
+ class FuzzyText(ABCRule):
10
+ def __init__(self, texts: str | list[str], min_ratio: float = 0.7) -> None:
11
+ if isinstance(texts, str):
12
+ texts = [texts]
13
+ self.texts = texts
14
+ self.min_ratio = min_ratio
15
+
16
+ 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)
18
+ if match < self.min_ratio:
19
+ return False
20
+ ctx.fuzzy_ratio = match
21
+ return True
22
+
23
+
24
+ __all__ = ("FuzzyText",)
@@ -1,60 +1,60 @@
1
- import abc
2
- import typing
3
-
4
- from telegrinder.bot.cute_types import InlineQueryCute
5
- from telegrinder.bot.dispatch.context import Context
6
- from telegrinder.bot.rules.abc import ABCRule, CheckResult
7
- from telegrinder.bot.rules.adapter import EventAdapter
8
- from telegrinder.types.enums import ChatType, UpdateType
9
-
10
- from .markup import Markup, PatternLike, check_string
11
-
12
- InlineQuery: typing.TypeAlias = InlineQueryCute
13
-
14
-
15
- class InlineQueryRule(ABCRule[InlineQuery], abc.ABC):
16
- adapter: EventAdapter[InlineQuery] = EventAdapter(UpdateType.INLINE_QUERY, InlineQuery)
17
-
18
- @abc.abstractmethod
19
- def check(self, *args: typing.Any, **kwargs: typing.Any) -> CheckResult: ...
20
-
21
-
22
- class HasLocation(InlineQueryRule):
23
- def check(self, query: InlineQuery) -> bool:
24
- return bool(query.location)
25
-
26
-
27
- class InlineQueryChatType(InlineQueryRule):
28
- def __init__(self, chat_type: ChatType, /) -> None:
29
- self.chat_type = chat_type
30
-
31
- def check(self, query: InlineQuery) -> bool:
32
- return query.chat_type.map(lambda x: x == self.chat_type).unwrap_or(False)
33
-
34
-
35
- class InlineQueryText(InlineQueryRule):
36
- def __init__(self, texts: str | list[str], *, lower_case: bool = False) -> None:
37
- self.texts = [
38
- text.lower() if lower_case else text for text in ([texts] if isinstance(texts, str) else texts)
39
- ]
40
- self.lower_case = lower_case
41
-
42
- def check(self, query: InlineQuery) -> bool:
43
- return (query.query.lower() if self.lower_case else query.query) in self.texts
44
-
45
-
46
- class InlineQueryMarkup(InlineQueryRule):
47
- def __init__(self, patterns: PatternLike | list[PatternLike], /) -> None:
48
- self.patterns = Markup(patterns).patterns
49
-
50
- def check(self, query: InlineQuery, ctx: Context) -> bool:
51
- return check_string(self.patterns, query.query, ctx)
52
-
53
-
54
- __all__ = (
55
- "HasLocation",
56
- "InlineQueryChatType",
57
- "InlineQueryMarkup",
58
- "InlineQueryRule",
59
- "InlineQueryText",
60
- )
1
+ import abc
2
+ import typing
3
+
4
+ from telegrinder.bot.cute_types import InlineQueryCute
5
+ from telegrinder.bot.dispatch.context import Context
6
+ from telegrinder.bot.rules.abc import ABCRule, CheckResult
7
+ from telegrinder.bot.rules.adapter import EventAdapter
8
+ from telegrinder.types.enums import ChatType, UpdateType
9
+
10
+ from .markup import Markup, PatternLike, check_string
11
+
12
+ InlineQuery: typing.TypeAlias = InlineQueryCute
13
+
14
+
15
+ class InlineQueryRule(ABCRule[InlineQuery], abc.ABC):
16
+ adapter: EventAdapter[InlineQuery] = EventAdapter(UpdateType.INLINE_QUERY, InlineQuery)
17
+
18
+ @abc.abstractmethod
19
+ def check(self, *args: typing.Any, **kwargs: typing.Any) -> CheckResult: ...
20
+
21
+
22
+ class HasLocation(InlineQueryRule):
23
+ def check(self, query: InlineQuery) -> bool:
24
+ return bool(query.location)
25
+
26
+
27
+ class InlineQueryChatType(InlineQueryRule):
28
+ def __init__(self, chat_type: ChatType, /) -> None:
29
+ self.chat_type = chat_type
30
+
31
+ def check(self, query: InlineQuery) -> bool:
32
+ return query.chat_type.map(lambda x: x == self.chat_type).unwrap_or(False)
33
+
34
+
35
+ class InlineQueryText(InlineQueryRule):
36
+ def __init__(self, texts: str | list[str], *, lower_case: bool = False) -> None:
37
+ self.texts = [
38
+ text.lower() if lower_case else text for text in ([texts] if isinstance(texts, str) else texts)
39
+ ]
40
+ self.lower_case = lower_case
41
+
42
+ def check(self, query: InlineQuery) -> bool:
43
+ return (query.query.lower() if self.lower_case else query.query) in self.texts
44
+
45
+
46
+ class InlineQueryMarkup(InlineQueryRule):
47
+ def __init__(self, patterns: PatternLike | list[PatternLike], /) -> None:
48
+ self.patterns = Markup(patterns).patterns
49
+
50
+ def check(self, query: InlineQuery, ctx: Context) -> bool:
51
+ return check_string(self.patterns, query.query, ctx)
52
+
53
+
54
+ __all__ = (
55
+ "HasLocation",
56
+ "InlineQueryChatType",
57
+ "InlineQueryMarkup",
58
+ "InlineQueryRule",
59
+ "InlineQueryText",
60
+ )
@@ -1,20 +1,20 @@
1
- from telegrinder.node.text import TextInteger
2
-
3
- from .abc import ABCRule
4
- from .node import NodeRule
5
-
6
-
7
- class IsInteger(NodeRule):
8
- def __init__(self) -> None:
9
- super().__init__(TextInteger)
10
-
11
-
12
- class IntegerInRange(ABCRule):
13
- def __init__(self, rng: range) -> None:
14
- self.rng = rng
15
-
16
- def check(self, integer: TextInteger) -> bool:
17
- return integer in self.rng
18
-
19
-
20
- __all__ = ("IntegerInRange", "IsInteger")
1
+ from telegrinder.node.text import TextInteger
2
+
3
+ from .abc import ABCRule
4
+ from .node import NodeRule
5
+
6
+
7
+ class IsInteger(NodeRule):
8
+ def __init__(self) -> None:
9
+ super().__init__(TextInteger)
10
+
11
+
12
+ class IntegerInRange(ABCRule):
13
+ def __init__(self, rng: range) -> None:
14
+ self.rng = rng
15
+
16
+ def check(self, integer: TextInteger) -> bool:
17
+ return integer in self.rng
18
+
19
+
20
+ __all__ = ("IntegerInRange", "IsInteger")