telegrinder 0.3.4__py3-none-any.whl → 0.4.0__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 (192) hide show
  1. telegrinder/__init__.py +148 -149
  2. telegrinder/api/__init__.py +9 -8
  3. telegrinder/api/api.py +101 -93
  4. telegrinder/api/error.py +20 -16
  5. telegrinder/api/response.py +20 -20
  6. telegrinder/api/token.py +36 -36
  7. telegrinder/bot/__init__.py +72 -66
  8. telegrinder/bot/bot.py +83 -76
  9. telegrinder/bot/cute_types/__init__.py +19 -17
  10. telegrinder/bot/cute_types/base.py +184 -258
  11. telegrinder/bot/cute_types/callback_query.py +400 -385
  12. telegrinder/bot/cute_types/chat_join_request.py +62 -61
  13. telegrinder/bot/cute_types/chat_member_updated.py +157 -160
  14. telegrinder/bot/cute_types/inline_query.py +44 -43
  15. telegrinder/bot/cute_types/message.py +2590 -2637
  16. telegrinder/bot/cute_types/pre_checkout_query.py +42 -0
  17. telegrinder/bot/cute_types/update.py +112 -104
  18. telegrinder/bot/cute_types/utils.py +62 -95
  19. telegrinder/bot/dispatch/__init__.py +59 -55
  20. telegrinder/bot/dispatch/abc.py +76 -77
  21. telegrinder/bot/dispatch/context.py +96 -98
  22. telegrinder/bot/dispatch/dispatch.py +254 -202
  23. telegrinder/bot/dispatch/handler/__init__.py +13 -13
  24. telegrinder/bot/dispatch/handler/abc.py +23 -24
  25. telegrinder/bot/dispatch/handler/audio_reply.py +44 -44
  26. telegrinder/bot/dispatch/handler/base.py +57 -57
  27. telegrinder/bot/dispatch/handler/document_reply.py +44 -44
  28. telegrinder/bot/dispatch/handler/func.py +129 -135
  29. telegrinder/bot/dispatch/handler/media_group_reply.py +44 -43
  30. telegrinder/bot/dispatch/handler/message_reply.py +36 -36
  31. telegrinder/bot/dispatch/handler/photo_reply.py +44 -44
  32. telegrinder/bot/dispatch/handler/sticker_reply.py +37 -37
  33. telegrinder/bot/dispatch/handler/video_reply.py +44 -44
  34. telegrinder/bot/dispatch/middleware/__init__.py +3 -3
  35. telegrinder/bot/dispatch/middleware/abc.py +97 -22
  36. telegrinder/bot/dispatch/middleware/global_middleware.py +70 -0
  37. telegrinder/bot/dispatch/process.py +151 -157
  38. telegrinder/bot/dispatch/return_manager/__init__.py +15 -13
  39. telegrinder/bot/dispatch/return_manager/abc.py +104 -108
  40. telegrinder/bot/dispatch/return_manager/callback_query.py +20 -20
  41. telegrinder/bot/dispatch/return_manager/inline_query.py +15 -15
  42. telegrinder/bot/dispatch/return_manager/message.py +36 -36
  43. telegrinder/bot/dispatch/return_manager/pre_checkout_query.py +20 -0
  44. telegrinder/bot/dispatch/view/__init__.py +15 -13
  45. telegrinder/bot/dispatch/view/abc.py +45 -41
  46. telegrinder/bot/dispatch/view/base.py +231 -200
  47. telegrinder/bot/dispatch/view/box.py +140 -129
  48. telegrinder/bot/dispatch/view/callback_query.py +16 -17
  49. telegrinder/bot/dispatch/view/chat_join_request.py +11 -16
  50. telegrinder/bot/dispatch/view/chat_member.py +37 -39
  51. telegrinder/bot/dispatch/view/inline_query.py +16 -17
  52. telegrinder/bot/dispatch/view/message.py +43 -44
  53. telegrinder/bot/dispatch/view/pre_checkout_query.py +16 -0
  54. telegrinder/bot/dispatch/view/raw.py +116 -114
  55. telegrinder/bot/dispatch/waiter_machine/__init__.py +17 -17
  56. telegrinder/bot/dispatch/waiter_machine/actions.py +14 -13
  57. telegrinder/bot/dispatch/waiter_machine/hasher/__init__.py +8 -8
  58. telegrinder/bot/dispatch/waiter_machine/hasher/callback.py +55 -55
  59. telegrinder/bot/dispatch/waiter_machine/hasher/hasher.py +59 -57
  60. telegrinder/bot/dispatch/waiter_machine/hasher/message.py +51 -51
  61. telegrinder/bot/dispatch/waiter_machine/hasher/state.py +20 -19
  62. telegrinder/bot/dispatch/waiter_machine/machine.py +251 -172
  63. telegrinder/bot/dispatch/waiter_machine/middleware.py +94 -89
  64. telegrinder/bot/dispatch/waiter_machine/short_state.py +57 -68
  65. telegrinder/bot/polling/__init__.py +4 -4
  66. telegrinder/bot/polling/abc.py +25 -25
  67. telegrinder/bot/polling/polling.py +139 -131
  68. telegrinder/bot/rules/__init__.py +85 -62
  69. telegrinder/bot/rules/abc.py +213 -206
  70. telegrinder/bot/rules/callback_data.py +122 -163
  71. telegrinder/bot/rules/chat_join.py +45 -43
  72. telegrinder/bot/rules/command.py +126 -126
  73. telegrinder/bot/rules/enum_text.py +33 -36
  74. telegrinder/bot/rules/func.py +28 -26
  75. telegrinder/bot/rules/fuzzy.py +24 -24
  76. telegrinder/bot/rules/id.py +24 -0
  77. telegrinder/bot/rules/inline.py +58 -56
  78. telegrinder/bot/rules/integer.py +21 -20
  79. telegrinder/bot/rules/is_from.py +127 -127
  80. telegrinder/bot/rules/logic.py +18 -0
  81. telegrinder/bot/rules/markup.py +42 -43
  82. telegrinder/bot/rules/mention.py +14 -14
  83. telegrinder/bot/rules/message.py +15 -17
  84. telegrinder/bot/rules/message_entities.py +33 -35
  85. telegrinder/bot/rules/node.py +33 -27
  86. telegrinder/bot/rules/payload.py +81 -0
  87. telegrinder/bot/rules/payment_invoice.py +29 -0
  88. telegrinder/bot/rules/regex.py +36 -37
  89. telegrinder/bot/rules/rule_enum.py +72 -72
  90. telegrinder/bot/rules/start.py +42 -42
  91. telegrinder/bot/rules/state.py +35 -37
  92. telegrinder/bot/rules/text.py +38 -33
  93. telegrinder/bot/rules/update.py +15 -15
  94. telegrinder/bot/scenario/__init__.py +5 -5
  95. telegrinder/bot/scenario/abc.py +17 -19
  96. telegrinder/bot/scenario/checkbox.py +174 -176
  97. telegrinder/bot/scenario/choice.py +48 -51
  98. telegrinder/client/__init__.py +12 -4
  99. telegrinder/client/abc.py +100 -75
  100. telegrinder/client/aiohttp.py +134 -130
  101. telegrinder/client/form_data.py +31 -0
  102. telegrinder/client/sonic.py +212 -0
  103. telegrinder/model.py +208 -315
  104. telegrinder/modules.py +239 -237
  105. telegrinder/msgspec_json.py +14 -14
  106. telegrinder/msgspec_utils.py +478 -410
  107. telegrinder/node/__init__.py +86 -25
  108. telegrinder/node/attachment.py +163 -87
  109. telegrinder/node/base.py +288 -160
  110. telegrinder/node/callback_query.py +54 -53
  111. telegrinder/node/command.py +34 -33
  112. telegrinder/node/composer.py +163 -198
  113. telegrinder/node/container.py +33 -27
  114. telegrinder/node/either.py +82 -0
  115. telegrinder/node/event.py +54 -65
  116. telegrinder/node/file.py +51 -0
  117. telegrinder/node/me.py +15 -16
  118. telegrinder/node/payload.py +78 -0
  119. telegrinder/node/polymorphic.py +67 -48
  120. telegrinder/node/rule.py +72 -76
  121. telegrinder/node/scope.py +36 -38
  122. telegrinder/node/source.py +87 -71
  123. telegrinder/node/text.py +53 -41
  124. telegrinder/node/tools/__init__.py +3 -3
  125. telegrinder/node/tools/generator.py +36 -40
  126. telegrinder/py.typed +0 -0
  127. telegrinder/rules.py +1 -62
  128. telegrinder/tools/__init__.py +152 -93
  129. telegrinder/tools/adapter/__init__.py +19 -0
  130. telegrinder/tools/adapter/abc.py +49 -0
  131. telegrinder/tools/adapter/dataclass.py +56 -0
  132. telegrinder/{bot/rules → tools}/adapter/errors.py +5 -5
  133. telegrinder/{bot/rules → tools}/adapter/event.py +63 -65
  134. telegrinder/{bot/rules → tools}/adapter/node.py +46 -48
  135. telegrinder/{bot/rules → tools}/adapter/raw_event.py +27 -27
  136. telegrinder/{bot/rules → tools}/adapter/raw_update.py +30 -30
  137. telegrinder/tools/buttons.py +106 -80
  138. telegrinder/tools/callback_data_serilization/__init__.py +5 -0
  139. telegrinder/tools/callback_data_serilization/abc.py +51 -0
  140. telegrinder/tools/callback_data_serilization/json_ser.py +60 -0
  141. telegrinder/tools/callback_data_serilization/msgpack_ser.py +172 -0
  142. telegrinder/tools/error_handler/__init__.py +7 -7
  143. telegrinder/tools/error_handler/abc.py +30 -33
  144. telegrinder/tools/error_handler/error.py +9 -9
  145. telegrinder/tools/error_handler/error_handler.py +179 -193
  146. telegrinder/tools/formatting/__init__.py +83 -63
  147. telegrinder/tools/formatting/deep_links.py +541 -0
  148. telegrinder/tools/formatting/{html.py → html_formatter.py} +266 -294
  149. telegrinder/tools/formatting/spec_html_formats.py +71 -117
  150. telegrinder/tools/functional.py +8 -12
  151. telegrinder/tools/global_context/__init__.py +7 -7
  152. telegrinder/tools/global_context/abc.py +63 -63
  153. telegrinder/tools/global_context/global_context.py +387 -412
  154. telegrinder/tools/global_context/telegrinder_ctx.py +27 -27
  155. telegrinder/tools/i18n/__init__.py +7 -7
  156. telegrinder/tools/i18n/abc.py +30 -30
  157. telegrinder/tools/i18n/middleware/__init__.py +3 -3
  158. telegrinder/tools/i18n/middleware/abc.py +22 -25
  159. telegrinder/tools/i18n/simple.py +43 -43
  160. telegrinder/tools/input_file_directory.py +30 -0
  161. telegrinder/tools/keyboard.py +128 -128
  162. telegrinder/tools/lifespan.py +105 -0
  163. telegrinder/tools/limited_dict.py +32 -37
  164. telegrinder/tools/loop_wrapper/__init__.py +4 -4
  165. telegrinder/tools/loop_wrapper/abc.py +20 -15
  166. telegrinder/tools/loop_wrapper/loop_wrapper.py +169 -224
  167. telegrinder/tools/magic.py +307 -157
  168. telegrinder/tools/parse_mode.py +6 -6
  169. telegrinder/tools/state_storage/__init__.py +4 -4
  170. telegrinder/tools/state_storage/abc.py +31 -35
  171. telegrinder/tools/state_storage/memory.py +25 -25
  172. telegrinder/tools/strings.py +13 -0
  173. telegrinder/types/__init__.py +268 -260
  174. telegrinder/types/enums.py +711 -701
  175. telegrinder/types/input_file.py +51 -0
  176. telegrinder/types/methods.py +5055 -4633
  177. telegrinder/types/objects.py +7058 -6950
  178. telegrinder/verification_utils.py +30 -32
  179. {telegrinder-0.3.4.dist-info → telegrinder-0.4.0.dist-info}/LICENSE +22 -22
  180. telegrinder-0.4.0.dist-info/METADATA +144 -0
  181. telegrinder-0.4.0.dist-info/RECORD +182 -0
  182. {telegrinder-0.3.4.dist-info → telegrinder-0.4.0.dist-info}/WHEEL +1 -1
  183. telegrinder/bot/rules/adapter/__init__.py +0 -17
  184. telegrinder/bot/rules/adapter/abc.py +0 -31
  185. telegrinder/node/message.py +0 -14
  186. telegrinder/node/update.py +0 -15
  187. telegrinder/tools/formatting/links.py +0 -38
  188. telegrinder/tools/kb_set/__init__.py +0 -4
  189. telegrinder/tools/kb_set/base.py +0 -15
  190. telegrinder/tools/kb_set/yaml.py +0 -63
  191. telegrinder-0.3.4.dist-info/METADATA +0 -110
  192. telegrinder-0.3.4.dist-info/RECORD +0 -165
@@ -1,57 +1,57 @@
1
- import abc
2
- import typing
3
-
4
- from fntypes.result import Result
5
-
6
- from telegrinder.api.api import API
7
- from telegrinder.api.error import APIError
8
- from telegrinder.bot.cute_types.message import MessageCute
9
- from telegrinder.bot.dispatch.context import Context
10
- from telegrinder.bot.dispatch.handler.abc import ABCHandler
11
- from telegrinder.bot.dispatch.process import check_rule
12
- from telegrinder.bot.rules.abc import ABCRule
13
- from telegrinder.modules import logger
14
- from telegrinder.types.objects import Update
15
-
16
- APIMethod: typing.TypeAlias = typing.Callable[
17
- typing.Concatenate[MessageCute, ...], typing.Awaitable[Result[typing.Any, APIError]]
18
- ]
19
-
20
-
21
- class BaseReplyHandler(ABCHandler[MessageCute], abc.ABC):
22
- def __init__(
23
- self,
24
- *rules: ABCRule,
25
- is_blocking: bool = True,
26
- as_reply: bool = False,
27
- preset_context: Context | None = None,
28
- **default_params: typing.Any,
29
- ) -> None:
30
- self.rules = list(rules)
31
- self.as_reply = as_reply
32
- self.is_blocking = is_blocking
33
- self.default_params = default_params
34
- self.preset_context = preset_context or Context()
35
-
36
- def __repr__(self) -> str:
37
- return f"<{self.__class__.__qualname__}>"
38
-
39
- async def check(self, api: API, event: Update, ctx: Context | None = None) -> bool:
40
- ctx = Context(raw_update=event) if ctx is None else ctx
41
- temp_ctx = ctx.copy()
42
- temp_ctx |= self.preset_context
43
-
44
- for rule in self.rules:
45
- if not await check_rule(api, rule, event, ctx):
46
- logger.debug("Rule {!r} failed!", rule)
47
- return False
48
-
49
- ctx |= temp_ctx
50
- return True
51
-
52
- @abc.abstractmethod
53
- async def run(self, api: API, event: MessageCute, ctx: Context) -> typing.Any:
54
- pass
55
-
56
-
57
- __all__ = ("BaseReplyHandler",)
1
+ import abc
2
+ import typing
3
+
4
+ from fntypes.result import Result
5
+
6
+ from telegrinder.api.api import API
7
+ from telegrinder.api.error import APIError
8
+ from telegrinder.bot.cute_types.message import MessageCute
9
+ from telegrinder.bot.dispatch.context import Context
10
+ from telegrinder.bot.dispatch.handler.abc import ABCHandler
11
+ from telegrinder.bot.dispatch.process import check_rule
12
+ from telegrinder.bot.rules.abc import ABCRule
13
+ from telegrinder.modules import logger
14
+ from telegrinder.types.objects import Update
15
+
16
+ type APIMethod = typing.Callable[
17
+ typing.Concatenate[MessageCute, ...], typing.Awaitable[Result[typing.Any, APIError]]
18
+ ]
19
+
20
+
21
+ class BaseReplyHandler(ABCHandler[MessageCute], abc.ABC):
22
+ def __init__(
23
+ self,
24
+ *rules: ABCRule,
25
+ final: bool = True,
26
+ as_reply: bool = False,
27
+ preset_context: Context | None = None,
28
+ **default_params: typing.Any,
29
+ ) -> None:
30
+ self.rules = list(rules)
31
+ self.as_reply = as_reply
32
+ self.final = final
33
+ self.default_params = default_params
34
+ self.preset_context = preset_context or Context()
35
+
36
+ def __repr__(self) -> str:
37
+ return f"<{self.__class__.__qualname__}>"
38
+
39
+ async def check(self, api: API, event: Update, ctx: Context | None = None) -> bool:
40
+ ctx = Context(raw_update=event) if ctx is None else ctx
41
+ temp_ctx = ctx.copy()
42
+ temp_ctx |= self.preset_context
43
+
44
+ for rule in self.rules:
45
+ if not await check_rule(api, rule, event, ctx):
46
+ logger.debug("Rule {!r} failed!", rule)
47
+ return False
48
+
49
+ ctx |= temp_ctx
50
+ return True
51
+
52
+ @abc.abstractmethod
53
+ async def run(self, api: API, event: MessageCute, ctx: Context) -> typing.Any:
54
+ pass
55
+
56
+
57
+ __all__ = ("BaseReplyHandler",)
@@ -1,44 +1,44 @@
1
- import typing
2
-
3
- from telegrinder.api.api import API
4
- from telegrinder.bot.cute_types.message import MessageCute
5
- from telegrinder.bot.dispatch.context import Context
6
- from telegrinder.bot.dispatch.handler.base import BaseReplyHandler
7
- from telegrinder.bot.rules.abc import ABCRule
8
- from telegrinder.types.objects import InputFile
9
-
10
-
11
- class DocumentReplyHandler(BaseReplyHandler):
12
- def __init__(
13
- self,
14
- document: InputFile | str,
15
- *rules: ABCRule,
16
- caption: str | None = None,
17
- parse_mode: str | None = None,
18
- is_blocking: bool = True,
19
- as_reply: bool = False,
20
- preset_context: Context | None = None,
21
- **default_params: typing.Any,
22
- ) -> None:
23
- self.document = document
24
- self.parse_mode = parse_mode
25
- self.caption = caption
26
- super().__init__(
27
- *rules,
28
- is_blocking=is_blocking,
29
- as_reply=as_reply,
30
- preset_context=preset_context,
31
- **default_params,
32
- )
33
-
34
- async def run(self, _: API, event: MessageCute, __: Context) -> typing.Any:
35
- method = event.answer_document if not self.as_reply else event.reply_document
36
- await method(
37
- document=self.document,
38
- parse_mode=self.parse_mode,
39
- caption=self.caption,
40
- **self.default_params,
41
- )
42
-
43
-
44
- __all__ = ("DocumentReplyHandler",)
1
+ import typing
2
+
3
+ from telegrinder.api.api import API
4
+ from telegrinder.bot.cute_types.message import MessageCute
5
+ from telegrinder.bot.dispatch.context import Context
6
+ from telegrinder.bot.dispatch.handler.base import BaseReplyHandler
7
+ from telegrinder.bot.rules.abc import ABCRule
8
+ from telegrinder.types.objects import InputFile
9
+
10
+
11
+ class DocumentReplyHandler(BaseReplyHandler):
12
+ def __init__(
13
+ self,
14
+ document: InputFile | str,
15
+ *rules: ABCRule,
16
+ caption: str | None = None,
17
+ parse_mode: str | None = None,
18
+ final: bool = True,
19
+ as_reply: bool = False,
20
+ preset_context: Context | None = None,
21
+ **default_params: typing.Any,
22
+ ) -> None:
23
+ self.document = document
24
+ self.parse_mode = parse_mode
25
+ self.caption = caption
26
+ super().__init__(
27
+ *rules,
28
+ final=final,
29
+ as_reply=as_reply,
30
+ preset_context=preset_context,
31
+ **default_params,
32
+ )
33
+
34
+ async def run(self, _: API, event: MessageCute, __: Context) -> typing.Any:
35
+ method = event.answer_document if not self.as_reply else event.reply_document
36
+ await method(
37
+ document=self.document,
38
+ parse_mode=self.parse_mode,
39
+ caption=self.caption,
40
+ **self.default_params,
41
+ )
42
+
43
+
44
+ __all__ = ("DocumentReplyHandler",)
@@ -1,135 +1,129 @@
1
- import dataclasses
2
- from functools import cached_property
3
-
4
- import typing_extensions as typing
5
-
6
- from telegrinder.api.api import API
7
- from telegrinder.bot.cute_types import BaseCute, UpdateCute
8
- from telegrinder.bot.dispatch.context import Context
9
- from telegrinder.bot.dispatch.process import check_rule
10
- from telegrinder.model import Model
11
- from telegrinder.modules import logger
12
- from telegrinder.node.base import Node, get_nodes
13
- from telegrinder.node.composer import NodeCollection, compose_nodes
14
- from telegrinder.node.event import EVENT_NODE_KEY
15
- from telegrinder.tools.error_handler import ABCErrorHandler, ErrorHandler
16
- from telegrinder.types.enums import UpdateType
17
- from telegrinder.types.objects import Update
18
-
19
- from .abc import ABCHandler
20
-
21
- if typing.TYPE_CHECKING:
22
- from telegrinder.bot.rules.abc import ABCRule
23
- from telegrinder.node.composer import NodeCollection
24
-
25
- Rest = typing.ParamSpec("Rest")
26
- Result = typing.TypeVar("Result")
27
- Function = typing.TypeVar("Function", bound="Func[..., typing.Any]")
28
- Event = typing.TypeVar("Event", bound=Model)
29
- ErrorHandlerT = typing.TypeVar("ErrorHandlerT", bound=ABCErrorHandler, default=ErrorHandler)
30
-
31
- Func: typing.TypeAlias = typing.Callable[Rest, typing.Coroutine[typing.Any, typing.Any, Result]]
32
-
33
-
34
- @dataclasses.dataclass(repr=False, slots=True)
35
- class FuncHandler(ABCHandler[Event], typing.Generic[Event, Function, ErrorHandlerT]):
36
- function: Function
37
- rules: list["ABCRule"]
38
- is_blocking: bool = dataclasses.field(default=True, kw_only=True)
39
- dataclass: type[typing.Any] | None = dataclasses.field(default=dict, kw_only=True)
40
- error_handler: ErrorHandlerT = dataclasses.field(
41
- default_factory=lambda: typing.cast(ErrorHandlerT, ErrorHandler()),
42
- kw_only=True,
43
- )
44
- preset_context: Context = dataclasses.field(default_factory=lambda: Context(), kw_only=True)
45
- update_type: UpdateType | None = dataclasses.field(default=None, kw_only=True)
46
-
47
- def __post_init__(self) -> None:
48
- self.dataclass = typing.get_origin(self.dataclass) or self.dataclass
49
-
50
- @property
51
- def __call__(self) -> Function:
52
- return self.function
53
-
54
- def __repr__(self) -> str:
55
- return "<{}: {}={!r} with rules={!r}, dataclass={!r}, error_handler={!r}>".format(
56
- self.__class__.__name__,
57
- "blocking function" if self.is_blocking else "function",
58
- self.function.__qualname__,
59
- self.rules,
60
- self.dataclass,
61
- self.error_handler,
62
- )
63
-
64
- @cached_property
65
- def required_nodes(self) -> dict[str, type[Node]]:
66
- return get_nodes(self.function)
67
-
68
- async def check(self, api: API, event: Update, ctx: Context | None = None) -> bool:
69
- if self.update_type is not None and self.update_type != event.update_type:
70
- return False
71
-
72
- logger.debug("Checking handler {!r}...", self)
73
- ctx = Context(raw_update=event) if ctx is None else ctx
74
- temp_ctx = ctx.copy()
75
- temp_ctx |= self.preset_context
76
- update = event
77
-
78
- for rule in self.rules:
79
- if not await check_rule(api, rule, update, temp_ctx):
80
- logger.debug("Rule {!r} failed!", rule)
81
- return False
82
-
83
- nodes = self.required_nodes
84
- node_col = None
85
- if nodes:
86
- result = await compose_nodes(nodes, ctx, data={Update: event, API: api})
87
- if not result:
88
- logger.debug(f"Cannot compose nodes for handler. Error: {result.error!r}")
89
- return False
90
-
91
- node_col = result.value
92
- temp_ctx |= node_col.values
93
-
94
- if EVENT_NODE_KEY in ctx:
95
- for name, node in nodes.items():
96
- if node is ctx[EVENT_NODE_KEY] and name in temp_ctx:
97
- ctx[name] = temp_ctx.pop(name)
98
-
99
- logger.debug("All checks passed for handler.")
100
-
101
- temp_ctx["node_col"] = node_col
102
- ctx |= temp_ctx
103
- return True
104
-
105
- async def run(
106
- self,
107
- api: API,
108
- event: Event,
109
- ctx: Context,
110
- node_col: "NodeCollection | None" = None,
111
- ) -> typing.Any:
112
- logger.debug(f"Running func handler {self.function.__qualname__!r}")
113
-
114
- if self.dataclass is not None and EVENT_NODE_KEY not in ctx:
115
- if self.update_type is not None and isinstance(event, Update):
116
- update = getattr(event, event.update_type.value).unwrap()
117
- event = (
118
- self.dataclass.from_update(update, bound_api=api)
119
- if issubclass(self.dataclass, BaseCute)
120
- else self.dataclass(**update.to_dict()) # type: ignore
121
- )
122
-
123
- elif issubclass(self.dataclass, UpdateCute) and isinstance(event, Update):
124
- event = self.dataclass.from_update(event, bound_api=api)
125
-
126
- else:
127
- event = self.dataclass(**event.to_dict()) # type: ignore
128
-
129
- result = (await self.error_handler.run(self.function, event, api, ctx)).unwrap()
130
- if node_col := ctx.node_col:
131
- await node_col.close_all()
132
- return result
133
-
134
-
135
- __all__ = ("FuncHandler",)
1
+ import dataclasses
2
+ from functools import cached_property
3
+
4
+ import typing_extensions as typing
5
+
6
+ from telegrinder.api.api import API
7
+ from telegrinder.bot.dispatch.context import Context
8
+ from telegrinder.bot.dispatch.process import check_rule
9
+ from telegrinder.modules import logger
10
+ from telegrinder.node.base import NodeType, get_nodes
11
+ from telegrinder.node.composer import NodeCollection, compose_nodes
12
+ from telegrinder.tools.adapter.abc import ABCAdapter
13
+ from telegrinder.tools.adapter.dataclass import DataclassAdapter
14
+ from telegrinder.tools.error_handler import ABCErrorHandler, ErrorHandler
15
+ from telegrinder.tools.magic import get_annotations, magic_bundle
16
+ from telegrinder.types.enums import UpdateType
17
+ from telegrinder.types.objects import Update
18
+
19
+ from .abc import ABCHandler
20
+
21
+ if typing.TYPE_CHECKING:
22
+ from telegrinder.bot.rules.abc import ABCRule
23
+ from telegrinder.node.composer import NodeCollection
24
+
25
+ Function = typing.TypeVar("Function", bound="Func[..., typing.Any]")
26
+ Event = typing.TypeVar("Event")
27
+ ErrorHandlerT = typing.TypeVar("ErrorHandlerT", bound=ABCErrorHandler, default=ErrorHandler)
28
+
29
+ type Func[**Rest, Result] = typing.Callable[Rest, typing.Coroutine[typing.Any, typing.Any, Result]]
30
+
31
+
32
+ @dataclasses.dataclass(repr=False, slots=True)
33
+ class FuncHandler(ABCHandler[Event], typing.Generic[Event, Function, ErrorHandlerT]):
34
+ function: Function
35
+ rules: list["ABCRule"]
36
+ adapter: ABCAdapter[Update, Event] | None = dataclasses.field(default=None, kw_only=True)
37
+ final: bool = dataclasses.field(default=True, kw_only=True)
38
+ dataclass: type[typing.Any] | None = dataclasses.field(default=dict[str, typing.Any], kw_only=True)
39
+ error_handler: ErrorHandlerT = dataclasses.field(
40
+ default_factory=lambda: typing.cast(ErrorHandlerT, ErrorHandler()),
41
+ kw_only=True,
42
+ )
43
+ preset_context: Context = dataclasses.field(default_factory=lambda: Context(), kw_only=True)
44
+ update_type: UpdateType | None = dataclasses.field(default=None, kw_only=True)
45
+
46
+ def __post_init__(self) -> None:
47
+ self.dataclass = typing.get_origin(self.dataclass) or self.dataclass
48
+
49
+ if self.dataclass is not None and self.adapter is None:
50
+ self.adapter = DataclassAdapter(self.dataclass, self.update_type)
51
+
52
+ @property
53
+ def __call__(self) -> Function:
54
+ return self.function
55
+
56
+ def __repr__(self) -> str:
57
+ return "<{}: {}={!r} with rules={!r}, dataclass={!r}, error_handler={!r}>".format(
58
+ self.__class__.__name__,
59
+ "final function" if self.final else "function",
60
+ self.function.__qualname__,
61
+ self.rules,
62
+ self.dataclass,
63
+ self.error_handler,
64
+ )
65
+
66
+ @cached_property
67
+ def required_nodes(self) -> dict[str, type[NodeType]]:
68
+ return get_nodes(self.function)
69
+
70
+ def get_name_event_param(self, event: Event) -> str | None:
71
+ event_class = self.dataclass or event.__class__
72
+ for k, v in get_annotations(self.function).items():
73
+ if isinstance(v := typing.get_origin(v) or v, type) and v is event_class:
74
+ self.func_event_param = k
75
+ return k
76
+ return None
77
+
78
+ async def check(self, api: API, event: Update, ctx: Context | None = None) -> bool:
79
+ if self.update_type is not None and self.update_type != event.update_type:
80
+ return False
81
+
82
+ logger.debug("Checking handler {!r}...", self)
83
+ ctx = Context(raw_update=event) if ctx is None else ctx
84
+ temp_ctx = ctx.copy()
85
+ temp_ctx |= self.preset_context.copy()
86
+ update = event
87
+
88
+ for rule in self.rules:
89
+ if not await check_rule(api, rule, update, temp_ctx):
90
+ logger.debug("Rule {!r} failed!", rule)
91
+ return False
92
+
93
+ nodes = self.required_nodes
94
+ node_col = None
95
+ if nodes:
96
+ result = await compose_nodes(nodes, ctx, data={Update: update, API: api})
97
+ if not result:
98
+ logger.debug(f"Cannot compose nodes for handler, error: {str(result.error)}")
99
+ return False
100
+
101
+ node_col = result.value
102
+ temp_ctx |= node_col.values
103
+
104
+ logger.debug("All checks passed for handler.")
105
+ temp_ctx["node_col"] = node_col
106
+ ctx |= temp_ctx
107
+ return True
108
+
109
+ async def run(
110
+ self,
111
+ api: API,
112
+ event: Event,
113
+ ctx: Context,
114
+ node_col: "NodeCollection | None" = None,
115
+ ) -> typing.Any:
116
+ logger.debug(f"Running handler {self!r}...")
117
+
118
+ try:
119
+ if event_param := self.get_name_event_param(event):
120
+ ctx = Context(**{event_param: event, **ctx})
121
+ return await self(**magic_bundle(self.function, ctx, start_idx=0))
122
+ except BaseException as exception:
123
+ return await self.error_handler.run(exception, event, api, ctx)
124
+ finally:
125
+ if node_col := ctx.node_col:
126
+ await node_col.close_all()
127
+
128
+
129
+ __all__ = ("FuncHandler",)
@@ -1,43 +1,44 @@
1
- import typing
2
-
3
- from telegrinder.api.api import API
4
- from telegrinder.bot.cute_types.message import InputMediaType, MessageCute
5
- from telegrinder.bot.dispatch.context import Context
6
- from telegrinder.bot.dispatch.handler.base import BaseReplyHandler
7
- from telegrinder.bot.rules.abc import ABCRule
8
-
9
-
10
- class MediaGroupReplyHandler(BaseReplyHandler):
11
- def __init__(
12
- self,
13
- media: InputMediaType | list[InputMediaType],
14
- *rules: ABCRule,
15
- caption: str | list[str] | None = None,
16
- parse_mode: str | list[str] | None = None,
17
- is_blocking: bool = True,
18
- as_reply: bool = False,
19
- preset_context: Context | None = None,
20
- **default_params: typing.Any,
21
- ) -> None:
22
- self.media = media
23
- self.parse_mode = parse_mode
24
- self.caption = caption
25
- super().__init__(
26
- *rules,
27
- is_blocking=is_blocking,
28
- as_reply=as_reply,
29
- preset_context=preset_context,
30
- **default_params,
31
- )
32
-
33
- async def run(self, _: API, event: MessageCute, __: Context) -> typing.Any:
34
- method = event.answer_media_group if not self.as_reply else event.reply_media_group
35
- await method(
36
- media=self.media,
37
- parse_mode=self.parse_mode,
38
- caption=self.caption,
39
- **self.default_params,
40
- )
41
-
42
-
43
- __all__ = ("MediaGroupReplyHandler",)
1
+ import typing
2
+
3
+ from telegrinder.api.api import API
4
+ from telegrinder.bot.cute_types.message import MessageCute
5
+ from telegrinder.bot.dispatch.context import Context
6
+ from telegrinder.bot.dispatch.handler.base import BaseReplyHandler
7
+ from telegrinder.bot.rules.abc import ABCRule
8
+ from telegrinder.types.objects import InputMedia
9
+
10
+
11
+ class MediaGroupReplyHandler(BaseReplyHandler):
12
+ def __init__(
13
+ self,
14
+ media: InputMedia | list[InputMedia],
15
+ *rules: ABCRule,
16
+ caption: str | list[str] | None = None,
17
+ parse_mode: str | list[str] | None = None,
18
+ final: bool = True,
19
+ as_reply: bool = False,
20
+ preset_context: Context | None = None,
21
+ **default_params: typing.Any,
22
+ ) -> None:
23
+ self.media = media
24
+ self.parse_mode = parse_mode
25
+ self.caption = caption
26
+ super().__init__(
27
+ *rules,
28
+ final=final,
29
+ as_reply=as_reply,
30
+ preset_context=preset_context,
31
+ **default_params,
32
+ )
33
+
34
+ async def run(self, _: API, event: MessageCute, __: Context) -> typing.Any:
35
+ method = event.answer_media_group if not self.as_reply else event.reply_media_group
36
+ await method(
37
+ media=self.media,
38
+ parse_mode=self.parse_mode,
39
+ caption=self.caption,
40
+ **self.default_params,
41
+ )
42
+
43
+
44
+ __all__ = ("MediaGroupReplyHandler",)
@@ -1,36 +1,36 @@
1
- import typing
2
-
3
- from telegrinder.api.api import API
4
- from telegrinder.bot.cute_types.message import MessageCute
5
- from telegrinder.bot.dispatch.context import Context
6
- from telegrinder.bot.dispatch.handler.base import BaseReplyHandler
7
- from telegrinder.bot.rules.abc import ABCRule
8
-
9
-
10
- class MessageReplyHandler(BaseReplyHandler):
11
- def __init__(
12
- self,
13
- text: str,
14
- *rules: ABCRule,
15
- parse_mode: str | None = None,
16
- is_blocking: bool = True,
17
- as_reply: bool = False,
18
- preset_context: Context | None = None,
19
- **default_params: typing.Any,
20
- ) -> None:
21
- self.text = text
22
- self.parse_mode = parse_mode
23
- super().__init__(
24
- *rules,
25
- is_blocking=is_blocking,
26
- as_reply=as_reply,
27
- preset_context=preset_context,
28
- **default_params,
29
- )
30
-
31
- async def run(self, _: API, event: MessageCute, __: Context) -> typing.Any:
32
- method = event.answer if not self.as_reply else event.reply
33
- await method(text=self.text, parse_mode=self.parse_mode, **self.default_params)
34
-
35
-
36
- __all__ = ("MessageReplyHandler",)
1
+ import typing
2
+
3
+ from telegrinder.api.api import API
4
+ from telegrinder.bot.cute_types.message import MessageCute
5
+ from telegrinder.bot.dispatch.context import Context
6
+ from telegrinder.bot.dispatch.handler.base import BaseReplyHandler
7
+ from telegrinder.bot.rules.abc import ABCRule
8
+
9
+
10
+ class MessageReplyHandler(BaseReplyHandler):
11
+ def __init__(
12
+ self,
13
+ text: str,
14
+ *rules: ABCRule,
15
+ parse_mode: str | None = None,
16
+ final: bool = True,
17
+ as_reply: bool = False,
18
+ preset_context: Context | None = None,
19
+ **default_params: typing.Any,
20
+ ) -> None:
21
+ self.text = text
22
+ self.parse_mode = parse_mode
23
+ super().__init__(
24
+ *rules,
25
+ final=final,
26
+ as_reply=as_reply,
27
+ preset_context=preset_context,
28
+ **default_params,
29
+ )
30
+
31
+ async def run(self, _: API, event: MessageCute, __: Context) -> typing.Any:
32
+ method = event.answer if not self.as_reply else event.reply
33
+ await method(text=self.text, parse_mode=self.parse_mode, **self.default_params)
34
+
35
+
36
+ __all__ = ("MessageReplyHandler",)