telegrinder 0.1.dev163__tar.gz → 0.1.dev165__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 (132) hide show
  1. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/PKG-INFO +1 -1
  2. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/pyproject.toml +1 -1
  3. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/__init__.py +36 -0
  4. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/bot.py +9 -0
  5. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/cute_types/message.py +1 -1
  6. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/abc.py +1 -1
  7. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/composition.py +15 -1
  8. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/context.py +8 -0
  9. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/dispatch.py +65 -28
  10. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/handler/abc.py +2 -4
  11. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/handler/func.py +31 -25
  12. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/handler/message_reply.py +21 -9
  13. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/middleware/abc.py +1 -1
  14. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/process.py +5 -3
  15. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/return_manager/abc.py +6 -0
  16. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/return_manager/callback_query.py +3 -1
  17. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/return_manager/inline_query.py +3 -1
  18. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/return_manager/message.py +9 -1
  19. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/view/abc.py +31 -5
  20. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/view/box.py +2 -1
  21. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/view/inline_query.py +1 -1
  22. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/view/message.py +1 -1
  23. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/waiter_machine/machine.py +14 -8
  24. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/waiter_machine/middleware.py +9 -7
  25. telegrinder-0.1.dev165/telegrinder/bot/dispatch/waiter_machine/short_state.py +36 -0
  26. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/polling/polling.py +14 -0
  27. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/rules/markup.py +1 -1
  28. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/rules/text.py +1 -1
  29. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/scenario/checkbox.py +16 -0
  30. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/model.py +10 -0
  31. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/msgspec_utils.py +13 -2
  32. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/node/base.py +1 -1
  33. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/node/composer.py +1 -1
  34. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/tools/__init__.py +2 -1
  35. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/tools/error_handler/abc.py +3 -3
  36. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/tools/error_handler/error_handler.py +21 -21
  37. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/tools/formatting/html.py +6 -12
  38. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/tools/formatting/links.py +12 -6
  39. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/tools/formatting/spec_html_formats.py +4 -5
  40. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/tools/global_context/global_context.py +2 -2
  41. telegrinder-0.1.dev165/telegrinder/tools/loop_wrapper/__init__.py +4 -0
  42. telegrinder-0.1.dev165/telegrinder/tools/loop_wrapper/abc.py +15 -0
  43. telegrinder-0.1.dev165/telegrinder/tools/loop_wrapper/loop_wrapper.py +178 -0
  44. telegrinder-0.1.dev165/telegrinder/verification_utils.py +31 -0
  45. telegrinder-0.1.dev163/telegrinder/bot/dispatch/waiter_machine/short_state.py +0 -37
  46. telegrinder-0.1.dev163/telegrinder/tools/loop_wrapper/__init__.py +0 -4
  47. telegrinder-0.1.dev163/telegrinder/tools/loop_wrapper/abc.py +0 -18
  48. telegrinder-0.1.dev163/telegrinder/tools/loop_wrapper/loop_wrapper.py +0 -132
  49. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/LICENSE +0 -0
  50. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/readme.md +0 -0
  51. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/api/__init__.py +0 -0
  52. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/api/abc.py +0 -0
  53. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/api/api.py +0 -0
  54. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/api/error.py +0 -0
  55. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/api/response.py +0 -0
  56. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/__init__.py +0 -0
  57. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/cute_types/__init__.py +0 -0
  58. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/cute_types/base.py +0 -0
  59. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/cute_types/callback_query.py +0 -0
  60. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/cute_types/inline_query.py +0 -0
  61. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/cute_types/update.py +0 -0
  62. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/cute_types/utils.py +0 -0
  63. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/__init__.py +0 -0
  64. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/handler/__init__.py +0 -0
  65. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/middleware/__init__.py +0 -0
  66. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/return_manager/__init__.py +0 -0
  67. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/view/__init__.py +0 -0
  68. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/view/callback_query.py +0 -0
  69. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/dispatch/waiter_machine/__init__.py +0 -0
  70. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/polling/__init__.py +0 -0
  71. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/polling/abc.py +0 -0
  72. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/rules/__init__.py +0 -0
  73. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/rules/abc.py +0 -0
  74. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/rules/adapter/__init__.py +0 -0
  75. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/rules/adapter/abc.py +0 -0
  76. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/rules/adapter/errors.py +0 -0
  77. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/rules/adapter/event.py +0 -0
  78. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/rules/adapter/raw_update.py +0 -0
  79. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/rules/callback_data.py +0 -0
  80. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/rules/command.py +0 -0
  81. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/rules/enum_text.py +0 -0
  82. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/rules/func.py +0 -0
  83. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/rules/fuzzy.py +0 -0
  84. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/rules/inline.py +0 -0
  85. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/rules/integer.py +0 -0
  86. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/rules/is_from.py +0 -0
  87. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/rules/mention.py +0 -0
  88. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/rules/message_entities.py +0 -0
  89. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/rules/regex.py +0 -0
  90. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/rules/rule_enum.py +0 -0
  91. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/rules/start.py +0 -0
  92. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/scenario/__init__.py +0 -0
  93. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/scenario/abc.py +0 -0
  94. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/bot/scenario/choice.py +0 -0
  95. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/client/__init__.py +0 -0
  96. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/client/abc.py +0 -0
  97. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/client/aiohttp.py +0 -0
  98. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/modules.py +0 -0
  99. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/msgspec_json.py +0 -0
  100. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/node/__init__.py +0 -0
  101. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/node/attachment.py +0 -0
  102. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/node/container.py +0 -0
  103. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/node/message.py +0 -0
  104. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/node/rule.py +0 -0
  105. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/node/source.py +0 -0
  106. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/node/text.py +0 -0
  107. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/node/tools/__init__.py +0 -0
  108. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/node/tools/generator.py +0 -0
  109. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/node/update.py +0 -0
  110. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/rules.py +0 -0
  111. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/tools/buttons.py +0 -0
  112. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/tools/error_handler/__init__.py +0 -0
  113. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/tools/error_handler/error.py +0 -0
  114. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/tools/formatting/__init__.py +0 -0
  115. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/tools/global_context/__init__.py +0 -0
  116. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/tools/global_context/abc.py +0 -0
  117. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/tools/global_context/telegrinder_ctx.py +0 -0
  118. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/tools/i18n/__init__.py +0 -0
  119. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/tools/i18n/base.py +0 -0
  120. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/tools/i18n/middleware/__init__.py +0 -0
  121. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/tools/i18n/middleware/base.py +0 -0
  122. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/tools/i18n/simple.py +0 -0
  123. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/tools/kb_set/__init__.py +0 -0
  124. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/tools/kb_set/base.py +0 -0
  125. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/tools/kb_set/yaml.py +0 -0
  126. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/tools/keyboard.py +0 -0
  127. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/tools/magic.py +0 -0
  128. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/tools/parse_mode.py +0 -0
  129. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/types/__init__.py +0 -0
  130. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/types/enums.py +0 -0
  131. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/types/methods.py +0 -0
  132. {telegrinder-0.1.dev163 → telegrinder-0.1.dev165}/telegrinder/types/objects.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: telegrinder
3
- Version: 0.1.dev163
3
+ Version: 0.1.dev165
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.dev163"
3
+ version = "0.1.dev165"
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>"]
@@ -1,3 +1,37 @@
1
+ """Telegrinder
2
+
3
+ Framework for effective and reliable telegram bot building.
4
+
5
+ * Type hinted
6
+ * Customizable and extensible
7
+ * Ready to use scenarios and rules
8
+ * Fast models built on msgspec
9
+ * Both low-level and high-level API
10
+
11
+ Basic example:
12
+
13
+ ```python
14
+ from telegrinder import API, Message, Telegrinder, Token
15
+ from telegrinder.modules import logger
16
+ from telegrinder.rules import Text
17
+
18
+ api = API(token=Token("123:token"))
19
+ bot = Telegrinder(api)
20
+ logger.set_level("INFO")
21
+
22
+
23
+ @bot.on.message(Text("/start"))
24
+ async def start(message: Message):
25
+ me = (await api.get_me()).unwrap()
26
+ await message.answer(
27
+ f"Hello, {message.from_user.full_name}! I'm {me.full_name}."
28
+ )
29
+
30
+
31
+ bot.run_forever()
32
+ ```
33
+ """
34
+
1
35
  import typing
2
36
 
3
37
  from .api import ABCAPI, API, APIError, APIResponse, Token
@@ -58,6 +92,7 @@ from .tools import (
58
92
  Keyboard,
59
93
  KeyboardSetBase,
60
94
  KeyboardSetYAML,
95
+ Lifespan,
61
96
  LoopWrapper,
62
97
  ParseMode,
63
98
  RowButtons,
@@ -123,6 +158,7 @@ __all__ = (
123
158
  "Keyboard",
124
159
  "KeyboardSetBase",
125
160
  "KeyboardSetYAML",
161
+ "Lifespan",
126
162
  "LoopWrapper",
127
163
  "Message",
128
164
  "MessageCute",
@@ -24,6 +24,15 @@ class Telegrinder(typing.Generic[DispatchT, PollingT, LoopWrapperT]):
24
24
  self.dispatch = typing.cast(DispatchT, dispatch or Dispatch())
25
25
  self.polling = typing.cast(PollingT, polling or Polling(api))
26
26
  self.loop_wrapper = typing.cast(LoopWrapperT, loop_wrapper or LoopWrapper())
27
+
28
+ def __repr__(self) -> str:
29
+ return "<{}: api={!r}, dispatch={!r}, polling={!r}, loop_wrapper={!r}>".format(
30
+ self.__class__.__name__,
31
+ self.api,
32
+ self.dispatch,
33
+ self.polling,
34
+ self.loop_wrapper,
35
+ )
27
36
 
28
37
  @property
29
38
  def on(self) -> DispatchT:
@@ -80,7 +80,7 @@ async def execute_method_answer(
80
80
  params["link_preview_options"] = compose_link_preview_options(
81
81
  **link_preview_options
82
82
  )
83
-
83
+
84
84
  result = await getattr(message.ctx_api, method_name)(**params)
85
85
  return result.map(
86
86
  lambda x: (
@@ -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
 
@@ -19,6 +21,7 @@ from .view.box import CallbackQueryViewT, InlineQueryViewT, MessageViewT, ViewBo
19
21
  T = typing.TypeVar("T")
20
22
  R = typing.TypeVar("R")
21
23
  P = typing.ParamSpec("P")
24
+ Handler = typing.Callable[typing.Concatenate[T, ...], typing.Coroutine[typing.Any, typing.Any, typing.Any]]
22
25
  Event = typing.TypeVar("Event", bound=BaseCute)
23
26
 
24
27
  DEFAULT_DATACLASS: typing.Final[type[Update]] = Update
@@ -47,17 +50,74 @@ class Dispatch(
47
50
  def patcher(self) -> Patcher:
48
51
  """Alias `patcher` to get `vbml.Patcher` from the global context"""
49
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
+ ...
50
60
 
61
+ @typing.overload
51
62
  def handle(
52
63
  self,
53
- *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,
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,
54
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,
55
110
  dataclass: type[typing.Any] = DEFAULT_DATACLASS,
56
111
  error_handler: ErrorHandlerT | None = None,
112
+ is_blocking: bool = True,
57
113
  ):
58
114
  def wrapper(func: typing.Callable):
59
115
  handler = FuncHandler(
60
- 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(),
61
121
  )
62
122
  self.default_handlers.append(handler)
63
123
  return handler
@@ -77,11 +137,12 @@ class Dispatch(
77
137
  return True
78
138
 
79
139
  loop = asyncio.get_running_loop()
140
+ ctx = Context()
80
141
  found = False
81
142
  for handler in self.default_handlers:
82
- if await handler.check(api, event):
143
+ if await handler.check(api, event, ctx):
83
144
  found = True
84
- loop.create_task(handler.run(event))
145
+ loop.create_task(handler.run(event, ctx))
85
146
  if handler.is_blocking:
86
147
  break
87
148
  return found
@@ -94,30 +155,6 @@ class Dispatch(
94
155
  ), f"View {name!r} is undefined in external dispatch."
95
156
  view.load(view_external[name])
96
157
  setattr(external, name, view)
97
-
98
- @classmethod
99
- def to_handler(
100
- cls,
101
- *rules: ABCRule[BaseCute],
102
- is_blocking: bool = True,
103
- error_handler: ErrorHandlerT | None = None,
104
- ):
105
- def wrapper(
106
- func: typing.Callable[typing.Concatenate[T, P], typing.Awaitable[R]]
107
- ) -> FuncHandler[
108
- BaseCute,
109
- typing.Callable[typing.Concatenate[T, P], typing.Awaitable[R]],
110
- ErrorHandlerT,
111
- ]:
112
- return FuncHandler(
113
- func,
114
- list(rules),
115
- is_blocking=is_blocking,
116
- dataclass=None,
117
- error_handler=error_handler,
118
- )
119
-
120
- return wrapper
121
158
 
122
159
 
123
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)
@@ -43,6 +43,12 @@ class ABCReturnManager(ABC, typing.Generic[EventT]):
43
43
 
44
44
 
45
45
  class BaseReturnManager(ABCReturnManager[EventT]):
46
+ def __repr__(self) -> str:
47
+ return "<{}: {}>".format(
48
+ self.__class__.__name__,
49
+ ", ".join(x.callback.__name__ + "=" + repr(x) for x in self.managers),
50
+ )
51
+
46
52
  @property
47
53
  def managers(self) -> list[Manager]:
48
54
  return [
@@ -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
@@ -30,7 +30,8 @@ class ViewBox(typing.Generic[CallbackQueryViewT, InlineQueryViewT, MessageViewT]
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",)