telegrinder 0.3.4__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 -104
  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 -22
  35. telegrinder/bot/dispatch/process.py +157 -157
  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 -172
  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 +206 -206
  66. telegrinder/bot/rules/adapter/__init__.py +17 -17
  67. telegrinder/bot/rules/adapter/abc.py +31 -31
  68. telegrinder/bot/rules/adapter/errors.py +5 -5
  69. telegrinder/bot/rules/adapter/event.py +65 -65
  70. telegrinder/bot/rules/adapter/node.py +48 -48
  71. telegrinder/bot/rules/adapter/raw_event.py +27 -27
  72. telegrinder/bot/rules/adapter/raw_update.py +30 -30
  73. telegrinder/bot/rules/callback_data.py +163 -163
  74. telegrinder/bot/rules/chat_join.py +43 -43
  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 +56 -56
  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 -176
  96. telegrinder/bot/scenario/choice.py +51 -51
  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 +313 -313
  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 +20 -20
  105. telegrinder/node/attachment.py +87 -87
  106. telegrinder/node/base.py +157 -157
  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 +5 -5
  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 +128 -128
  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 -6950
  160. telegrinder/verification_utils.py +32 -32
  161. {telegrinder-0.3.4.dist-info → telegrinder-0.3.4.post1.dist-info}/LICENSE +22 -22
  162. {telegrinder-0.3.4.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.4.dist-info/RECORD +0 -165
  165. {telegrinder-0.3.4.dist-info → telegrinder-0.3.4.post1.dist-info}/WHEEL +0 -0
@@ -1,207 +1,207 @@
1
- import inspect
2
- from abc import ABC, abstractmethod
3
- from functools import cached_property
4
-
5
- import typing_extensions as typing
6
-
7
- from telegrinder.bot.cute_types import MessageCute, UpdateCute
8
- from telegrinder.bot.dispatch.context import Context
9
- from telegrinder.bot.dispatch.process import check_rule
10
- from telegrinder.bot.rules.adapter import ABCAdapter, RawUpdateAdapter
11
- from telegrinder.bot.rules.adapter.node import Event
12
- from telegrinder.node.base import Node, get_nodes, is_node
13
- from telegrinder.tools.i18n.abc import ABCTranslator
14
- from telegrinder.tools.magic import (
15
- cache_translation,
16
- get_annotations,
17
- get_cached_translation,
18
- get_default_args,
19
- )
20
- from telegrinder.types.objects import Update as UpdateObject
21
-
22
- if typing.TYPE_CHECKING:
23
- from telegrinder.node.composer import NodeCollection
24
-
25
- AdaptTo = typing.TypeVar("AdaptTo", default=typing.Any, contravariant=True)
26
-
27
- CheckResult: typing.TypeAlias = bool | typing.Awaitable[bool]
28
- Message: typing.TypeAlias = MessageCute
29
- Update: typing.TypeAlias = UpdateCute
30
-
31
-
32
- def with_caching_translations(func: typing.Callable[..., typing.Any]):
33
- """Should be used as decorator for .translate method. Caches rule translations."""
34
-
35
- async def wrapper(self: "ABCRule", translator: ABCTranslator):
36
- if translation := get_cached_translation(self, translator.locale):
37
- return translation
38
- translation = await func(self, translator)
39
- cache_translation(self, translator.locale, translation)
40
- return translation
41
-
42
- return wrapper
43
-
44
-
45
- class ABCRule(ABC, typing.Generic[AdaptTo]):
46
- adapter: ABCAdapter[UpdateObject, AdaptTo]
47
- requires: list["ABCRule"] = []
48
-
49
- if typing.TYPE_CHECKING:
50
-
51
- @abstractmethod
52
- def check(self, *args: typing.Any, **kwargs: typing.Any) -> CheckResult:
53
- pass
54
- else:
55
- adapter = RawUpdateAdapter()
56
-
57
- @abstractmethod
58
- def check(self, *args, **kwargs):
59
- pass
60
-
61
- def __init_subclass__(cls, requires: list["ABCRule"] | None = None) -> None:
62
- """Merges requirements from inherited classes and rule-specific requirements."""
63
-
64
- requirements = []
65
- for base in inspect.getmro(cls):
66
- if issubclass(base, ABCRule) and base != cls:
67
- requirements.extend(base.requires or ()) # type: ignore
68
-
69
- requirements.extend(requires or ())
70
- cls.requires = list(dict.fromkeys(requirements))
71
-
72
- def __and__(self, other: "ABCRule") -> "AndRule":
73
- """And Rule.
74
-
75
- ```python
76
- rule = HasText() & HasCaption()
77
- rule #> AndRule(HasText(), HasCaption()) -> True if all rules in an AndRule are True, otherwise False.
78
- ```
79
- """
80
-
81
- return AndRule(self, other)
82
-
83
- def __or__(self, other: "ABCRule") -> "OrRule":
84
- """Or Rule.
85
-
86
- ```python
87
- rule = HasText() | HasCaption()
88
- rule #> OrRule(HasText(), HasCaption()) -> True if any rule in an OrRule are True, otherwise False.
89
- ```
90
- """
91
-
92
- return OrRule(self, other)
93
-
94
- def __invert__(self) -> "NotRule":
95
- """Not Rule.
96
-
97
- ```python
98
- rule = ~HasText()
99
- rule # NotRule(HasText()) -> True if rule returned False, otherwise False.
100
- ```
101
- """
102
-
103
- return NotRule(self)
104
-
105
- def __repr__(self) -> str:
106
- return "<{}: adapter={!r}>".format(
107
- self.__class__.__name__,
108
- self.adapter,
109
- )
110
-
111
- @cached_property
112
- def required_nodes(self) -> dict[str, type[Node]]:
113
- return get_nodes(self.check)
114
-
115
- def as_optional(self) -> "ABCRule":
116
- return self | Always()
117
-
118
- def should_fail(self) -> "ABCRule":
119
- return self & Never()
120
-
121
- async def bounding_check(
122
- self,
123
- adapted_value: AdaptTo,
124
- ctx: Context,
125
- node_col: "NodeCollection | None" = None,
126
- ) -> bool:
127
- bound_check_rule = self.check
128
- kw = {}
129
- node_col_values = node_col.values if node_col is not None else {}
130
- temp_ctx = get_default_args(bound_check_rule) | ctx
131
-
132
- for i, (k, v) in enumerate(get_annotations(bound_check_rule).items()):
133
- if (isinstance(adapted_value, Event) and i == 0) or ( # First arg is Event
134
- isinstance(v, type) and isinstance(adapted_value, v)
135
- ):
136
- kw[k] = adapted_value if not isinstance(adapted_value, Event) else adapted_value.obj
137
- elif is_node(v):
138
- assert k in node_col_values, "Node is undefined, error while bounding."
139
- kw[k] = node_col_values[k]
140
- elif k in temp_ctx:
141
- kw[k] = temp_ctx[k]
142
- elif v is Context:
143
- kw[k] = ctx
144
- else:
145
- raise LookupError(
146
- f"Cannot bound {k!r} of type {v!r} to '{self.__class__.__qualname__}.check()', "
147
- "because it cannot be resolved."
148
- )
149
-
150
- result = bound_check_rule(**kw) # type: ignore
151
- if inspect.isawaitable(result):
152
- result = await result
153
- return result
154
-
155
- async def translate(self, translator: ABCTranslator) -> typing.Self:
156
- return self
157
-
158
-
159
- class AndRule(ABCRule):
160
- def __init__(self, *rules: ABCRule) -> None:
161
- self.rules = rules
162
-
163
- async def check(self, event: Update, ctx: Context) -> bool:
164
- ctx_copy = ctx.copy()
165
- for rule in self.rules:
166
- if not await check_rule(event.ctx_api, rule, event, ctx_copy):
167
- return False
168
- ctx |= ctx_copy
169
- return True
170
-
171
-
172
- class OrRule(ABCRule):
173
- def __init__(self, *rules: ABCRule) -> None:
174
- self.rules = rules
175
-
176
- async def check(self, event: Update, ctx: Context) -> bool:
177
- for rule in self.rules:
178
- ctx_copy = ctx.copy()
179
- if await check_rule(event.ctx_api, rule, event, ctx_copy):
180
- ctx |= ctx_copy
181
- return True
182
- return False
183
-
184
-
185
- class NotRule(ABCRule):
186
- def __init__(self, rule: ABCRule) -> None:
187
- self.rule = rule
188
-
189
- async def check(self, event: Update, ctx: Context) -> bool:
190
- ctx_copy = ctx.copy()
191
- return not await check_rule(event.ctx_api, self.rule, event, ctx_copy)
192
-
193
-
194
- class Never(ABCRule):
195
- async def check(self) -> typing.Literal[False]:
196
- return False
197
-
198
-
199
- class Always(ABCRule):
200
- async def check(self) -> typing.Literal[True]:
201
- return True
202
-
203
-
204
- __all__ = (
1
+ import inspect
2
+ from abc import ABC, abstractmethod
3
+ from functools import cached_property
4
+
5
+ import typing_extensions as typing
6
+
7
+ from telegrinder.bot.cute_types import MessageCute, UpdateCute
8
+ from telegrinder.bot.dispatch.context import Context
9
+ from telegrinder.bot.dispatch.process import check_rule
10
+ from telegrinder.bot.rules.adapter import ABCAdapter, RawUpdateAdapter
11
+ from telegrinder.bot.rules.adapter.node import Event
12
+ from telegrinder.node.base import Node, get_nodes, is_node
13
+ from telegrinder.tools.i18n.abc import ABCTranslator
14
+ from telegrinder.tools.magic import (
15
+ cache_translation,
16
+ get_annotations,
17
+ get_cached_translation,
18
+ get_default_args,
19
+ )
20
+ from telegrinder.types.objects import Update as UpdateObject
21
+
22
+ if typing.TYPE_CHECKING:
23
+ from telegrinder.node.composer import NodeCollection
24
+
25
+ AdaptTo = typing.TypeVar("AdaptTo", default=typing.Any, contravariant=True)
26
+
27
+ CheckResult: typing.TypeAlias = bool | typing.Awaitable[bool]
28
+ Message: typing.TypeAlias = MessageCute
29
+ Update: typing.TypeAlias = UpdateCute
30
+
31
+
32
+ def with_caching_translations(func: typing.Callable[..., typing.Any]):
33
+ """Should be used as decorator for .translate method. Caches rule translations."""
34
+
35
+ async def wrapper(self: "ABCRule", translator: ABCTranslator):
36
+ if translation := get_cached_translation(self, translator.locale):
37
+ return translation
38
+ translation = await func(self, translator)
39
+ cache_translation(self, translator.locale, translation)
40
+ return translation
41
+
42
+ return wrapper
43
+
44
+
45
+ class ABCRule(ABC, typing.Generic[AdaptTo]):
46
+ adapter: ABCAdapter[UpdateObject, AdaptTo]
47
+ requires: list["ABCRule"] = []
48
+
49
+ if typing.TYPE_CHECKING:
50
+
51
+ @abstractmethod
52
+ def check(self, *args: typing.Any, **kwargs: typing.Any) -> CheckResult:
53
+ pass
54
+ else:
55
+ adapter = RawUpdateAdapter()
56
+
57
+ @abstractmethod
58
+ def check(self, *args, **kwargs):
59
+ pass
60
+
61
+ def __init_subclass__(cls, requires: list["ABCRule"] | None = None) -> None:
62
+ """Merges requirements from inherited classes and rule-specific requirements."""
63
+
64
+ requirements = []
65
+ for base in inspect.getmro(cls):
66
+ if issubclass(base, ABCRule) and base != cls:
67
+ requirements.extend(base.requires or ()) # type: ignore
68
+
69
+ requirements.extend(requires or ())
70
+ cls.requires = list(dict.fromkeys(requirements))
71
+
72
+ def __and__(self, other: "ABCRule") -> "AndRule":
73
+ """And Rule.
74
+
75
+ ```python
76
+ rule = HasText() & HasCaption()
77
+ rule #> AndRule(HasText(), HasCaption()) -> True if all rules in an AndRule are True, otherwise False.
78
+ ```
79
+ """
80
+
81
+ return AndRule(self, other)
82
+
83
+ def __or__(self, other: "ABCRule") -> "OrRule":
84
+ """Or Rule.
85
+
86
+ ```python
87
+ rule = HasText() | HasCaption()
88
+ rule #> OrRule(HasText(), HasCaption()) -> True if any rule in an OrRule are True, otherwise False.
89
+ ```
90
+ """
91
+
92
+ return OrRule(self, other)
93
+
94
+ def __invert__(self) -> "NotRule":
95
+ """Not Rule.
96
+
97
+ ```python
98
+ rule = ~HasText()
99
+ rule # NotRule(HasText()) -> True if rule returned False, otherwise False.
100
+ ```
101
+ """
102
+
103
+ return NotRule(self)
104
+
105
+ def __repr__(self) -> str:
106
+ return "<{}: adapter={!r}>".format(
107
+ self.__class__.__name__,
108
+ self.adapter,
109
+ )
110
+
111
+ @cached_property
112
+ def required_nodes(self) -> dict[str, type[Node]]:
113
+ return get_nodes(self.check)
114
+
115
+ def as_optional(self) -> "ABCRule":
116
+ return self | Always()
117
+
118
+ def should_fail(self) -> "ABCRule":
119
+ return self & Never()
120
+
121
+ async def bounding_check(
122
+ self,
123
+ adapted_value: AdaptTo,
124
+ ctx: Context,
125
+ node_col: "NodeCollection | None" = None,
126
+ ) -> bool:
127
+ bound_check_rule = self.check
128
+ kw = {}
129
+ node_col_values = node_col.values if node_col is not None else {}
130
+ temp_ctx = get_default_args(bound_check_rule) | ctx
131
+
132
+ for i, (k, v) in enumerate(get_annotations(bound_check_rule).items()):
133
+ if (isinstance(adapted_value, Event) and i == 0) or ( # First arg is Event
134
+ isinstance(v, type) and isinstance(adapted_value, v)
135
+ ):
136
+ kw[k] = adapted_value if not isinstance(adapted_value, Event) else adapted_value.obj
137
+ elif is_node(v):
138
+ assert k in node_col_values, "Node is undefined, error while bounding."
139
+ kw[k] = node_col_values[k]
140
+ elif k in temp_ctx:
141
+ kw[k] = temp_ctx[k]
142
+ elif v is Context:
143
+ kw[k] = ctx
144
+ else:
145
+ raise LookupError(
146
+ f"Cannot bound {k!r} of type {v!r} to '{self.__class__.__qualname__}.check()', "
147
+ "because it cannot be resolved."
148
+ )
149
+
150
+ result = bound_check_rule(**kw) # type: ignore
151
+ if inspect.isawaitable(result):
152
+ result = await result
153
+ return result
154
+
155
+ async def translate(self, translator: ABCTranslator) -> typing.Self:
156
+ return self
157
+
158
+
159
+ class AndRule(ABCRule):
160
+ def __init__(self, *rules: ABCRule) -> None:
161
+ self.rules = rules
162
+
163
+ async def check(self, event: Update, ctx: Context) -> bool:
164
+ ctx_copy = ctx.copy()
165
+ for rule in self.rules:
166
+ if not await check_rule(event.ctx_api, rule, event, ctx_copy):
167
+ return False
168
+ ctx |= ctx_copy
169
+ return True
170
+
171
+
172
+ class OrRule(ABCRule):
173
+ def __init__(self, *rules: ABCRule) -> None:
174
+ self.rules = rules
175
+
176
+ async def check(self, event: Update, ctx: Context) -> bool:
177
+ for rule in self.rules:
178
+ ctx_copy = ctx.copy()
179
+ if await check_rule(event.ctx_api, rule, event, ctx_copy):
180
+ ctx |= ctx_copy
181
+ return True
182
+ return False
183
+
184
+
185
+ class NotRule(ABCRule):
186
+ def __init__(self, rule: ABCRule) -> None:
187
+ self.rule = rule
188
+
189
+ async def check(self, event: Update, ctx: Context) -> bool:
190
+ ctx_copy = ctx.copy()
191
+ return not await check_rule(event.ctx_api, self.rule, event, ctx_copy)
192
+
193
+
194
+ class Never(ABCRule):
195
+ async def check(self) -> typing.Literal[False]:
196
+ return False
197
+
198
+
199
+ class Always(ABCRule):
200
+ async def check(self) -> typing.Literal[True]:
201
+ return True
202
+
203
+
204
+ __all__ = (
205
205
  "ABCRule",
206
206
  "Always",
207
207
  "AndRule",
@@ -209,5 +209,5 @@ __all__ = (
209
209
  "Never",
210
210
  "NotRule",
211
211
  "OrRule",
212
- "with_caching_translations",
213
- )
212
+ "with_caching_translations",
213
+ )
@@ -1,17 +1,17 @@
1
- from telegrinder.bot.rules.adapter.abc import ABCAdapter, AdaptResult, Event
2
- from telegrinder.bot.rules.adapter.errors import AdapterError
3
- from telegrinder.bot.rules.adapter.event import EventAdapter
4
- from telegrinder.bot.rules.adapter.node import NodeAdapter
5
- from telegrinder.bot.rules.adapter.raw_event import RawEventAdapter
6
- from telegrinder.bot.rules.adapter.raw_update import RawUpdateAdapter
7
-
8
- __all__ = (
9
- "ABCAdapter",
10
- "AdaptResult",
11
- "AdapterError",
12
- "Event",
13
- "EventAdapter",
14
- "NodeAdapter",
15
- "RawEventAdapter",
16
- "RawUpdateAdapter",
17
- )
1
+ from telegrinder.bot.rules.adapter.abc import ABCAdapter, AdaptResult, Event
2
+ from telegrinder.bot.rules.adapter.errors import AdapterError
3
+ from telegrinder.bot.rules.adapter.event import EventAdapter
4
+ from telegrinder.bot.rules.adapter.node import NodeAdapter
5
+ from telegrinder.bot.rules.adapter.raw_event import RawEventAdapter
6
+ from telegrinder.bot.rules.adapter.raw_update import RawUpdateAdapter
7
+
8
+ __all__ = (
9
+ "ABCAdapter",
10
+ "AdaptResult",
11
+ "AdapterError",
12
+ "Event",
13
+ "EventAdapter",
14
+ "NodeAdapter",
15
+ "RawEventAdapter",
16
+ "RawUpdateAdapter",
17
+ )
@@ -1,31 +1,31 @@
1
- import abc
2
- import dataclasses
3
- import typing
4
-
5
- from fntypes.result import Result
6
-
7
- from telegrinder.api.api import API
8
- from telegrinder.bot.dispatch.context import Context
9
- from telegrinder.bot.rules.adapter.errors import AdapterError
10
- from telegrinder.model import Model
11
-
12
- From = typing.TypeVar("From", bound=Model)
13
- To = typing.TypeVar("To")
14
-
15
- AdaptResult: typing.TypeAlias = Result[To, AdapterError] | typing.Awaitable[Result[To, AdapterError]]
16
-
17
-
18
- class ABCAdapter(abc.ABC, typing.Generic[From, To]):
19
- ADAPTED_VALUE_KEY: str | None = None
20
-
21
- @abc.abstractmethod
22
- def adapt(self, api: API, update: From, context: Context) -> AdaptResult[To]:
23
- pass
24
-
25
-
26
- @dataclasses.dataclass(slots=True)
27
- class Event(typing.Generic[To]):
28
- obj: To
29
-
30
-
31
- __all__ = ("ABCAdapter", "AdaptResult", "Event")
1
+ import abc
2
+ import dataclasses
3
+ import typing
4
+
5
+ from fntypes.result import Result
6
+
7
+ from telegrinder.api.api import API
8
+ from telegrinder.bot.dispatch.context import Context
9
+ from telegrinder.bot.rules.adapter.errors import AdapterError
10
+ from telegrinder.model import Model
11
+
12
+ From = typing.TypeVar("From", bound=Model)
13
+ To = typing.TypeVar("To")
14
+
15
+ AdaptResult: typing.TypeAlias = Result[To, AdapterError] | typing.Awaitable[Result[To, AdapterError]]
16
+
17
+
18
+ class ABCAdapter(abc.ABC, typing.Generic[From, To]):
19
+ ADAPTED_VALUE_KEY: str | None = None
20
+
21
+ @abc.abstractmethod
22
+ def adapt(self, api: API, update: From, context: Context) -> AdaptResult[To]:
23
+ pass
24
+
25
+
26
+ @dataclasses.dataclass(slots=True)
27
+ class Event(typing.Generic[To]):
28
+ obj: To
29
+
30
+
31
+ __all__ = ("ABCAdapter", "AdaptResult", "Event")
@@ -1,5 +1,5 @@
1
- class AdapterError(RuntimeError):
2
- pass
3
-
4
-
5
- __all__ = ("AdapterError",)
1
+ class AdapterError(RuntimeError):
2
+ pass
3
+
4
+
5
+ __all__ = ("AdapterError",)
@@ -1,65 +1,65 @@
1
- import typing
2
-
3
- from fntypes.result import Error, Ok, Result
4
-
5
- from telegrinder.api.api import API
6
- from telegrinder.bot.cute_types.base import BaseCute
7
- from telegrinder.bot.cute_types.update import UpdateCute
8
- from telegrinder.bot.dispatch.context import Context
9
- from telegrinder.bot.rules.adapter.abc import ABCAdapter
10
- from telegrinder.bot.rules.adapter.errors import AdapterError
11
- from telegrinder.bot.rules.adapter.raw_update import RawUpdateAdapter
12
- from telegrinder.types.enums import UpdateType
13
- from telegrinder.types.objects import Model, Update
14
-
15
- ToCute = typing.TypeVar("ToCute", bound=BaseCute)
16
-
17
-
18
- class EventAdapter(ABCAdapter[Update, ToCute]):
19
- ADAPTED_VALUE_KEY: str = "_adapted_cute_event"
20
-
21
- def __init__(self, event: UpdateType | type[Model], cute_model: type[ToCute]) -> None:
22
- self.event = event
23
- self.cute_model = cute_model
24
-
25
- def __repr__(self) -> str:
26
- raw_update_type = (
27
- f"Update -> {self.event.__name__}"
28
- if isinstance(self.event, type)
29
- else f"Update.{self.event.value}"
30
- )
31
- return "<{}: adapt {} -> {}>".format(
32
- self.__class__.__name__,
33
- raw_update_type,
34
- self.cute_model.__name__,
35
- )
36
-
37
- def get_event(self, update: UpdateCute) -> Model | None:
38
- if isinstance(self.event, UpdateType) and self.event == update.update_type:
39
- return update.incoming_update
40
-
41
- if not isinstance(self.event, UpdateType) and (event := update.get_event(self.event)):
42
- return event.unwrap()
43
-
44
- return None
45
-
46
- def adapt(self, api: API, update: Update, context: Context) -> Result[ToCute, AdapterError]:
47
- match RawUpdateAdapter().adapt(api, update, context):
48
- case Ok(update_cute) if event := self.get_event(update_cute):
49
- if self.ADAPTED_VALUE_KEY in context:
50
- return Ok(context[self.ADAPTED_VALUE_KEY])
51
-
52
- adapted = (
53
- typing.cast(ToCute, event)
54
- if isinstance(event, BaseCute)
55
- else self.cute_model.from_update(event, bound_api=api)
56
- )
57
- context[self.ADAPTED_VALUE_KEY] = adapted
58
- return Ok(adapted)
59
- case Error(_) as err:
60
- return err
61
- case _:
62
- return Error(AdapterError(f"Update is not an {self.event!r}."))
63
-
64
-
65
- __all__ = ("EventAdapter",)
1
+ import typing
2
+
3
+ from fntypes.result import Error, Ok, Result
4
+
5
+ from telegrinder.api.api import API
6
+ from telegrinder.bot.cute_types.base import BaseCute
7
+ from telegrinder.bot.cute_types.update import UpdateCute
8
+ from telegrinder.bot.dispatch.context import Context
9
+ from telegrinder.bot.rules.adapter.abc import ABCAdapter
10
+ from telegrinder.bot.rules.adapter.errors import AdapterError
11
+ from telegrinder.bot.rules.adapter.raw_update import RawUpdateAdapter
12
+ from telegrinder.types.enums import UpdateType
13
+ from telegrinder.types.objects import Model, Update
14
+
15
+ ToCute = typing.TypeVar("ToCute", bound=BaseCute)
16
+
17
+
18
+ class EventAdapter(ABCAdapter[Update, ToCute]):
19
+ ADAPTED_VALUE_KEY: str = "_adapted_cute_event"
20
+
21
+ def __init__(self, event: UpdateType | type[Model], cute_model: type[ToCute]) -> None:
22
+ self.event = event
23
+ self.cute_model = cute_model
24
+
25
+ def __repr__(self) -> str:
26
+ raw_update_type = (
27
+ f"Update -> {self.event.__name__}"
28
+ if isinstance(self.event, type)
29
+ else f"Update.{self.event.value}"
30
+ )
31
+ return "<{}: adapt {} -> {}>".format(
32
+ self.__class__.__name__,
33
+ raw_update_type,
34
+ self.cute_model.__name__,
35
+ )
36
+
37
+ def get_event(self, update: UpdateCute) -> Model | None:
38
+ if isinstance(self.event, UpdateType) and self.event == update.update_type:
39
+ return update.incoming_update
40
+
41
+ if not isinstance(self.event, UpdateType) and (event := update.get_event(self.event)):
42
+ return event.unwrap()
43
+
44
+ return None
45
+
46
+ def adapt(self, api: API, update: Update, context: Context) -> Result[ToCute, AdapterError]:
47
+ match RawUpdateAdapter().adapt(api, update, context):
48
+ case Ok(update_cute) if event := self.get_event(update_cute):
49
+ if self.ADAPTED_VALUE_KEY in context:
50
+ return Ok(context[self.ADAPTED_VALUE_KEY])
51
+
52
+ adapted = (
53
+ typing.cast(ToCute, event)
54
+ if isinstance(event, BaseCute)
55
+ else self.cute_model.from_update(event, bound_api=api)
56
+ )
57
+ context[self.ADAPTED_VALUE_KEY] = adapted
58
+ return Ok(adapted)
59
+ case Error(_) as err:
60
+ return err
61
+ case _:
62
+ return Error(AdapterError(f"Update is not an {self.event!r}."))
63
+
64
+
65
+ __all__ = ("EventAdapter",)