telegrinder 0.1.dev169__py3-none-any.whl → 0.1.dev171__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 (79) hide show
  1. telegrinder/api/abc.py +7 -1
  2. telegrinder/api/api.py +12 -3
  3. telegrinder/api/error.py +2 -1
  4. telegrinder/bot/bot.py +6 -1
  5. telegrinder/bot/cute_types/base.py +144 -17
  6. telegrinder/bot/cute_types/callback_query.py +6 -1
  7. telegrinder/bot/cute_types/chat_member_updated.py +1 -2
  8. telegrinder/bot/cute_types/message.py +23 -11
  9. telegrinder/bot/cute_types/update.py +48 -0
  10. telegrinder/bot/cute_types/utils.py +2 -465
  11. telegrinder/bot/dispatch/__init__.py +2 -3
  12. telegrinder/bot/dispatch/abc.py +6 -3
  13. telegrinder/bot/dispatch/context.py +6 -6
  14. telegrinder/bot/dispatch/dispatch.py +61 -23
  15. telegrinder/bot/dispatch/handler/abc.py +2 -2
  16. telegrinder/bot/dispatch/handler/func.py +36 -17
  17. telegrinder/bot/dispatch/handler/message_reply.py +2 -2
  18. telegrinder/bot/dispatch/middleware/abc.py +2 -2
  19. telegrinder/bot/dispatch/process.py +10 -10
  20. telegrinder/bot/dispatch/return_manager/abc.py +3 -3
  21. telegrinder/bot/dispatch/view/abc.py +12 -15
  22. telegrinder/bot/dispatch/view/box.py +73 -62
  23. telegrinder/bot/dispatch/view/message.py +11 -3
  24. telegrinder/bot/dispatch/view/raw.py +3 -0
  25. telegrinder/bot/dispatch/waiter_machine/machine.py +2 -2
  26. telegrinder/bot/dispatch/waiter_machine/middleware.py +1 -1
  27. telegrinder/bot/dispatch/waiter_machine/short_state.py +2 -1
  28. telegrinder/bot/polling/polling.py +3 -3
  29. telegrinder/bot/rules/abc.py +11 -7
  30. telegrinder/bot/rules/adapter/event.py +7 -4
  31. telegrinder/bot/rules/adapter/node.py +1 -1
  32. telegrinder/bot/rules/command.py +5 -7
  33. telegrinder/bot/rules/func.py +1 -1
  34. telegrinder/bot/rules/fuzzy.py +1 -1
  35. telegrinder/bot/rules/integer.py +1 -2
  36. telegrinder/bot/rules/markup.py +3 -3
  37. telegrinder/bot/rules/message_entities.py +1 -1
  38. telegrinder/bot/rules/node.py +2 -2
  39. telegrinder/bot/rules/regex.py +1 -1
  40. telegrinder/bot/rules/rule_enum.py +1 -1
  41. telegrinder/bot/scenario/checkbox.py +2 -2
  42. telegrinder/model.py +87 -47
  43. telegrinder/modules.py +3 -3
  44. telegrinder/msgspec_utils.py +94 -13
  45. telegrinder/node/__init__.py +20 -11
  46. telegrinder/node/attachment.py +19 -16
  47. telegrinder/node/base.py +120 -24
  48. telegrinder/node/callback_query.py +5 -9
  49. telegrinder/node/command.py +6 -2
  50. telegrinder/node/composer.py +82 -54
  51. telegrinder/node/container.py +4 -4
  52. telegrinder/node/event.py +59 -0
  53. telegrinder/node/me.py +3 -0
  54. telegrinder/node/message.py +6 -10
  55. telegrinder/node/polymorphic.py +11 -12
  56. telegrinder/node/rule.py +27 -5
  57. telegrinder/node/source.py +10 -11
  58. telegrinder/node/text.py +4 -4
  59. telegrinder/node/update.py +1 -2
  60. telegrinder/py.typed +0 -0
  61. telegrinder/tools/__init__.py +2 -2
  62. telegrinder/tools/buttons.py +5 -10
  63. telegrinder/tools/error_handler/error.py +2 -0
  64. telegrinder/tools/error_handler/error_handler.py +1 -1
  65. telegrinder/tools/formatting/spec_html_formats.py +10 -10
  66. telegrinder/tools/global_context/__init__.py +2 -2
  67. telegrinder/tools/global_context/global_context.py +2 -2
  68. telegrinder/tools/global_context/telegrinder_ctx.py +4 -4
  69. telegrinder/tools/keyboard.py +2 -2
  70. telegrinder/tools/loop_wrapper/loop_wrapper.py +39 -5
  71. telegrinder/tools/magic.py +48 -15
  72. telegrinder/types/enums.py +1 -0
  73. telegrinder/types/methods.py +14 -5
  74. telegrinder/types/objects.py +3 -0
  75. {telegrinder-0.1.dev169.dist-info → telegrinder-0.1.dev171.dist-info}/METADATA +2 -2
  76. telegrinder-0.1.dev171.dist-info/RECORD +145 -0
  77. telegrinder-0.1.dev169.dist-info/RECORD +0 -143
  78. {telegrinder-0.1.dev169.dist-info → telegrinder-0.1.dev171.dist-info}/LICENSE +0 -0
  79. {telegrinder-0.1.dev169.dist-info → telegrinder-0.1.dev171.dist-info}/WHEEL +0 -0
@@ -8,15 +8,15 @@ from telegrinder.api.abc import ABCAPI
8
8
  from telegrinder.bot.cute_types.base import BaseCute
9
9
  from telegrinder.bot.cute_types.update import UpdateCute
10
10
  from telegrinder.bot.dispatch.context import Context
11
- from telegrinder.bot.rules import ABCRule
11
+ from telegrinder.bot.dispatch.handler.abc import ABCHandler
12
+ from telegrinder.bot.dispatch.handler.func import ErrorHandlerT, FuncHandler
12
13
  from telegrinder.modules import logger
13
14
  from telegrinder.tools.error_handler.error_handler import ErrorHandler
14
- from telegrinder.tools.global_context import TelegrinderCtx
15
- from telegrinder.types import Update
15
+ from telegrinder.tools.global_context import TelegrinderContext
16
+ from telegrinder.types.enums import UpdateType
17
+ from telegrinder.types.objects import Update
16
18
 
17
19
  from .abc import ABCDispatch
18
- from .handler import ABCHandler, FuncHandler
19
- from .handler.func import ErrorHandlerT
20
20
  from .view.box import (
21
21
  CallbackQueryView,
22
22
  ChatJoinRequestView,
@@ -27,9 +27,10 @@ from .view.box import (
27
27
  ViewBox,
28
28
  )
29
29
 
30
+ if typing.TYPE_CHECKING:
31
+ from telegrinder.bot.rules.abc import ABCRule
32
+
30
33
  T = typing.TypeVar("T")
31
- R = typing.TypeVar("R")
32
- P = typing.ParamSpec("P")
33
34
  Handler = typing.Callable[typing.Concatenate[T, ...], typing.Coroutine[typing.Any, typing.Any, typing.Any]]
34
35
  Event = typing.TypeVar("Event", bound=BaseCute)
35
36
 
@@ -48,18 +49,22 @@ class Dispatch(
48
49
  RawEventView,
49
50
  ],
50
51
  ):
51
- global_context: TelegrinderCtx = dataclasses.field(
52
- init=False,
53
- default_factory=lambda: TelegrinderCtx(),
54
- )
55
52
  default_handlers: list[ABCHandler] = dataclasses.field(
56
53
  init=False,
57
54
  default_factory=lambda: [],
58
55
  )
56
+ _global_context: TelegrinderContext = dataclasses.field(
57
+ init=False,
58
+ default_factory=lambda: TelegrinderContext(),
59
+ )
59
60
 
60
61
  def __repr__(self) -> str:
61
62
  return "Dispatch(%s)" % ", ".join(f"{k}={v!r}" for k, v in self.__dict__.items())
62
63
 
64
+ @property
65
+ def global_context(self) -> TelegrinderContext:
66
+ return self._global_context
67
+
63
68
  @property
64
69
  def patcher(self) -> Patcher:
65
70
  """Alias `patcher` to get `vbml.Patcher` from the global context."""
@@ -69,28 +74,57 @@ class Dispatch(
69
74
  @typing.overload
70
75
  def handle(
71
76
  self,
72
- *rules: ABCRule,
77
+ *rules: "ABCRule",
78
+ is_blocking: bool = True,
79
+ ) -> typing.Callable[[Handler[T]], FuncHandler[UpdateCute, Handler[T], ErrorHandler]]: ...
80
+
81
+ @typing.overload
82
+ def handle(
83
+ self,
84
+ *rules: "ABCRule",
85
+ dataclass: type[T],
86
+ is_blocking: bool = True,
73
87
  ) -> typing.Callable[[Handler[T]], FuncHandler[UpdateCute, Handler[T], ErrorHandler]]: ...
74
88
 
75
89
  @typing.overload
76
90
  def handle(
77
91
  self,
78
- *rules: ABCRule,
92
+ *rules: "ABCRule",
93
+ update_type: UpdateType,
79
94
  is_blocking: bool = True,
80
95
  ) -> typing.Callable[[Handler[T]], FuncHandler[UpdateCute, Handler[T], ErrorHandler]]: ...
81
96
 
82
97
  @typing.overload
83
98
  def handle(
84
99
  self,
85
- *rules: ABCRule,
100
+ *rules: "ABCRule",
86
101
  dataclass: type[T],
102
+ update_type: UpdateType,
87
103
  is_blocking: bool = True,
88
104
  ) -> typing.Callable[[Handler[T]], FuncHandler[UpdateCute, Handler[T], ErrorHandler]]: ...
89
105
 
90
106
  @typing.overload
91
107
  def handle( # type: ignore
92
108
  self,
93
- *rules: ABCRule,
109
+ *rules: "ABCRule",
110
+ error_handler: ErrorHandlerT,
111
+ is_blocking: bool = True,
112
+ ) -> typing.Callable[[Handler[T]], FuncHandler[UpdateCute, Handler[T], ErrorHandlerT]]: ...
113
+
114
+ @typing.overload
115
+ def handle( # type: ignore
116
+ self,
117
+ *rules: "ABCRule",
118
+ update_type: UpdateType,
119
+ error_handler: ErrorHandlerT,
120
+ is_blocking: bool = True,
121
+ ) -> typing.Callable[[Handler[T]], FuncHandler[UpdateCute, Handler[T], ErrorHandlerT]]: ...
122
+
123
+ @typing.overload
124
+ def handle(
125
+ self,
126
+ *rules: "ABCRule",
127
+ dataclass: type[T],
94
128
  error_handler: ErrorHandlerT,
95
129
  is_blocking: bool = True,
96
130
  ) -> typing.Callable[[Handler[T]], FuncHandler[UpdateCute, Handler[T], ErrorHandlerT]]: ...
@@ -98,8 +132,9 @@ class Dispatch(
98
132
  @typing.overload
99
133
  def handle(
100
134
  self,
101
- *rules: ABCRule,
135
+ *rules: "ABCRule",
102
136
  dataclass: type[T],
137
+ update_type: UpdateType,
103
138
  error_handler: ErrorHandlerT,
104
139
  is_blocking: bool = True,
105
140
  ) -> typing.Callable[[Handler[T]], FuncHandler[UpdateCute, Handler[T], ErrorHandlerT]]: ...
@@ -107,7 +142,8 @@ class Dispatch(
107
142
  @typing.overload
108
143
  def handle(
109
144
  self,
110
- *rules: ABCRule,
145
+ *rules: "ABCRule",
146
+ update_type: UpdateType | None = None,
111
147
  dataclass: type[T] = DEFAULT_DATACLASS,
112
148
  error_handler: typing.Literal[None] = None,
113
149
  is_blocking: bool = True,
@@ -115,7 +151,8 @@ class Dispatch(
115
151
 
116
152
  def handle( # type: ignore
117
153
  self,
118
- *rules: ABCRule,
154
+ *rules: "ABCRule",
155
+ update_type: UpdateType | None = None,
119
156
  dataclass: type[typing.Any] = DEFAULT_DATACLASS,
120
157
  error_handler: ErrorHandlerT | None = None,
121
158
  is_blocking: bool = True,
@@ -127,6 +164,7 @@ class Dispatch(
127
164
  is_blocking=is_blocking,
128
165
  dataclass=dataclass,
129
166
  error_handler=error_handler or ErrorHandler(),
167
+ update_type=update_type,
130
168
  )
131
169
  self.default_handlers.append(handler)
132
170
  return handler
@@ -142,18 +180,18 @@ class Dispatch(
142
180
  logger.debug(
143
181
  "Update (update_id={}) matched view {!r}.",
144
182
  event.update_id,
145
- view.__class__.__name__,
183
+ view,
146
184
  )
147
- await view.process(event, api)
148
- return True
185
+ if await view.process(event, api):
186
+ return True
149
187
 
150
- ctx = Context()
188
+ ctx = Context(raw_update=event)
151
189
  loop = asyncio.get_running_loop()
152
190
  found = False
153
191
  for handler in self.default_handlers:
154
192
  if await handler.check(api, event, ctx):
155
193
  found = True
156
- loop.create_task(handler.run(event, ctx))
194
+ loop.create_task(handler.run(api, event, ctx))
157
195
  if handler.is_blocking:
158
196
  break
159
197
  return found
@@ -4,7 +4,7 @@ from abc import ABC, abstractmethod
4
4
  from telegrinder.api.abc import ABCAPI
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
 
@@ -17,7 +17,7 @@ class ABCHandler(ABC, typing.Generic[T]):
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: ABCAPI, event: T, ctx: Context) -> typing.Any:
21
21
  pass
22
22
 
23
23
 
@@ -1,4 +1,5 @@
1
1
  import dataclasses
2
+ from functools import cached_property
2
3
 
3
4
  import typing_extensions as typing
4
5
 
@@ -6,12 +7,14 @@ from telegrinder.api.abc import ABCAPI
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,26 +52,37 @@ 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
59
  async def check(self, api: ABCAPI, 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
+ ctx = Context(raw_update=event) if ctx is None else ctx
60
64
  temp_ctx = ctx.copy()
61
65
  temp_ctx |= self.preset_context
62
66
 
63
- nodes = self.get_required_nodes()
67
+ nodes = self.required_nodes
64
68
  node_col = None
65
69
 
66
70
  if nodes:
67
- node_col = await compose_nodes(nodes, UpdateCute.from_update(event, api), ctx)
71
+ node_col = await compose_nodes(
72
+ UpdateCute.from_update(event, api),
73
+ ctx,
74
+ nodes,
75
+ )
76
+
68
77
  if node_col is None:
69
78
  return False
70
79
  temp_ctx |= node_col.values()
71
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)
85
+
72
86
  for rule in self.rules:
73
87
  if not await check_rule(api, rule, event, temp_ctx):
74
88
  logger.debug("Rule {!r} failed!", rule)
@@ -78,19 +92,24 @@ class FuncHandler(ABCHandler[Event], typing.Generic[Event, F, ErrorHandlerT]):
78
92
  ctx |= temp_ctx
79
93
  return True
80
94
 
81
- async def run(self, event: Event, ctx: Context) -> typing.Any:
82
- api = event.api
95
+ async def run(self, api: ABCAPI, event: Event, ctx: Context) -> typing.Any:
96
+ dataclass_type = typing.get_origin(self.dataclass) or self.dataclass
97
+
98
+ if dataclass_type is Update and (event_node := ctx.pop(EVENT_NODE_KEY, None)) is not None:
99
+ event = event_node
83
100
 
84
- if self.dataclass is not None:
85
- if self.update_type is not None:
101
+ elif dataclass_type is not None:
102
+ if self.update_type is not None and isinstance(event, Update):
86
103
  update = event.to_dict()[self.update_type.value].unwrap()
87
104
  event = (
88
105
  self.dataclass.from_update(update, bound_api=api) # type: ignore
89
- if issubclass(self.dataclass, BaseCute)
90
- else self.dataclass(**update.to_dict())
106
+ if issubclass(dataclass_type, BaseCute)
107
+ else self.dataclass(**update.to_dict()) # type: ignore
91
108
  )
109
+ elif issubclass(dataclass_type, UpdateCute) and isinstance(event, Update):
110
+ event = self.dataclass.from_update(event, bound_api=api) # type: ignore
92
111
  else:
93
- event = self.dataclass(**event.to_dict())
112
+ event = self.dataclass(**event.to_dict()) # type: ignore
94
113
 
95
114
  result = (await self.error_handler.run(self.func, event, api, ctx)).unwrap()
96
115
  if node_col := ctx.node_col:
@@ -37,7 +37,7 @@ class MessageReplyHandler(ABCHandler[MessageCute]):
37
37
  )
38
38
 
39
39
  async def check(self, api: ABCAPI, event: Update, ctx: Context | None = None) -> bool:
40
- ctx = ctx or Context()
40
+ ctx = Context() if ctx is None else ctx
41
41
  temp_ctx = ctx.copy()
42
42
  temp_ctx |= self.preset_context
43
43
 
@@ -49,7 +49,7 @@ class MessageReplyHandler(ABCHandler[MessageCute]):
49
49
  ctx |= temp_ctx
50
50
  return True
51
51
 
52
- async def run(self, event: MessageCute, _: Context) -> typing.Any:
52
+ async def run(self, _: ABCAPI, event: MessageCute, __: Context) -> typing.Any:
53
53
  await event.answer(
54
54
  text=self.text,
55
55
  reply_parameters=ReplyParameters(event.message_id) if self.as_reply else None,
@@ -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]):
@@ -3,26 +3,26 @@ import typing
3
3
  from fntypes.result import Error, Ok
4
4
 
5
5
  from telegrinder.api.abc import ABCAPI
6
- from telegrinder.bot.cute_types import BaseCute
7
6
  from telegrinder.bot.cute_types.update import UpdateCute
8
7
  from telegrinder.bot.dispatch.context import Context
8
+ from telegrinder.bot.dispatch.middleware.abc import ABCMiddleware
9
+ from telegrinder.bot.dispatch.return_manager.abc import ABCReturnManager
10
+ from telegrinder.model import Model
9
11
  from telegrinder.modules import logger
10
12
  from telegrinder.node.composer import CONTEXT_STORE_NODES_KEY, NodeScope, compose_nodes
11
13
  from telegrinder.tools.i18n.base import I18nEnum
12
- from telegrinder.types import Update
13
-
14
- from .middleware.abc import ABCMiddleware
15
- from .return_manager.abc import ABCReturnManager
14
+ from telegrinder.types.objects import Update
16
15
 
17
16
  if typing.TYPE_CHECKING:
18
17
  from telegrinder.bot.dispatch.handler.abc import ABCHandler
19
18
  from telegrinder.bot.rules.abc import ABCRule
20
19
 
21
- Event = typing.TypeVar("Event", bound=BaseCute)
20
+ Event = typing.TypeVar("Event", bound=Model)
22
21
  _: typing.TypeAlias = typing.Any
23
22
 
24
23
 
25
24
  async def process_inner(
25
+ api: ABCAPI,
26
26
  event: Event,
27
27
  raw_event: Update,
28
28
  middlewares: list[ABCMiddleware[Event]],
@@ -42,9 +42,9 @@ async def process_inner(
42
42
  ctx_copy = ctx.copy()
43
43
 
44
44
  for handler in handlers:
45
- if await handler.check(event.api, raw_event, ctx):
45
+ if await handler.check(api, raw_event, ctx):
46
46
  found = True
47
- response = await handler.run(event, ctx)
47
+ response = await handler.run(api, event, ctx)
48
48
  logger.debug("Handler {!r} returned: {!r}", handler, response)
49
49
  responses.append(response)
50
50
  if return_manager is not None:
@@ -93,10 +93,10 @@ async def check_rule(
93
93
  ctx |= ctx_copy
94
94
 
95
95
  # Composing required nodes
96
- nodes = rule.get_required_nodes()
96
+ nodes = rule.required_nodes
97
97
  node_col = None
98
98
  if nodes:
99
- node_col = await compose_nodes(nodes, UpdateCute.from_update(update, api), ctx)
99
+ node_col = await compose_nodes(UpdateCute.from_update(update, api), ctx, nodes)
100
100
  if node_col is None:
101
101
  return False
102
102
 
@@ -3,12 +3,12 @@ import types
3
3
  import typing
4
4
  from abc import ABC, abstractmethod
5
5
 
6
- from telegrinder.bot.cute_types import BaseCute
7
6
  from telegrinder.bot.dispatch.context import Context
7
+ from telegrinder.model import Model
8
8
  from telegrinder.modules import logger
9
9
 
10
10
  T = typing.TypeVar("T")
11
- Event = typing.TypeVar("Event", bound=BaseCute, contravariant=True)
11
+ Event = typing.TypeVar("Event", bound=Model, contravariant=True)
12
12
 
13
13
 
14
14
  def get_union_types(t: types.UnionType) -> tuple[type, ...] | None:
@@ -24,7 +24,7 @@ def register_manager(return_type: type[typing.Any] | types.UnionType):
24
24
  return wrapper
25
25
 
26
26
 
27
- @dataclasses.dataclass(frozen=True)
27
+ @dataclasses.dataclass(frozen=True, slots=True)
28
28
  class Manager:
29
29
  types: tuple[type, ...]
30
30
  callback: typing.Callable[..., typing.Awaitable]
@@ -1,5 +1,6 @@
1
1
  import typing
2
2
  from abc import ABC, abstractmethod
3
+ from functools import cached_property
3
4
 
4
5
  from fntypes.co import Nothing, Some
5
6
 
@@ -28,17 +29,14 @@ FuncType: typing.TypeAlias = typing.Callable[
28
29
 
29
30
  class ABCView(ABC):
30
31
  def __repr__(self) -> str:
31
- return "<{!r}: {}>".format(
32
- self.__class__.__name__,
33
- ", ".join(f"{k}={v!r}" for k, v in self.__dict__.items()),
34
- )
32
+ return "<{!r}>".format(self.__class__.__name__)
35
33
 
36
34
  @abstractmethod
37
35
  async def check(self, event: Update) -> bool:
38
36
  pass
39
37
 
40
38
  @abstractmethod
41
- async def process(self, event: Update, api: ABCAPI) -> None:
39
+ async def process(self, event: Update, api: ABCAPI) -> bool:
42
40
  pass
43
41
 
44
42
  @abstractmethod
@@ -62,9 +60,9 @@ class BaseView(ABCView, typing.Generic[Event]):
62
60
  def get_raw_event(update: Update) -> Option[Model]:
63
61
  return getattr(update, update.update_type.value)
64
62
 
65
- @classmethod
66
- def get_event_type(cls) -> Option[type[Event]]:
67
- for base in cls.__dict__.get("__orig_bases__", ()):
63
+ @cached_property
64
+ def get_event_type(self) -> Option[type[Event]]:
65
+ for base in self.__class__.__dict__.get("__orig_bases__", ()):
68
66
  if issubclass(typing.get_origin(base) or base, ABCView):
69
67
  for generic_type in typing.get_args(base):
70
68
  if issubclass(typing.get_origin(generic_type) or generic_type, BaseCute):
@@ -177,20 +175,19 @@ class BaseView(ABCView, typing.Generic[Event]):
177
175
  async def check(self, event: Update) -> bool:
178
176
  match self.get_raw_event(event):
179
177
  case Some(e) if issubclass(
180
- self.get_event_type().expect(
178
+ self.get_event_type.expect(
181
179
  "{!r} has no event type in generic.".format(self.__class__.__name__),
182
180
  ),
183
181
  e.__class__,
184
- ):
182
+ ) and (self.handlers or self.middlewares):
185
183
  return True
186
184
  case _:
187
185
  return False
188
186
 
189
- async def process(self, event: Update, api: ABCAPI) -> None:
190
- await process_inner(
191
- self.get_event_type()
192
- .unwrap()
193
- .from_update(
187
+ async def process(self, event: Update, api: ABCAPI) -> bool:
188
+ return await process_inner(
189
+ api,
190
+ self.get_event_type.unwrap().from_update(
194
191
  update=self.get_raw_event(event).unwrap(),
195
192
  bound_api=api,
196
193
  ),
@@ -36,78 +36,89 @@ class ViewBox(
36
36
  RawEventView,
37
37
  ],
38
38
  ):
39
- callback_query: CallbackQueryView = dataclasses.field(
40
- default_factory=lambda: typing.cast(
39
+ callback_query_view: dataclasses.InitVar[CallbackQueryView | None] = None
40
+ chat_join_request_view: dataclasses.InitVar[ChatJoinRequestView | None] = None
41
+ chat_member_view: dataclasses.InitVar[ChatMemberView | None] = None
42
+ my_chat_member_view: dataclasses.InitVar[ChatMemberView | None] = None
43
+ inline_query_view: dataclasses.InitVar[InlineQueryView | None] = None
44
+ message_view: dataclasses.InitVar[MessageView | None] = None
45
+ business_message_view: dataclasses.InitVar[MessageView | None] = None
46
+ channel_post_view: dataclasses.InitVar[MessageView | None] = None
47
+ edited_message_view: dataclasses.InitVar[MessageView | None] = None
48
+ edited_business_message_view: dataclasses.InitVar[MessageView | None] = None
49
+ edited_channel_post_view: dataclasses.InitVar[MessageView | None] = None
50
+ any_message_view: dataclasses.InitVar[MessageView | None] = None
51
+ chat_member_updated_view: dataclasses.InitVar[ChatMemberView | None] = None
52
+ raw_event_view: dataclasses.InitVar[RawEventView | None] = None
53
+
54
+ def __post_init__(
55
+ self,
56
+ callback_query_view: CallbackQueryView | None = None,
57
+ chat_join_request_view: ChatJoinRequestView | None = None,
58
+ chat_member_view: ChatMemberView | None = None,
59
+ my_chat_member_view: ChatMemberView | None = None,
60
+ inline_query_view: InlineQueryView | None = None,
61
+ message_view: MessageView | None = None,
62
+ business_message_view: MessageView | None = None,
63
+ channel_post_view: MessageView | None = None,
64
+ edited_message_view: MessageView | None = None,
65
+ edited_business_message_view: MessageView | None = None,
66
+ edited_channel_post_view: MessageView | None = None,
67
+ any_message_view: MessageView | None = None,
68
+ chat_member_updated_view: ChatMemberView | None = None,
69
+ raw_event_view: RawEventView | None = None,
70
+ ) -> None:
71
+ self.callback_query = typing.cast(
41
72
  CallbackQueryView,
42
- callback_query.CallbackQueryView(),
43
- ),
44
- )
45
- chat_join_request: ChatJoinRequestView = dataclasses.field(
46
- default_factory=lambda: typing.cast(
73
+ callback_query_view or callback_query.CallbackQueryView(),
74
+ )
75
+ self.chat_join_request = typing.cast(
47
76
  ChatJoinRequestView,
48
- chat_join_request.ChatJoinRequestView(),
49
- ),
50
- )
51
- chat_member: ChatMemberView = dataclasses.field(
52
- default_factory=lambda: typing.cast(
77
+ chat_join_request_view or chat_join_request.ChatJoinRequestView(),
78
+ )
79
+ self.chat_member = typing.cast(
53
80
  ChatMemberView,
54
- chat_member.ChatMemberView(update_type=UpdateType.CHAT_MEMBER),
55
- ),
56
- )
57
- my_chat_member: ChatMemberView = dataclasses.field(
58
- default_factory=lambda: typing.cast(
81
+ chat_member_view or chat_member.ChatMemberView(update_type=UpdateType.CHAT_MEMBER),
82
+ )
83
+ self.my_chat_member = typing.cast(
59
84
  ChatMemberView,
60
- chat_member.ChatMemberView(update_type=UpdateType.MY_CHAT_MEMBER),
61
- ),
62
- )
63
- inline_query: InlineQueryView = dataclasses.field(
64
- default_factory=lambda: typing.cast(InlineQueryView, inline_query.InlineQueryView()),
65
- )
66
- message: MessageView = dataclasses.field(
67
- default_factory=lambda: typing.cast(
85
+ my_chat_member_view or chat_member.ChatMemberView(update_type=UpdateType.MY_CHAT_MEMBER),
86
+ )
87
+ self.inline_query = typing.cast(
88
+ InlineQueryView,
89
+ inline_query_view or inline_query.InlineQueryView(),
90
+ )
91
+ self.message = typing.cast(
68
92
  MessageView,
69
- message.MessageView(update_type=UpdateType.MESSAGE),
70
- ),
71
- )
72
- business_message: MessageView = dataclasses.field(
73
- default_factory=lambda: typing.cast(
93
+ message_view or message.MessageView(update_type=UpdateType.MESSAGE),
94
+ )
95
+ self.business_message = typing.cast(
74
96
  MessageView,
75
- message.MessageView(update_type=UpdateType.BUSINESS_MESSAGE),
76
- ),
77
- )
78
- channel_post: MessageView = dataclasses.field(
79
- default_factory=lambda: typing.cast(
97
+ business_message_view or message.MessageView(update_type=UpdateType.BUSINESS_MESSAGE),
98
+ )
99
+ self.channel_post = typing.cast(
80
100
  MessageView,
81
- message.MessageView(update_type=UpdateType.CHANNEL_POST),
82
- ),
83
- )
84
- edited_message: MessageView = dataclasses.field(
85
- default_factory=lambda: typing.cast(
101
+ channel_post_view or message.MessageView(update_type=UpdateType.CHANNEL_POST),
102
+ )
103
+ self.edited_message = typing.cast(
86
104
  MessageView,
87
- message.MessageView(update_type=UpdateType.EDITED_MESSAGE),
88
- ),
89
- )
90
- edited_business_message: MessageView = dataclasses.field(
91
- default_factory=lambda: typing.cast(
105
+ edited_message_view or message.MessageView(update_type=UpdateType.EDITED_MESSAGE),
106
+ )
107
+ self.edited_business_message = typing.cast(
92
108
  MessageView,
93
- message.MessageView(update_type=UpdateType.EDITED_BUSINESS_MESSAGE),
94
- ),
95
- )
96
- edited_channel_post: MessageView = dataclasses.field(
97
- default_factory=lambda: typing.cast(
109
+ edited_business_message_view
110
+ or message.MessageView(update_type=UpdateType.EDITED_BUSINESS_MESSAGE),
111
+ )
112
+ self.edited_channel_post = typing.cast(
98
113
  MessageView,
99
- message.MessageView(update_type=UpdateType.EDITED_CHANNEL_POST),
100
- ),
101
- )
102
- any_message: MessageView = dataclasses.field(
103
- default_factory=lambda: typing.cast(MessageView, message.MessageView()),
104
- )
105
- chat_member_updated: ChatMemberView = dataclasses.field(
106
- default_factory=lambda: typing.cast(ChatMemberView, chat_member.ChatMemberView()),
107
- )
108
- raw_event: RawEventView = dataclasses.field(
109
- default_factory=lambda: typing.cast(RawEventView, raw.RawEventView()),
110
- )
114
+ edited_channel_post_view or message.MessageView(update_type=UpdateType.EDITED_CHANNEL_POST),
115
+ )
116
+ self.any_message = typing.cast(MessageView, any_message_view or message.MessageView())
117
+ self.chat_member_updated = typing.cast(
118
+ ChatMemberView,
119
+ chat_member_updated_view or chat_member.ChatMemberView(),
120
+ )
121
+ self.raw_event = typing.cast(RawEventView, raw_event_view or raw.RawEventView())
111
122
 
112
123
  def get_views(self) -> dict[str, ABCView]:
113
124
  """Get all views."""
@@ -24,13 +24,21 @@ class MessageView(BaseStateView[MessageCute]):
24
24
  self.middlewares = []
25
25
  self.return_manager = MessageReturnManager()
26
26
 
27
+ def __repr__(self) -> str:
28
+ return "<{}: {!r}>".format(
29
+ self.__class__.__name__,
30
+ "any message update" if self.update_type is None else self.update_type.value,
31
+ )
32
+
27
33
  def get_state_key(self, event: MessageCute) -> int | None:
28
34
  return event.chat_id
29
35
 
30
36
  async def check(self, event: Update) -> bool:
31
- if not await super().check(event):
32
- return False
33
- return True if self.update_type is None else self.update_type == event.update_type
37
+ return not (
38
+ self.update_type is not None
39
+ and self.update_type != event.update_type
40
+ or not await super().check(event)
41
+ )
34
42
 
35
43
 
36
44
  __all__ = ("MessageView",)