telegrinder 0.1.dev162__tar.gz → 0.1.dev164__tar.gz

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 (128) hide show
  1. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/PKG-INFO +1 -1
  2. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/pyproject.toml +1 -1
  3. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/bot.py +7 -11
  4. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/dispatch/abc.py +1 -1
  5. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/dispatch/composition.py +15 -1
  6. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/dispatch/context.py +8 -0
  7. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/dispatch/dispatch.py +67 -31
  8. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/dispatch/handler/abc.py +2 -4
  9. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/dispatch/handler/func.py +31 -25
  10. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/dispatch/handler/message_reply.py +21 -9
  11. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/dispatch/middleware/abc.py +1 -1
  12. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/dispatch/process.py +5 -3
  13. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/dispatch/return_manager/callback_query.py +3 -1
  14. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/dispatch/return_manager/inline_query.py +3 -1
  15. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/dispatch/return_manager/message.py +9 -1
  16. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/dispatch/view/abc.py +31 -5
  17. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/dispatch/view/box.py +8 -7
  18. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/dispatch/view/inline_query.py +1 -1
  19. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/dispatch/view/message.py +1 -1
  20. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/dispatch/waiter_machine/machine.py +14 -8
  21. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/dispatch/waiter_machine/middleware.py +9 -7
  22. telegrinder-0.1.dev164/telegrinder/bot/dispatch/waiter_machine/short_state.py +36 -0
  23. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/rules/__init__.py +4 -0
  24. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/rules/is_from.py +18 -3
  25. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/rules/text.py +1 -1
  26. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/scenario/checkbox.py +16 -0
  27. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/client/aiohttp.py +1 -2
  28. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/msgspec_utils.py +51 -8
  29. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/node/base.py +1 -1
  30. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/node/composer.py +1 -1
  31. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/node/source.py +2 -3
  32. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/tools/error_handler/abc.py +3 -3
  33. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/tools/error_handler/error_handler.py +23 -23
  34. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/tools/global_context/global_context.py +7 -5
  35. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/tools/magic.py +1 -1
  36. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/types/objects.py +19 -2
  37. telegrinder-0.1.dev162/telegrinder/bot/dispatch/waiter_machine/short_state.py +0 -37
  38. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/LICENSE +0 -0
  39. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/readme.md +0 -0
  40. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/__init__.py +0 -0
  41. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/api/__init__.py +0 -0
  42. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/api/abc.py +0 -0
  43. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/api/api.py +0 -0
  44. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/api/error.py +0 -0
  45. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/api/response.py +0 -0
  46. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/__init__.py +0 -0
  47. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/cute_types/__init__.py +0 -0
  48. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/cute_types/base.py +0 -0
  49. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/cute_types/callback_query.py +0 -0
  50. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/cute_types/inline_query.py +0 -0
  51. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/cute_types/message.py +0 -0
  52. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/cute_types/update.py +0 -0
  53. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/cute_types/utils.py +0 -0
  54. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/dispatch/__init__.py +0 -0
  55. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/dispatch/handler/__init__.py +0 -0
  56. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/dispatch/middleware/__init__.py +0 -0
  57. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/dispatch/return_manager/__init__.py +0 -0
  58. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/dispatch/return_manager/abc.py +0 -0
  59. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/dispatch/view/__init__.py +0 -0
  60. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/dispatch/view/callback_query.py +0 -0
  61. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/dispatch/waiter_machine/__init__.py +0 -0
  62. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/polling/__init__.py +0 -0
  63. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/polling/abc.py +0 -0
  64. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/polling/polling.py +0 -0
  65. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/rules/abc.py +0 -0
  66. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/rules/adapter/__init__.py +0 -0
  67. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/rules/adapter/abc.py +0 -0
  68. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/rules/adapter/errors.py +0 -0
  69. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/rules/adapter/event.py +0 -0
  70. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/rules/adapter/raw_update.py +0 -0
  71. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/rules/callback_data.py +0 -0
  72. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/rules/command.py +0 -0
  73. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/rules/enum_text.py +0 -0
  74. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/rules/func.py +0 -0
  75. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/rules/fuzzy.py +0 -0
  76. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/rules/inline.py +0 -0
  77. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/rules/integer.py +0 -0
  78. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/rules/markup.py +0 -0
  79. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/rules/mention.py +0 -0
  80. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/rules/message_entities.py +0 -0
  81. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/rules/regex.py +0 -0
  82. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/rules/rule_enum.py +0 -0
  83. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/rules/start.py +0 -0
  84. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/scenario/__init__.py +0 -0
  85. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/scenario/abc.py +0 -0
  86. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/bot/scenario/choice.py +0 -0
  87. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/client/__init__.py +0 -0
  88. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/client/abc.py +0 -0
  89. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/model.py +0 -0
  90. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/modules.py +0 -0
  91. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/msgspec_json.py +0 -0
  92. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/node/__init__.py +0 -0
  93. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/node/attachment.py +0 -0
  94. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/node/container.py +0 -0
  95. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/node/message.py +0 -0
  96. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/node/rule.py +0 -0
  97. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/node/text.py +0 -0
  98. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/node/tools/__init__.py +0 -0
  99. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/node/tools/generator.py +0 -0
  100. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/node/update.py +0 -0
  101. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/rules.py +0 -0
  102. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/tools/__init__.py +0 -0
  103. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/tools/buttons.py +0 -0
  104. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/tools/error_handler/__init__.py +0 -0
  105. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/tools/error_handler/error.py +0 -0
  106. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/tools/formatting/__init__.py +0 -0
  107. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/tools/formatting/html.py +0 -0
  108. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/tools/formatting/links.py +0 -0
  109. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/tools/formatting/spec_html_formats.py +0 -0
  110. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/tools/global_context/__init__.py +0 -0
  111. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/tools/global_context/abc.py +0 -0
  112. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/tools/global_context/telegrinder_ctx.py +0 -0
  113. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/tools/i18n/__init__.py +0 -0
  114. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/tools/i18n/base.py +0 -0
  115. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/tools/i18n/middleware/__init__.py +0 -0
  116. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/tools/i18n/middleware/base.py +0 -0
  117. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/tools/i18n/simple.py +0 -0
  118. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/tools/kb_set/__init__.py +0 -0
  119. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/tools/kb_set/base.py +0 -0
  120. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/tools/kb_set/yaml.py +0 -0
  121. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/tools/keyboard.py +0 -0
  122. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/tools/loop_wrapper/__init__.py +0 -0
  123. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/tools/loop_wrapper/abc.py +0 -0
  124. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/tools/loop_wrapper/loop_wrapper.py +0 -0
  125. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/tools/parse_mode.py +0 -0
  126. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/types/__init__.py +0 -0
  127. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/types/enums.py +0 -0
  128. {telegrinder-0.1.dev162 → telegrinder-0.1.dev164}/telegrinder/types/methods.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: telegrinder
3
- Version: 0.1.dev162
3
+ Version: 0.1.dev164
4
4
  Summary: Framework for effective and reliable async telegram bot building.
5
5
  Home-page: https://github.com/timoniq/telegrinder
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "telegrinder"
3
- version = "0.1.dev162"
3
+ version = "0.1.dev164"
4
4
  description = "Framework for effective and reliable async telegram bot building."
5
5
  authors = ["timoniq <tesseradecades@mail.ru>"]
6
6
  maintainers = ["luwqz1 <howluwqz1@gmail.com>"]
@@ -11,11 +11,7 @@ PollingT = typing.TypeVar("PollingT", bound=ABCPolling, default=Polling)
11
11
  LoopWrapperT = typing.TypeVar("LoopWrapperT", bound=ABCLoopWrapper, default=LoopWrapper)
12
12
 
13
13
 
14
- class Telegrinder(typing.Generic[DispatchT, PollingT, LoopWrapperT]):
15
- dispatch: DispatchT
16
- polling: PollingT
17
- loop_wrapper: LoopWrapperT
18
-
14
+ class Telegrinder(typing.Generic[DispatchT, PollingT, LoopWrapperT]):
19
15
  def __init__(
20
16
  self,
21
17
  api: API,
@@ -25,9 +21,9 @@ class Telegrinder(typing.Generic[DispatchT, PollingT, LoopWrapperT]):
25
21
  loop_wrapper: LoopWrapperT | None = None,
26
22
  ) -> None:
27
23
  self.api = api
28
- self.dispatch = dispatch or Dispatch() # type: ignore
29
- self.polling = polling or Polling(api) # type: ignore
30
- self.loop_wrapper = loop_wrapper or LoopWrapper() # type: ignore
24
+ self.dispatch = typing.cast(DispatchT, dispatch or Dispatch())
25
+ self.polling = typing.cast(PollingT, polling or Polling(api))
26
+ self.loop_wrapper = typing.cast(LoopWrapperT, loop_wrapper or LoopWrapper())
31
27
 
32
28
  @property
33
29
  def on(self) -> DispatchT:
@@ -38,7 +34,7 @@ class Telegrinder(typing.Generic[DispatchT, PollingT, LoopWrapperT]):
38
34
  return
39
35
  await self.api.delete_webhook()
40
36
 
41
- async def run_polling(self, offset: int = 0, skip_updates: bool = False) -> None:
37
+ async def run_polling(self, *, offset: int = 0, skip_updates: bool = False) -> None:
42
38
  if skip_updates:
43
39
  logger.debug("Dropping pending updates")
44
40
  await self.reset_webhook()
@@ -50,9 +46,9 @@ class Telegrinder(typing.Generic[DispatchT, PollingT, LoopWrapperT]):
50
46
  logger.debug("Received update (update_id={})", update.update_id)
51
47
  self.loop_wrapper.add_task(self.dispatch.feed(update, self.api))
52
48
 
53
- def run_forever(self, offset: int = 0, skip_updates: bool = False) -> None:
49
+ def run_forever(self, *, offset: int = 0, skip_updates: bool = False) -> None:
54
50
  logger.debug("Running blocking polling (id={})", self.api.id)
55
- self.loop_wrapper.add_task(self.run_polling(offset, skip_updates=skip_updates))
51
+ self.loop_wrapper.add_task(self.run_polling(offset=offset, skip_updates=skip_updates))
56
52
  self.loop_wrapper.run_event_loop()
57
53
 
58
54
 
@@ -14,7 +14,7 @@ class ABCDispatch(ABC):
14
14
  pass
15
15
 
16
16
  @abstractmethod
17
- def load(self, external: typing.Self):
17
+ def load(self, external: typing.Self) -> None:
18
18
  pass
19
19
 
20
20
 
@@ -27,12 +27,20 @@ class Composition:
27
27
  }
28
28
  self.is_blocking = is_blocking
29
29
 
30
+ def __repr__(self) -> str:
31
+ return "<{}: for function={!r} with nodes={}>".format(
32
+ ("blocking " if self.is_blocking else "")
33
+ + self.__class__.__name__,
34
+ self.func.__name__,
35
+ self.nodes,
36
+ )
37
+
30
38
  async def compose_nodes(self, update: UpdateCute) -> NodeCollection | None:
31
39
  nodes: dict[str, NodeSession] = {}
32
40
  for name, node_t in self.nodes.items():
33
41
  try:
34
42
  nodes[name] = await compose_node(node_t, update)
35
- except ComposeError as err:
43
+ except ComposeError:
36
44
  await NodeCollection(nodes).close_all()
37
45
  return None
38
46
  return NodeCollection(nodes)
@@ -45,6 +53,12 @@ class CompositionDispatch(ABCDispatch):
45
53
  def __init__(self) -> None:
46
54
  self.compositions: list[Composition] = []
47
55
 
56
+ def __repr__(self) -> str:
57
+ return "<{}: with compositions={!r}>".format(
58
+ self.__class__.__name__,
59
+ self.compositions,
60
+ )
61
+
48
62
  async def feed(self, event: Update, api: ABCAPI) -> bool:
49
63
  update = UpdateCute(**event.to_dict(), api=api)
50
64
  is_found = False
@@ -1,5 +1,6 @@
1
1
  import enum
2
2
  import typing
3
+ from reprlib import recursive_repr
3
4
 
4
5
  from telegrinder.types import Update
5
6
 
@@ -33,6 +34,13 @@ class Context(dict[str, AnyValue]):
33
34
  delattr(self.__class__, k)
34
35
  dict.__init__(self, **defaults | kwargs)
35
36
 
37
+ @recursive_repr()
38
+ def __repr__(self) -> str:
39
+ return "{}({})".format(
40
+ self.__class__.__name__,
41
+ ", ".join(f"{k}={v!r}" for k, v in self.items())
42
+ )
43
+
36
44
  def __setitem__(self, __key: Key, __value: AnyValue) -> None:
37
45
  dict.__setitem__(self, self.key_to_str(__key), __value)
38
46
 
@@ -6,8 +6,10 @@ from vbml.patcher import Patcher
6
6
 
7
7
  from telegrinder.api.abc import ABCAPI
8
8
  from telegrinder.bot.cute_types.base import BaseCute
9
+ from telegrinder.bot.dispatch.context import Context
9
10
  from telegrinder.bot.rules import ABCRule
10
11
  from telegrinder.modules import logger
12
+ from telegrinder.tools.error_handler.error_handler import ErrorHandler
11
13
  from telegrinder.tools.global_context import TelegrinderCtx
12
14
  from telegrinder.types import Update
13
15
 
@@ -17,12 +19,12 @@ from .handler.func import ErrorHandlerT
17
19
  from .view.box import CallbackQueryViewT, InlineQueryViewT, MessageViewT, ViewBox
18
20
 
19
21
  T = typing.TypeVar("T")
20
-
21
- Event = typing.TypeVar("Event", bound=BaseCute)
22
22
  R = typing.TypeVar("R")
23
23
  P = typing.ParamSpec("P")
24
+ Handler = typing.Callable[typing.Concatenate[T, ...], typing.Coroutine[typing.Any, typing.Any, typing.Any]]
25
+ Event = typing.TypeVar("Event", bound=BaseCute)
24
26
 
25
- DEFAULT_DATACLASS = Update
27
+ DEFAULT_DATACLASS: typing.Final[type[Update]] = Update
26
28
 
27
29
 
28
30
  @dataclasses.dataclass(repr=False, kw_only=True)
@@ -48,17 +50,74 @@ class Dispatch(
48
50
  def patcher(self) -> Patcher:
49
51
  """Alias `patcher` to get `vbml.Patcher` from the global context"""
50
52
  return self.global_context.vbml_patcher
53
+
54
+ @typing.overload
55
+ def handle(
56
+ self,
57
+ *rules: ABCRule[Event],
58
+ ) -> typing.Callable[[Handler[T]], FuncHandler[Event, Handler[T], ErrorHandler]]:
59
+ ...
51
60
 
61
+ @typing.overload
52
62
  def handle(
53
63
  self,
54
- *rules: ABCRule,
64
+ *rules: ABCRule[Event],
65
+ is_blocking: bool = True,
66
+ ) -> typing.Callable[[Handler[T]], FuncHandler[Event, Handler[T], ErrorHandler]]:
67
+ ...
68
+
69
+ @typing.overload
70
+ def handle(
71
+ self,
72
+ *rules: ABCRule[Event],
73
+ dataclass: type[T],
74
+ is_blocking: bool = True,
75
+ ) -> typing.Callable[[Handler[T]], FuncHandler[Event, Handler[T], ErrorHandler]]:
76
+ ...
77
+
78
+ @typing.overload
79
+ def handle( # type: ignore
80
+ self,
81
+ *rules: ABCRule[Event],
82
+ error_handler: ErrorHandlerT,
83
+ is_blocking: bool = True,
84
+ ) -> typing.Callable[[Handler[T]], FuncHandler[Event, Handler[T], ErrorHandlerT]]:
85
+ ...
86
+
87
+ @typing.overload
88
+ def handle(
89
+ self,
90
+ *rules: ABCRule[Event],
91
+ dataclass: type[T],
92
+ error_handler: ErrorHandlerT,
55
93
  is_blocking: bool = True,
94
+ ) -> typing.Callable[[Handler[T]], FuncHandler[Event, Handler[T], ErrorHandlerT]]:
95
+ ...
96
+
97
+ @typing.overload
98
+ def handle(
99
+ self,
100
+ *rules: ABCRule[Event],
101
+ dataclass: type[T] = DEFAULT_DATACLASS,
102
+ error_handler: typing.Literal[None] = None,
103
+ is_blocking: bool = True,
104
+ ) -> typing.Callable[[Handler[T]], FuncHandler[Event, Handler[T], ErrorHandler]]:
105
+ ...
106
+
107
+ def handle( # type: ignore
108
+ self,
109
+ *rules: ABCRule,
56
110
  dataclass: type[typing.Any] = DEFAULT_DATACLASS,
57
111
  error_handler: ErrorHandlerT | None = None,
112
+ is_blocking: bool = True,
58
113
  ):
59
114
  def wrapper(func: typing.Callable):
60
115
  handler = FuncHandler(
61
- func, list(rules), is_blocking, dataclass, error_handler,
116
+ func,
117
+ list(rules),
118
+ is_blocking=is_blocking,
119
+ dataclass=dataclass,
120
+ error_handler=error_handler or ErrorHandler(),
62
121
  )
63
122
  self.default_handlers.append(handler)
64
123
  return handler
@@ -78,11 +137,12 @@ class Dispatch(
78
137
  return True
79
138
 
80
139
  loop = asyncio.get_running_loop()
140
+ ctx = Context()
81
141
  found = False
82
142
  for handler in self.default_handlers:
83
- if await handler.check(api, event):
143
+ if await handler.check(api, event, ctx):
84
144
  found = True
85
- loop.create_task(handler.run(event))
145
+ loop.create_task(handler.run(event, ctx))
86
146
  if handler.is_blocking:
87
147
  break
88
148
  return found
@@ -95,30 +155,6 @@ class Dispatch(
95
155
  ), f"View {name!r} is undefined in external dispatch."
96
156
  view.load(view_external[name])
97
157
  setattr(external, name, view)
98
-
99
- @classmethod
100
- def to_handler(
101
- cls,
102
- *rules: ABCRule[BaseCute],
103
- is_blocking: bool = True,
104
- error_handler: ErrorHandlerT | None = None,
105
- ):
106
- def wrapper(
107
- func: typing.Callable[typing.Concatenate[T, P], typing.Awaitable[R]]
108
- ) -> FuncHandler[
109
- BaseCute,
110
- typing.Callable[typing.Concatenate[T, P], typing.Awaitable[R]],
111
- ErrorHandlerT,
112
- ]:
113
- return FuncHandler(
114
- func,
115
- list(rules),
116
- is_blocking=is_blocking,
117
- dataclass=None,
118
- error_handler=error_handler,
119
- )
120
-
121
- return wrapper
122
158
 
123
159
 
124
160
  __all__ = ("Dispatch",)
@@ -11,15 +11,13 @@ T = typing.TypeVar("T", bound=Model)
11
11
 
12
12
  class ABCHandler(ABC, typing.Generic[T]):
13
13
  is_blocking: bool
14
- ctx: Context
15
14
 
16
15
  @abstractmethod
17
- async def run(self, event: T) -> typing.Any:
16
+ async def check(self, api: ABCAPI, event: Update, ctx: Context | None = None) -> bool:
18
17
  pass
19
18
 
20
19
  @abstractmethod
21
- async def check(self, api: ABCAPI, event: Update, ctx: Context | None = None) -> bool:
20
+ async def run(self, event: T, ctx: Context) -> typing.Any:
22
21
  pass
23
22
 
24
-
25
23
  __all__ = ("ABCHandler",)
@@ -1,3 +1,5 @@
1
+ import dataclasses
2
+
1
3
  import typing_extensions as typing
2
4
 
3
5
  from telegrinder.api.abc import ABCAPI
@@ -13,46 +15,50 @@ from .abc import ABCHandler
13
15
  if typing.TYPE_CHECKING:
14
16
  from telegrinder.bot.rules import ABCRule
15
17
 
16
- F = typing.TypeVar("F", bound=typing.Callable[typing.Concatenate[typing.Any, ...], typing.Awaitable])
18
+ F = typing.TypeVar("F", bound=typing.Callable[typing.Concatenate[typing.Any, ...], typing.Awaitable[typing.Any]])
17
19
  EventT = typing.TypeVar("EventT", bound=BaseCute)
18
20
  ErrorHandlerT = typing.TypeVar("ErrorHandlerT", bound=ABCErrorHandler, default=ErrorHandler)
19
21
 
20
22
 
23
+ @dataclasses.dataclass(repr=False)
21
24
  class FuncHandler(ABCHandler[EventT], typing.Generic[EventT, F, ErrorHandlerT]):
22
- def __init__(
23
- self,
24
- func: F,
25
- rules: list["ABCRule[EventT]"],
26
- is_blocking: bool = True,
27
- dataclass: type[typing.Any] | None = dict,
28
- error_handler: ErrorHandlerT | None = None,
29
- ):
30
- self.func = func
31
- self.is_blocking = is_blocking
32
- self.rules = rules
33
- self.dataclass = dataclass
34
- self.error_handler: ErrorHandlerT = error_handler or ErrorHandler() # type: ignore
35
- self.ctx = Context()
36
-
37
- @property
38
- def on_error(self):
39
- return self.error_handler.register_catcher
25
+ func: F
26
+ rules: list["ABCRule[EventT]"]
27
+ _: dataclasses.KW_ONLY
28
+ is_blocking: bool = dataclasses.field(default=True)
29
+ dataclass: type[typing.Any] | None = dataclasses.field(default=dict)
30
+ error_handler: ErrorHandlerT = dataclasses.field(
31
+ default_factory=lambda: typing.cast(ErrorHandlerT, ErrorHandler()),
32
+ )
33
+ preset_context: Context = dataclasses.field(default_factory=lambda: Context())
34
+
35
+ def __repr__(self) -> str:
36
+ return "<{}: {}={!r} with rules={!r}, dataclass={!r}, error_handler={!r}>".format(
37
+ self.__class__.__name__,
38
+ "blocking function" if self.is_blocking else "function",
39
+ self.func.__name__,
40
+ self.rules,
41
+ self.dataclass,
42
+ self.error_handler,
43
+ )
40
44
 
41
45
  async def check(self, api: ABCAPI, event: Update, ctx: Context | None = None) -> bool:
42
46
  ctx = ctx or Context()
43
- preset_ctx = self.ctx.copy()
44
- self.ctx |= ctx
47
+ temp_ctx = ctx.copy()
48
+ temp_ctx |= self.preset_context
49
+
45
50
  for rule in self.rules:
46
- if not await check_rule(api, rule, event, self.ctx):
51
+ if not await check_rule(api, rule, event, temp_ctx):
47
52
  logger.debug("Rule {!r} failed!", rule)
48
- self.ctx = preset_ctx
49
53
  return False
54
+
55
+ ctx |= temp_ctx
50
56
  return True
51
57
 
52
- async def run(self, event: EventT) -> typing.Any:
58
+ async def run(self, event: EventT, ctx: Context) -> typing.Any:
53
59
  if self.dataclass is not None:
54
60
  event = self.dataclass(**event.to_dict())
55
- return (await self.error_handler.run(self.func, event, event.api, self.ctx)).unwrap()
61
+ return (await self.error_handler.run(self.func, event, event.api, ctx)).unwrap()
56
62
 
57
63
 
58
64
  __all__ = ("FuncHandler",)
@@ -7,7 +7,7 @@ from telegrinder.bot.dispatch.process import check_rule
7
7
  from telegrinder.bot.rules.abc import ABCRule
8
8
  from telegrinder.modules import logger
9
9
  from telegrinder.msgspec_utils import Nothing
10
- from telegrinder.types.objects import Update
10
+ from telegrinder.types.objects import ReplyParameters, Update
11
11
 
12
12
  from .abc import ABCHandler
13
13
 
@@ -17,29 +17,41 @@ class MessageReplyHandler(ABCHandler[MessageCute]):
17
17
  self,
18
18
  text: str,
19
19
  *rules: ABCRule[MessageCute],
20
- as_reply: bool = False,
21
20
  is_blocking: bool = True,
22
- ):
21
+ as_reply: bool = False,
22
+ ) -> None:
23
23
  self.text = text
24
24
  self.rules = list(rules)
25
25
  self.as_reply = as_reply
26
26
  self.is_blocking = is_blocking
27
- self.dataclass = MessageCute
28
- self.ctx = Context()
27
+ self.preset_context = Context()
28
+
29
+ def __repr__(self) -> str:
30
+ return "<{}: with rules={!r}, {}: {!r}>".format(
31
+ ("blocking " if self.is_blocking else "")
32
+ + self.__class__.__name__,
33
+ self.rules,
34
+ "answer text as reply" if self.as_reply else "answer text",
35
+ self.text,
36
+ )
29
37
 
30
38
  async def check(self, api: ABCAPI, event: Update, ctx: Context | None = None) -> bool:
31
39
  ctx = ctx or Context()
32
- self.ctx |= ctx
40
+ temp_ctx = ctx.copy()
41
+ temp_ctx |= self.preset_context
42
+
33
43
  for rule in self.rules:
34
- if not await check_rule(api, rule, event, self.ctx):
44
+ if not await check_rule(api, rule, event, ctx):
35
45
  logger.debug("Rule {!r} failed!", rule)
36
46
  return False
47
+
48
+ ctx |= temp_ctx
37
49
  return True
38
50
 
39
- async def run(self, event: MessageCute) -> typing.Any:
51
+ async def run(self, event: MessageCute, _: Context) -> typing.Any:
40
52
  await event.answer(
41
53
  text=self.text,
42
- reply_to_message_id=(event.message_id if self.as_reply else Nothing),
54
+ reply_parameters=ReplyParameters(event.message_id) if self.as_reply else None,
43
55
  )
44
56
 
45
57
 
@@ -11,7 +11,7 @@ class ABCMiddleware(ABC, typing.Generic[T]):
11
11
  async def pre(self, event: T, ctx: Context) -> bool:
12
12
  ...
13
13
 
14
- async def post(self, event: T, responses: list[typing.Any], ctx: Context):
14
+ async def post(self, event: T, responses: list[typing.Any], ctx: Context) -> None:
15
15
  ...
16
16
 
17
17
 
@@ -17,7 +17,7 @@ if typing.TYPE_CHECKING:
17
17
  from telegrinder.bot.rules.abc import ABCRule
18
18
 
19
19
  T = typing.TypeVar("T", bound=BaseCute)
20
- _ = typing.Any
20
+ _: typing.TypeAlias = typing.Any
21
21
 
22
22
 
23
23
  async def process_inner(
@@ -36,15 +36,17 @@ async def process_inner(
36
36
 
37
37
  found = False
38
38
  responses = []
39
+ ctx_copy = ctx.copy()
40
+
39
41
  for handler in handlers:
40
42
  if await handler.check(event.api, raw_event, ctx):
41
43
  found = True
42
- handler.ctx |= ctx
43
- response = await handler.run(event)
44
+ response = await handler.run(event, ctx)
44
45
  responses.append(response)
45
46
  await return_manager.run(response, event, ctx)
46
47
  if handler.is_blocking:
47
48
  break
49
+ ctx = ctx_copy
48
50
 
49
51
  for middleware in middlewares:
50
52
  await middleware.post(event, responses, ctx)
@@ -1,3 +1,5 @@
1
+ import typing
2
+
1
3
  from telegrinder.bot.cute_types import CallbackQueryCute
2
4
  from telegrinder.bot.dispatch.context import Context
3
5
 
@@ -12,7 +14,7 @@ class CallbackQueryReturnManager(BaseReturnManager[CallbackQueryCute]):
12
14
 
13
15
  @register_manager(dict)
14
16
  @staticmethod
15
- async def dict_manager(value: dict, event: CallbackQueryCute, ctx: Context) -> None:
17
+ async def dict_manager(value: dict[str, typing.Any], event: CallbackQueryCute, ctx: Context) -> None:
16
18
  await event.answer(**value)
17
19
 
18
20
 
@@ -1,3 +1,5 @@
1
+ import typing
2
+
1
3
  from telegrinder.bot.cute_types import InlineQueryCute
2
4
  from telegrinder.bot.dispatch.context import Context
3
5
 
@@ -7,7 +9,7 @@ from .abc import BaseReturnManager, register_manager
7
9
  class InlineQueryReturnManager(BaseReturnManager[InlineQueryCute]):
8
10
  @register_manager(dict)
9
11
  @staticmethod
10
- async def dict_manager(value: dict, event: InlineQueryCute, ctx: Context) -> None:
12
+ async def dict_manager(value: dict[str, typing.Any], event: InlineQueryCute, ctx: Context) -> None:
11
13
  await event.answer(**value)
12
14
 
13
15
 
@@ -1,5 +1,8 @@
1
+ import typing
2
+
1
3
  from telegrinder.bot.cute_types import MessageCute
2
4
  from telegrinder.bot.dispatch.context import Context
5
+ from telegrinder.tools.formatting import HTMLFormatter
3
6
 
4
7
  from .abc import BaseReturnManager, register_manager
5
8
 
@@ -18,8 +21,13 @@ class MessageReturnManager(BaseReturnManager[MessageCute]):
18
21
 
19
22
  @register_manager(dict)
20
23
  @staticmethod
21
- async def dict_manager(value: dict, event: MessageCute, ctx: Context) -> None:
24
+ async def dict_manager(value: dict[str, typing.Any], event: MessageCute, ctx: Context) -> None:
22
25
  await event.answer(**value)
23
26
 
27
+ @register_manager(HTMLFormatter)
28
+ @staticmethod
29
+ async def htmlformatter_manager(value: HTMLFormatter, event: MessageCute, ctx: Context) -> None:
30
+ await event.answer(value, parse_mode=HTMLFormatter.PARSE_MODE)
31
+
24
32
 
25
33
  __all__ = ("MessageReturnManager",)
@@ -6,16 +6,18 @@ from fntypes.co import Nothing, Some
6
6
  from telegrinder.api.abc import ABCAPI
7
7
  from telegrinder.bot.cute_types.base import BaseCute
8
8
  from telegrinder.bot.dispatch.handler.abc import ABCHandler
9
- from telegrinder.bot.dispatch.handler.func import ErrorHandlerT, FuncHandler
9
+ from telegrinder.bot.dispatch.handler.func import FuncHandler
10
10
  from telegrinder.bot.dispatch.middleware.abc import ABCMiddleware
11
11
  from telegrinder.bot.dispatch.process import process_inner
12
12
  from telegrinder.bot.dispatch.return_manager.abc import ABCReturnManager
13
13
  from telegrinder.bot.rules.abc import ABCRule
14
14
  from telegrinder.model import Model
15
15
  from telegrinder.msgspec_utils import Option
16
+ from telegrinder.tools.error_handler.error_handler import ABCErrorHandler, ErrorHandler
16
17
  from telegrinder.types.objects import Update
17
18
 
18
19
  EventType = typing.TypeVar("EventType", bound=BaseCute)
20
+ ErrorHandlerT = typing.TypeVar("ErrorHandlerT", bound=ABCErrorHandler)
19
21
  MiddlewareT = typing.TypeVar("MiddlewareT", bound=ABCMiddleware)
20
22
 
21
23
  FuncType: typing.TypeAlias = typing.Callable[
@@ -74,20 +76,44 @@ class BaseView(ABCView, typing.Generic[EventType]):
74
76
  return getattr(update, update_type.value)
75
77
  case _:
76
78
  return Nothing()
79
+
80
+ @typing.overload
81
+ def __call__(
82
+ self,
83
+ *rules: ABCRule[EventType],
84
+ ) -> typing.Callable[[FuncType[EventType]], FuncHandler[EventType, FuncType[EventType], ErrorHandler]]:
85
+ ...
86
+
87
+ @typing.overload
88
+ def __call__(
89
+ self,
90
+ *rules: ABCRule[EventType],
91
+ error_handler: ErrorHandlerT,
92
+ is_blocking: bool = True,
93
+ ) -> typing.Callable[[FuncType[EventType]], FuncHandler[EventType, FuncType[EventType], ErrorHandlerT]]:
94
+ ...
77
95
 
96
+ @typing.overload
78
97
  def __call__(
79
98
  self,
80
99
  *rules: ABCRule[EventType],
100
+ error_handler: typing.Literal[None] = None,
101
+ is_blocking: bool = True,
102
+ ) -> typing.Callable[[FuncType[EventType]], FuncHandler[EventType, FuncType[EventType], ErrorHandler]]:
103
+ ...
104
+
105
+ def __call__( # type: ignore
106
+ self,
107
+ *rules: ABCRule[EventType],
108
+ error_handler: ABCErrorHandler | None = None,
81
109
  is_blocking: bool = True,
82
- error_handler: ErrorHandlerT | None = None,
83
110
  ):
84
111
  def wrapper(func: FuncType[EventType]):
85
- func_handler = FuncHandler[EventType, FuncType[EventType], ErrorHandlerT](
112
+ func_handler = FuncHandler(
86
113
  func,
87
114
  [*self.auto_rules, *rules],
88
- is_blocking,
115
+ is_blocking=is_blocking,
89
116
  dataclass=None,
90
- error_handler=error_handler,
91
117
  )
92
118
  self.handlers.append(func_handler)
93
119
  return func_handler
@@ -18,19 +18,20 @@ MessageViewT = typing.TypeVar("MessageViewT", bound=ABCView, default=MessageView
18
18
 
19
19
  @dataclasses.dataclass(kw_only=True)
20
20
  class ViewBox(typing.Generic[CallbackQueryViewT, InlineQueryViewT, MessageViewT]):
21
- callback_query: CallbackQueryViewT = dataclasses.field( # type: ignore
22
- default_factory=lambda: CallbackQueryView(),
21
+ callback_query: CallbackQueryViewT = dataclasses.field(
22
+ default_factory=lambda: typing.cast(CallbackQueryViewT, CallbackQueryView()),
23
23
  )
24
- inline_query: InlineQueryViewT = dataclasses.field( # type: ignore
25
- default_factory=lambda: InlineQueryView(),
24
+ inline_query: InlineQueryViewT = dataclasses.field(
25
+ default_factory=lambda: typing.cast(InlineQueryViewT, InlineQueryView()),
26
26
  )
27
- message: MessageViewT = dataclasses.field( # type: ignore
28
- default_factory=lambda: MessageView(),
27
+ message: MessageViewT = dataclasses.field(
28
+ default_factory=lambda: typing.cast(MessageViewT, MessageView()),
29
29
  )
30
30
 
31
31
  def get_views(self) -> dict[str, ABCView]:
32
32
  return {
33
- name: view for name, view in self.__dict__.items()
33
+ name: view
34
+ for name, view in self.__dict__.items()
34
35
  if isinstance(view, ABCView)
35
36
  }
36
37
 
@@ -12,7 +12,7 @@ class InlineQueryView(BaseStateView[InlineQueryCute]):
12
12
  self.return_manager = InlineQueryReturnManager()
13
13
 
14
14
  def get_state_key(self, event: InlineQueryCute) -> int | None:
15
- return event.from_.id
15
+ return event.from_user.id
16
16
 
17
17
 
18
18
  __all__ = ("InlineQueryView",)
@@ -12,7 +12,7 @@ class MessageView(BaseStateView[MessageCute]):
12
12
  self.return_manager = MessageReturnManager()
13
13
 
14
14
  def get_state_key(self, event: MessageCute) -> int | None:
15
- return event.chat.id
15
+ return event.chat_id
16
16
 
17
17
 
18
18
  __all__ = ("MessageView",)