telegrinder 0.1.dev170__py3-none-any.whl → 0.2.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 (116) hide show
  1. telegrinder/__init__.py +2 -2
  2. telegrinder/api/__init__.py +1 -2
  3. telegrinder/api/api.py +15 -6
  4. telegrinder/api/error.py +2 -1
  5. telegrinder/api/token.py +36 -0
  6. telegrinder/bot/__init__.py +12 -6
  7. telegrinder/bot/bot.py +18 -6
  8. telegrinder/bot/cute_types/__init__.py +7 -7
  9. telegrinder/bot/cute_types/base.py +122 -20
  10. telegrinder/bot/cute_types/callback_query.py +10 -6
  11. telegrinder/bot/cute_types/chat_join_request.py +4 -5
  12. telegrinder/bot/cute_types/chat_member_updated.py +4 -6
  13. telegrinder/bot/cute_types/inline_query.py +3 -4
  14. telegrinder/bot/cute_types/message.py +32 -21
  15. telegrinder/bot/cute_types/update.py +51 -4
  16. telegrinder/bot/cute_types/utils.py +3 -466
  17. telegrinder/bot/dispatch/__init__.py +10 -11
  18. telegrinder/bot/dispatch/abc.py +8 -5
  19. telegrinder/bot/dispatch/context.py +17 -8
  20. telegrinder/bot/dispatch/dispatch.py +71 -48
  21. telegrinder/bot/dispatch/handler/__init__.py +3 -3
  22. telegrinder/bot/dispatch/handler/abc.py +4 -4
  23. telegrinder/bot/dispatch/handler/func.py +46 -22
  24. telegrinder/bot/dispatch/handler/message_reply.py +6 -7
  25. telegrinder/bot/dispatch/middleware/__init__.py +1 -1
  26. telegrinder/bot/dispatch/middleware/abc.py +2 -2
  27. telegrinder/bot/dispatch/process.py +38 -19
  28. telegrinder/bot/dispatch/return_manager/__init__.py +4 -4
  29. telegrinder/bot/dispatch/return_manager/abc.py +3 -3
  30. telegrinder/bot/dispatch/return_manager/callback_query.py +1 -2
  31. telegrinder/bot/dispatch/return_manager/inline_query.py +1 -2
  32. telegrinder/bot/dispatch/return_manager/message.py +1 -2
  33. telegrinder/bot/dispatch/view/__init__.py +8 -8
  34. telegrinder/bot/dispatch/view/abc.py +18 -16
  35. telegrinder/bot/dispatch/view/box.py +75 -64
  36. telegrinder/bot/dispatch/view/callback_query.py +1 -2
  37. telegrinder/bot/dispatch/view/chat_join_request.py +1 -2
  38. telegrinder/bot/dispatch/view/chat_member.py +16 -2
  39. telegrinder/bot/dispatch/view/inline_query.py +1 -2
  40. telegrinder/bot/dispatch/view/message.py +12 -5
  41. telegrinder/bot/dispatch/view/raw.py +9 -8
  42. telegrinder/bot/dispatch/waiter_machine/__init__.py +3 -3
  43. telegrinder/bot/dispatch/waiter_machine/machine.py +12 -8
  44. telegrinder/bot/dispatch/waiter_machine/middleware.py +1 -1
  45. telegrinder/bot/dispatch/waiter_machine/short_state.py +4 -3
  46. telegrinder/bot/polling/abc.py +1 -1
  47. telegrinder/bot/polling/polling.py +6 -6
  48. telegrinder/bot/rules/__init__.py +20 -20
  49. telegrinder/bot/rules/abc.py +57 -43
  50. telegrinder/bot/rules/adapter/__init__.py +5 -5
  51. telegrinder/bot/rules/adapter/abc.py +6 -3
  52. telegrinder/bot/rules/adapter/errors.py +2 -1
  53. telegrinder/bot/rules/adapter/event.py +28 -13
  54. telegrinder/bot/rules/adapter/node.py +28 -22
  55. telegrinder/bot/rules/adapter/raw_update.py +13 -5
  56. telegrinder/bot/rules/callback_data.py +4 -4
  57. telegrinder/bot/rules/chat_join.py +4 -4
  58. telegrinder/bot/rules/command.py +5 -7
  59. telegrinder/bot/rules/func.py +2 -2
  60. telegrinder/bot/rules/fuzzy.py +1 -1
  61. telegrinder/bot/rules/inline.py +3 -3
  62. telegrinder/bot/rules/integer.py +1 -2
  63. telegrinder/bot/rules/markup.py +5 -3
  64. telegrinder/bot/rules/message_entities.py +2 -2
  65. telegrinder/bot/rules/node.py +2 -2
  66. telegrinder/bot/rules/regex.py +1 -1
  67. telegrinder/bot/rules/rule_enum.py +1 -1
  68. telegrinder/bot/rules/text.py +1 -2
  69. telegrinder/bot/rules/update.py +1 -2
  70. telegrinder/bot/scenario/abc.py +2 -2
  71. telegrinder/bot/scenario/checkbox.py +3 -4
  72. telegrinder/bot/scenario/choice.py +1 -2
  73. telegrinder/model.py +89 -45
  74. telegrinder/modules.py +3 -3
  75. telegrinder/msgspec_utils.py +85 -57
  76. telegrinder/node/__init__.py +17 -10
  77. telegrinder/node/attachment.py +19 -16
  78. telegrinder/node/base.py +46 -22
  79. telegrinder/node/callback_query.py +5 -9
  80. telegrinder/node/command.py +6 -2
  81. telegrinder/node/composer.py +102 -77
  82. telegrinder/node/container.py +3 -3
  83. telegrinder/node/event.py +68 -0
  84. telegrinder/node/me.py +3 -0
  85. telegrinder/node/message.py +6 -10
  86. telegrinder/node/polymorphic.py +15 -10
  87. telegrinder/node/rule.py +20 -6
  88. telegrinder/node/scope.py +9 -1
  89. telegrinder/node/source.py +21 -11
  90. telegrinder/node/text.py +4 -4
  91. telegrinder/node/update.py +7 -4
  92. telegrinder/py.typed +0 -0
  93. telegrinder/rules.py +59 -0
  94. telegrinder/tools/__init__.py +2 -2
  95. telegrinder/tools/buttons.py +5 -10
  96. telegrinder/tools/error_handler/abc.py +2 -2
  97. telegrinder/tools/error_handler/error.py +2 -0
  98. telegrinder/tools/error_handler/error_handler.py +6 -6
  99. telegrinder/tools/formatting/spec_html_formats.py +10 -10
  100. telegrinder/tools/global_context/__init__.py +2 -2
  101. telegrinder/tools/global_context/global_context.py +3 -3
  102. telegrinder/tools/global_context/telegrinder_ctx.py +4 -4
  103. telegrinder/tools/keyboard.py +3 -3
  104. telegrinder/tools/loop_wrapper/loop_wrapper.py +47 -13
  105. telegrinder/tools/magic.py +96 -18
  106. telegrinder/types/__init__.py +1 -0
  107. telegrinder/types/enums.py +2 -0
  108. telegrinder/types/methods.py +91 -15
  109. telegrinder/types/objects.py +49 -24
  110. telegrinder/verification_utils.py +1 -3
  111. {telegrinder-0.1.dev170.dist-info → telegrinder-0.2.0.dist-info}/METADATA +2 -2
  112. telegrinder-0.2.0.dist-info/RECORD +145 -0
  113. telegrinder/api/abc.py +0 -73
  114. telegrinder-0.1.dev170.dist-info/RECORD +0 -143
  115. {telegrinder-0.1.dev170.dist-info → telegrinder-0.2.0.dist-info}/LICENSE +0 -0
  116. {telegrinder-0.1.dev170.dist-info → telegrinder-0.2.0.dist-info}/WHEEL +0 -0
@@ -1,10 +1,10 @@
1
- from .abc import ABCDispatch
2
- from .context import Context
3
- from .dispatch import ABCRule, Dispatch, TelegrinderCtx
4
- from .handler import ABCHandler, FuncHandler, MessageReplyHandler
5
- from .middleware import ABCMiddleware
6
- from .process import check_rule, process_inner
7
- from .return_manager import (
1
+ from telegrinder.bot.dispatch.abc import ABCDispatch
2
+ from telegrinder.bot.dispatch.context import Context
3
+ from telegrinder.bot.dispatch.dispatch import Dispatch, TelegrinderContext
4
+ from telegrinder.bot.dispatch.handler import ABCHandler, FuncHandler, MessageReplyHandler
5
+ from telegrinder.bot.dispatch.middleware import ABCMiddleware
6
+ from telegrinder.bot.dispatch.process import check_rule, process_inner
7
+ from telegrinder.bot.dispatch.return_manager import (
8
8
  ABCReturnManager,
9
9
  BaseReturnManager,
10
10
  CallbackQueryReturnManager,
@@ -13,7 +13,7 @@ from .return_manager import (
13
13
  MessageReturnManager,
14
14
  register_manager,
15
15
  )
16
- from .view import (
16
+ from telegrinder.bot.dispatch.view import (
17
17
  ABCStateView,
18
18
  ABCView,
19
19
  BaseStateView,
@@ -26,14 +26,13 @@ from .view import (
26
26
  RawEventView,
27
27
  ViewBox,
28
28
  )
29
- from .waiter_machine import ShortState, WaiterMachine, clear_wm_storage_worker
29
+ from telegrinder.bot.dispatch.waiter_machine import ShortState, WaiterMachine, clear_wm_storage_worker
30
30
 
31
31
  __all__ = (
32
32
  "ABCDispatch",
33
33
  "ABCHandler",
34
34
  "ABCMiddleware",
35
35
  "ABCReturnManager",
36
- "ABCRule",
37
36
  "ABCStateView",
38
37
  "ABCView",
39
38
  "BaseReturnManager",
@@ -54,7 +53,7 @@ __all__ = (
54
53
  "MessageView",
55
54
  "RawEventView",
56
55
  "ShortState",
57
- "TelegrinderCtx",
56
+ "TelegrinderContext",
58
57
  "ViewBox",
59
58
  "WaiterMachine",
60
59
  "check_rule",
@@ -1,16 +1,19 @@
1
1
  import typing
2
2
  from abc import ABC, abstractmethod
3
3
 
4
- from telegrinder.api.abc import ABCAPI
5
- from telegrinder.tools.global_context import ABCGlobalContext
6
- from telegrinder.types import Update
4
+ from telegrinder.api import API
5
+ from telegrinder.tools.global_context.abc import ABCGlobalContext
6
+ from telegrinder.types.objects import Update
7
7
 
8
8
 
9
9
  class ABCDispatch(ABC):
10
- global_context: ABCGlobalContext
10
+ @property
11
+ @abstractmethod
12
+ def global_context(self) -> ABCGlobalContext:
13
+ pass
11
14
 
12
15
  @abstractmethod
13
- async def feed(self, event: Update, api: ABCAPI) -> bool:
16
+ async def feed(self, event: Update, api: API) -> bool:
14
17
  pass
15
18
 
16
19
  @abstractmethod
@@ -10,15 +10,15 @@ Key: typing.TypeAlias = str | enum.Enum
10
10
  AnyValue: typing.TypeAlias = typing.Any
11
11
 
12
12
 
13
- @typing.dataclass_transform(kw_only_default=True, order_default=True)
14
13
  class Context(dict[str, AnyValue]):
15
- """Context class for rules and middlewares.
16
- ```
17
- class MyRule(ABCRule[T]):
18
- adapter = RawUpdateAdapter()
14
+ """Context class like dict & dotdict.
19
15
 
16
+ For example:
17
+ ```python
18
+ class MyRule(ABCRule[T]):
20
19
  async def check(self, event: T, ctx: Context) -> bool:
21
- ctx.set("value", (await event.ctx_api.get_me()).unwrap())
20
+ ctx.me = (await event.ctx_api.get_me()).unwrap()
21
+ ctx["items"] = [1, 2, 3]
22
22
  return True
23
23
  ```
24
24
  """
@@ -68,13 +68,22 @@ class Context(dict[str, AnyValue]):
68
68
  def set(self, key: Key, value: AnyValue) -> None:
69
69
  self[key] = value
70
70
 
71
- def get(self, key: Key, default: T | None = None) -> T | AnyValue:
71
+ @typing.overload
72
+ def get(self, key: Key) -> AnyValue | None: ...
73
+
74
+ @typing.overload
75
+ def get(self, key: Key, default: T) -> T | AnyValue: ...
76
+
77
+ @typing.overload
78
+ def get(self, key: Key, default: None = None) -> AnyValue | None: ...
79
+
80
+ def get(self, key: Key, default: T | None = None) -> T | AnyValue | None:
72
81
  return dict.get(self, key, default)
73
82
 
74
83
  def get_or_set(self, key: Key, default: T) -> T:
75
84
  if key not in self:
76
85
  self.set(key, default)
77
- return self.get(key)
86
+ return self.get(key, default)
78
87
 
79
88
  def delete(self, key: Key) -> None:
80
89
  del self[key]
@@ -1,23 +1,14 @@
1
- import asyncio
2
1
  import dataclasses
3
2
  import typing
4
3
 
5
4
  from vbml.patcher import Patcher
6
5
 
7
- from telegrinder.api.abc import ABCAPI
6
+ from telegrinder.api import API
8
7
  from telegrinder.bot.cute_types.base import BaseCute
9
8
  from telegrinder.bot.cute_types.update import UpdateCute
10
- from telegrinder.bot.dispatch.context import Context
11
- from telegrinder.bot.rules import ABCRule
12
- from telegrinder.modules import logger
13
- from telegrinder.tools.error_handler.error_handler import ErrorHandler
14
- from telegrinder.tools.global_context import TelegrinderCtx
15
- from telegrinder.types import Update
16
-
17
- from .abc import ABCDispatch
18
- from .handler import ABCHandler, FuncHandler
19
- from .handler.func import ErrorHandlerT
20
- from .view.box import (
9
+ from telegrinder.bot.dispatch.abc import ABCDispatch
10
+ from telegrinder.bot.dispatch.handler.func import ErrorHandlerT, FuncHandler
11
+ from telegrinder.bot.dispatch.view.box import (
21
12
  CallbackQueryView,
22
13
  ChatJoinRequestView,
23
14
  ChatMemberView,
@@ -26,10 +17,16 @@ from .view.box import (
26
17
  RawEventView,
27
18
  ViewBox,
28
19
  )
20
+ from telegrinder.modules import logger
21
+ from telegrinder.tools.error_handler.error_handler import ErrorHandler
22
+ from telegrinder.tools.global_context import TelegrinderContext
23
+ from telegrinder.types.enums import UpdateType
24
+ from telegrinder.types.objects import Update
25
+
26
+ if typing.TYPE_CHECKING:
27
+ from telegrinder.bot.rules.abc import ABCRule
29
28
 
30
29
  T = typing.TypeVar("T")
31
- R = typing.TypeVar("R")
32
- P = typing.ParamSpec("P")
33
30
  Handler = typing.Callable[typing.Concatenate[T, ...], typing.Coroutine[typing.Any, typing.Any, typing.Any]]
34
31
  Event = typing.TypeVar("Event", bound=BaseCute)
35
32
 
@@ -48,18 +45,18 @@ class Dispatch(
48
45
  RawEventView,
49
46
  ],
50
47
  ):
51
- global_context: TelegrinderCtx = dataclasses.field(
52
- init=False,
53
- default_factory=lambda: TelegrinderCtx(),
54
- )
55
- default_handlers: list[ABCHandler] = dataclasses.field(
48
+ _global_context: TelegrinderContext = dataclasses.field(
56
49
  init=False,
57
- default_factory=lambda: [],
50
+ default_factory=lambda: TelegrinderContext(),
58
51
  )
59
52
 
60
53
  def __repr__(self) -> str:
61
54
  return "Dispatch(%s)" % ", ".join(f"{k}={v!r}" for k, v in self.__dict__.items())
62
55
 
56
+ @property
57
+ def global_context(self) -> TelegrinderContext:
58
+ return self._global_context
59
+
63
60
  @property
64
61
  def patcher(self) -> Patcher:
65
62
  """Alias `patcher` to get `vbml.Patcher` from the global context."""
@@ -69,28 +66,48 @@ class Dispatch(
69
66
  @typing.overload
70
67
  def handle(
71
68
  self,
72
- *rules: ABCRule,
69
+ *rules: "ABCRule",
70
+ is_blocking: bool = True,
71
+ ) -> typing.Callable[[Handler[T]], FuncHandler[UpdateCute, Handler[T], ErrorHandler]]: ...
72
+
73
+ @typing.overload
74
+ def handle(
75
+ self,
76
+ *rules: "ABCRule",
77
+ dataclass: type[T],
78
+ is_blocking: bool = True,
73
79
  ) -> typing.Callable[[Handler[T]], FuncHandler[UpdateCute, Handler[T], ErrorHandler]]: ...
74
80
 
75
81
  @typing.overload
76
82
  def handle(
77
83
  self,
78
- *rules: ABCRule,
84
+ *rules: "ABCRule",
85
+ update_type: UpdateType,
79
86
  is_blocking: bool = True,
80
87
  ) -> typing.Callable[[Handler[T]], FuncHandler[UpdateCute, Handler[T], ErrorHandler]]: ...
81
88
 
82
89
  @typing.overload
83
90
  def handle(
84
91
  self,
85
- *rules: ABCRule,
92
+ *rules: "ABCRule",
86
93
  dataclass: type[T],
94
+ update_type: UpdateType,
87
95
  is_blocking: bool = True,
88
96
  ) -> typing.Callable[[Handler[T]], FuncHandler[UpdateCute, Handler[T], ErrorHandler]]: ...
89
97
 
90
98
  @typing.overload
91
99
  def handle( # type: ignore
92
100
  self,
93
- *rules: ABCRule,
101
+ *rules: "ABCRule",
102
+ error_handler: ErrorHandlerT,
103
+ is_blocking: bool = True,
104
+ ) -> typing.Callable[[Handler[T]], FuncHandler[UpdateCute, Handler[T], ErrorHandlerT]]: ...
105
+
106
+ @typing.overload
107
+ def handle( # type: ignore
108
+ self,
109
+ *rules: "ABCRule",
110
+ update_type: UpdateType,
94
111
  error_handler: ErrorHandlerT,
95
112
  is_blocking: bool = True,
96
113
  ) -> typing.Callable[[Handler[T]], FuncHandler[UpdateCute, Handler[T], ErrorHandlerT]]: ...
@@ -98,7 +115,7 @@ class Dispatch(
98
115
  @typing.overload
99
116
  def handle(
100
117
  self,
101
- *rules: ABCRule,
118
+ *rules: "ABCRule",
102
119
  dataclass: type[T],
103
120
  error_handler: ErrorHandlerT,
104
121
  is_blocking: bool = True,
@@ -107,7 +124,18 @@ class Dispatch(
107
124
  @typing.overload
108
125
  def handle(
109
126
  self,
110
- *rules: ABCRule,
127
+ *rules: "ABCRule",
128
+ dataclass: type[T],
129
+ update_type: UpdateType,
130
+ error_handler: ErrorHandlerT,
131
+ is_blocking: bool = True,
132
+ ) -> typing.Callable[[Handler[T]], FuncHandler[UpdateCute, Handler[T], ErrorHandlerT]]: ...
133
+
134
+ @typing.overload
135
+ def handle(
136
+ self,
137
+ *rules: "ABCRule",
138
+ update_type: UpdateType | None = None,
111
139
  dataclass: type[T] = DEFAULT_DATACLASS,
112
140
  error_handler: typing.Literal[None] = None,
113
141
  is_blocking: bool = True,
@@ -115,7 +143,8 @@ class Dispatch(
115
143
 
116
144
  def handle( # type: ignore
117
145
  self,
118
- *rules: ABCRule,
146
+ *rules: "ABCRule",
147
+ update_type: UpdateType | None = None,
119
148
  dataclass: type[typing.Any] = DEFAULT_DATACLASS,
120
149
  error_handler: ErrorHandlerT | None = None,
121
150
  is_blocking: bool = True,
@@ -127,36 +156,30 @@ class Dispatch(
127
156
  is_blocking=is_blocking,
128
157
  dataclass=dataclass,
129
158
  error_handler=error_handler or ErrorHandler(),
159
+ update_type=update_type,
130
160
  )
131
- self.default_handlers.append(handler)
161
+ self.raw_event.handlers.append(handler)
132
162
  return handler
133
163
 
134
164
  return wrapper
135
165
 
136
- async def feed(self, event: Update, api: ABCAPI) -> bool:
137
- logger.debug("Processing update (update_id={})", event.update_id)
138
- await self.raw_event.process(event, api)
139
-
166
+ async def feed(self, event: Update, api: API) -> bool:
167
+ logger.debug(
168
+ "Processing update (update_id={}, update_type={!r})",
169
+ event.update_id,
170
+ event.update_type.name,
171
+ )
140
172
  for view in self.get_views().values():
141
173
  if await view.check(event):
142
174
  logger.debug(
143
- "Update (update_id={}) matched view {!r}.",
175
+ "Update (update_id={}, update_type={!r}) matched view {!r}.",
144
176
  event.update_id,
145
- view.__class__.__name__,
177
+ event.update_type.name,
178
+ view,
146
179
  )
147
- await view.process(event, api)
148
- return True
149
-
150
- ctx = Context()
151
- loop = asyncio.get_running_loop()
152
- found = False
153
- for handler in self.default_handlers:
154
- if await handler.check(api, event, ctx):
155
- found = True
156
- loop.create_task(handler.run(event, ctx))
157
- if handler.is_blocking:
158
- break
159
- return found
180
+ if await view.process(event, api):
181
+ return True
182
+ return False
160
183
 
161
184
  def load(self, external: typing.Self) -> None:
162
185
  view_external = external.get_views()
@@ -1,5 +1,5 @@
1
- from .abc import ABCHandler
2
- from .func import FuncHandler
3
- from .message_reply import MessageReplyHandler
1
+ from telegrinder.bot.dispatch.handler.abc import ABCHandler
2
+ from telegrinder.bot.dispatch.handler.func import FuncHandler
3
+ from telegrinder.bot.dispatch.handler.message_reply import MessageReplyHandler
4
4
 
5
5
  __all__ = ("ABCHandler", "FuncHandler", "MessageReplyHandler")
@@ -1,10 +1,10 @@
1
1
  import typing
2
2
  from abc import ABC, abstractmethod
3
3
 
4
- from telegrinder.api.abc import ABCAPI
4
+ from telegrinder.api import API
5
5
  from telegrinder.bot.dispatch.context import Context
6
6
  from telegrinder.model import Model
7
- from telegrinder.types import Update
7
+ from telegrinder.types.objects import Update
8
8
 
9
9
  T = typing.TypeVar("T", bound=Model)
10
10
 
@@ -13,11 +13,11 @@ class ABCHandler(ABC, typing.Generic[T]):
13
13
  is_blocking: bool
14
14
 
15
15
  @abstractmethod
16
- async def check(self, api: ABCAPI, event: Update, ctx: Context | None = None) -> bool:
16
+ async def check(self, api: API, event: Update, ctx: Context | None = None) -> bool:
17
17
  pass
18
18
 
19
19
  @abstractmethod
20
- async def run(self, event: T, ctx: Context) -> typing.Any:
20
+ async def run(self, api: API, event: T, ctx: Context) -> typing.Any:
21
21
  pass
22
22
 
23
23
 
@@ -1,17 +1,20 @@
1
1
  import dataclasses
2
+ from functools import cached_property
2
3
 
3
4
  import typing_extensions as typing
4
5
 
5
- from telegrinder.api.abc import ABCAPI
6
+ from telegrinder.api import API
6
7
  from telegrinder.bot.cute_types import BaseCute, UpdateCute
7
8
  from telegrinder.bot.dispatch.context import Context
8
9
  from telegrinder.bot.dispatch.process import check_rule
10
+ from telegrinder.model import Model
9
11
  from telegrinder.modules import logger
10
- from telegrinder.node import Node, compose_nodes, is_node
12
+ from telegrinder.node.base import Node, get_nodes
13
+ from telegrinder.node.composer import compose_nodes
14
+ from telegrinder.node.event import EVENT_NODE_KEY
11
15
  from telegrinder.tools.error_handler import ABCErrorHandler, ErrorHandler
12
- from telegrinder.tools.magic import get_annotations
13
- from telegrinder.types import Update
14
16
  from telegrinder.types.enums import UpdateType
17
+ from telegrinder.types.objects import Update
15
18
 
16
19
  from .abc import ABCHandler
17
20
 
@@ -22,11 +25,11 @@ F = typing.TypeVar(
22
25
  "F",
23
26
  bound=typing.Callable[typing.Concatenate[typing.Any, ...], typing.Awaitable[typing.Any]],
24
27
  )
25
- Event = typing.TypeVar("Event", bound=BaseCute)
28
+ Event = typing.TypeVar("Event", bound=Model)
26
29
  ErrorHandlerT = typing.TypeVar("ErrorHandlerT", bound=ABCErrorHandler, default=ErrorHandler)
27
30
 
28
31
 
29
- @dataclasses.dataclass(repr=False)
32
+ @dataclasses.dataclass(repr=False, slots=True)
30
33
  class FuncHandler(ABCHandler[Event], typing.Generic[Event, F, ErrorHandlerT]):
31
34
  func: F
32
35
  rules: list["ABCRule"]
@@ -49,48 +52,69 @@ class FuncHandler(ABCHandler[Event], typing.Generic[Event, F, ErrorHandlerT]):
49
52
  self.error_handler,
50
53
  )
51
54
 
52
- def get_required_nodes(self) -> dict[str, type[Node]]:
53
- return {k: v for k, v in get_annotations(self.func).items() if is_node(v)}
55
+ @cached_property
56
+ def required_nodes(self) -> dict[str, type[Node]]:
57
+ return get_nodes(self.func)
54
58
 
55
- async def check(self, api: ABCAPI, event: Update, ctx: Context | None = None) -> bool:
59
+ async def check(self, api: API, event: Update, ctx: Context | None = None) -> bool:
56
60
  if self.update_type is not None and self.update_type != event.update_type:
57
61
  return False
58
62
 
59
- ctx = ctx or Context()
63
+ logger.debug("Checking handler {!r}...", self)
64
+ ctx = Context(raw_update=event) if ctx is None else ctx
60
65
  temp_ctx = ctx.copy()
61
66
  temp_ctx |= self.preset_context
62
67
 
63
- nodes = self.get_required_nodes()
68
+ nodes = self.required_nodes
64
69
  node_col = None
70
+ update = event
65
71
 
66
72
  if nodes:
67
- node_col = await compose_nodes(nodes, UpdateCute.from_update(event, api), ctx)
68
- if node_col is None:
73
+ result = await compose_nodes(nodes, ctx, data={Update: event, API: api})
74
+ if not result:
75
+ logger.debug(f"Cannot compose nodes for handler. {result.error}")
69
76
  return False
70
- temp_ctx |= node_col.values()
77
+
78
+ node_col = result.value
79
+ temp_ctx |= node_col.values
80
+
81
+ if EVENT_NODE_KEY in ctx:
82
+ for name, node in nodes.items():
83
+ if node is ctx[EVENT_NODE_KEY] and name in temp_ctx:
84
+ ctx[EVENT_NODE_KEY] = temp_ctx.pop(name)
71
85
 
72
86
  for rule in self.rules:
73
- if not await check_rule(api, rule, event, temp_ctx):
87
+ if not await check_rule(api, rule, update, temp_ctx):
74
88
  logger.debug("Rule {!r} failed!", rule)
75
89
  return False
76
90
 
91
+ logger.debug("All checks passed for handler.")
92
+
77
93
  temp_ctx["node_col"] = node_col
78
94
  ctx |= temp_ctx
79
95
  return True
80
96
 
81
- async def run(self, event: Event, ctx: Context) -> typing.Any:
82
- api = event.api
97
+ async def run(self, api: API, event: Event, ctx: Context) -> typing.Any:
98
+ logger.debug(f"Running func handler {self.func}")
99
+ dataclass_type = typing.get_origin(self.dataclass) or self.dataclass
83
100
 
84
- if self.dataclass is not None:
85
- if self.update_type is not None:
101
+ if dataclass_type is Update and (event_node := ctx.pop(EVENT_NODE_KEY, None)) is not None:
102
+ event = event_node
103
+
104
+ elif dataclass_type is not None:
105
+ if self.update_type is not None and isinstance(event, Update):
86
106
  update = event.to_dict()[self.update_type.value].unwrap()
87
107
  event = (
88
108
  self.dataclass.from_update(update, bound_api=api) # type: ignore
89
- if issubclass(self.dataclass, BaseCute)
90
- else self.dataclass(**update.to_dict())
109
+ if issubclass(dataclass_type, BaseCute)
110
+ else self.dataclass(**update.to_dict()) # type: ignore
91
111
  )
112
+
113
+ elif issubclass(dataclass_type, UpdateCute) and isinstance(event, Update):
114
+ event = self.dataclass.from_update(event, bound_api=api) # type: ignore
115
+
92
116
  else:
93
- event = self.dataclass(**event.to_dict())
117
+ event = self.dataclass(**event.to_dict()) # type: ignore
94
118
 
95
119
  result = (await self.error_handler.run(self.func, event, api, ctx)).unwrap()
96
120
  if node_col := ctx.node_col:
@@ -1,15 +1,14 @@
1
1
  import typing
2
2
 
3
- from telegrinder.api.abc import ABCAPI
4
- from telegrinder.bot.cute_types import MessageCute
3
+ from telegrinder.api.api import API
4
+ from telegrinder.bot.cute_types.message import MessageCute
5
5
  from telegrinder.bot.dispatch.context import Context
6
+ from telegrinder.bot.dispatch.handler.abc import ABCHandler
6
7
  from telegrinder.bot.dispatch.process import check_rule
7
8
  from telegrinder.bot.rules.abc import ABCRule
8
9
  from telegrinder.modules import logger
9
10
  from telegrinder.types.objects import ReplyParameters, Update
10
11
 
11
- from .abc import ABCHandler
12
-
13
12
 
14
13
  class MessageReplyHandler(ABCHandler[MessageCute]):
15
14
  def __init__(
@@ -36,8 +35,8 @@ class MessageReplyHandler(ABCHandler[MessageCute]):
36
35
  self.text,
37
36
  )
38
37
 
39
- async def check(self, api: ABCAPI, event: Update, ctx: Context | None = None) -> bool:
40
- ctx = ctx or Context()
38
+ async def check(self, api: API, event: Update, ctx: Context | None = None) -> bool:
39
+ ctx = Context(raw_update=event) if ctx is None else ctx
41
40
  temp_ctx = ctx.copy()
42
41
  temp_ctx |= self.preset_context
43
42
 
@@ -49,7 +48,7 @@ class MessageReplyHandler(ABCHandler[MessageCute]):
49
48
  ctx |= temp_ctx
50
49
  return True
51
50
 
52
- async def run(self, event: MessageCute, _: Context) -> typing.Any:
51
+ async def run(self, _: API, event: MessageCute, __: Context) -> typing.Any:
53
52
  await event.answer(
54
53
  text=self.text,
55
54
  reply_parameters=ReplyParameters(event.message_id) if self.as_reply else None,
@@ -1,3 +1,3 @@
1
- from .abc import ABCMiddleware
1
+ from telegrinder.bot.dispatch.middleware.abc import ABCMiddleware
2
2
 
3
3
  __all__ = ("ABCMiddleware",)
@@ -1,10 +1,10 @@
1
1
  import typing
2
2
  from abc import ABC
3
3
 
4
- from telegrinder.bot.cute_types.base import BaseCute
5
4
  from telegrinder.bot.dispatch.context import Context
5
+ from telegrinder.model import Model
6
6
 
7
- Event = typing.TypeVar("Event", bound=BaseCute)
7
+ Event = typing.TypeVar("Event", bound=Model)
8
8
 
9
9
 
10
10
  class ABCMiddleware(ABC, typing.Generic[Event]):