telegrinder 0.1.dev167__tar.gz → 0.1.dev168__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 (137) hide show
  1. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/PKG-INFO +5 -5
  2. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/pyproject.toml +13 -5
  3. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/readme.md +4 -4
  4. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/cute_types/callback_query.py +1 -1
  5. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/dispatch/dispatch.py +4 -3
  6. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/dispatch/handler/func.py +1 -1
  7. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/dispatch/view/abc.py +53 -2
  8. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/dispatch/waiter_machine/machine.py +14 -20
  9. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/dispatch/waiter_machine/short_state.py +10 -0
  10. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/rules/callback_data.py +9 -11
  11. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/scenario/checkbox.py +5 -5
  12. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/scenario/choice.py +4 -3
  13. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/tools/limited_dict.py +12 -2
  14. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/types/methods.py +20 -89
  15. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/types/objects.py +12 -41
  16. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/LICENSE +0 -0
  17. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/__init__.py +0 -0
  18. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/api/__init__.py +0 -0
  19. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/api/abc.py +0 -0
  20. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/api/api.py +0 -0
  21. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/api/error.py +0 -0
  22. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/api/response.py +0 -0
  23. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/__init__.py +0 -0
  24. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/bot.py +0 -0
  25. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/cute_types/__init__.py +0 -0
  26. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/cute_types/base.py +0 -0
  27. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/cute_types/chat_join_request.py +0 -0
  28. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/cute_types/chat_member_updated.py +0 -0
  29. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/cute_types/inline_query.py +0 -0
  30. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/cute_types/message.py +0 -0
  31. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/cute_types/update.py +0 -0
  32. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/cute_types/utils.py +0 -0
  33. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/dispatch/__init__.py +0 -0
  34. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/dispatch/abc.py +0 -0
  35. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/dispatch/composition.py +0 -0
  36. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/dispatch/context.py +0 -0
  37. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/dispatch/handler/__init__.py +0 -0
  38. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/dispatch/handler/abc.py +0 -0
  39. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/dispatch/handler/message_reply.py +0 -0
  40. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/dispatch/middleware/__init__.py +0 -0
  41. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/dispatch/middleware/abc.py +0 -0
  42. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/dispatch/process.py +0 -0
  43. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/dispatch/return_manager/__init__.py +0 -0
  44. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/dispatch/return_manager/abc.py +0 -0
  45. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/dispatch/return_manager/callback_query.py +0 -0
  46. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/dispatch/return_manager/inline_query.py +0 -0
  47. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/dispatch/return_manager/message.py +0 -0
  48. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/dispatch/view/__init__.py +0 -0
  49. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/dispatch/view/box.py +0 -0
  50. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/dispatch/view/callback_query.py +0 -0
  51. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/dispatch/view/chat_join_request.py +0 -0
  52. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/dispatch/view/chat_member.py +0 -0
  53. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/dispatch/view/inline_query.py +0 -0
  54. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/dispatch/view/message.py +0 -0
  55. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/dispatch/view/raw.py +0 -0
  56. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/dispatch/waiter_machine/__init__.py +0 -0
  57. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/dispatch/waiter_machine/middleware.py +0 -0
  58. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/polling/__init__.py +0 -0
  59. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/polling/abc.py +0 -0
  60. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/polling/polling.py +0 -0
  61. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/rules/__init__.py +0 -0
  62. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/rules/abc.py +0 -0
  63. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/rules/adapter/__init__.py +0 -0
  64. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/rules/adapter/abc.py +0 -0
  65. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/rules/adapter/errors.py +0 -0
  66. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/rules/adapter/event.py +0 -0
  67. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/rules/adapter/raw_update.py +0 -0
  68. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/rules/chat_join.py +0 -0
  69. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/rules/command.py +0 -0
  70. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/rules/enum_text.py +0 -0
  71. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/rules/func.py +0 -0
  72. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/rules/fuzzy.py +0 -0
  73. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/rules/inline.py +0 -0
  74. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/rules/integer.py +0 -0
  75. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/rules/is_from.py +0 -0
  76. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/rules/markup.py +0 -0
  77. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/rules/mention.py +0 -0
  78. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/rules/message.py +0 -0
  79. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/rules/message_entities.py +0 -0
  80. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/rules/regex.py +0 -0
  81. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/rules/rule_enum.py +0 -0
  82. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/rules/start.py +0 -0
  83. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/rules/text.py +0 -0
  84. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/rules/update.py +0 -0
  85. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/scenario/__init__.py +0 -0
  86. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/bot/scenario/abc.py +0 -0
  87. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/client/__init__.py +0 -0
  88. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/client/abc.py +0 -0
  89. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/client/aiohttp.py +0 -0
  90. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/model.py +0 -0
  91. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/modules.py +0 -0
  92. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/msgspec_json.py +0 -0
  93. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/msgspec_utils.py +0 -0
  94. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/node/__init__.py +0 -0
  95. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/node/attachment.py +0 -0
  96. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/node/base.py +0 -0
  97. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/node/composer.py +0 -0
  98. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/node/container.py +0 -0
  99. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/node/message.py +0 -0
  100. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/node/rule.py +0 -0
  101. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/node/source.py +0 -0
  102. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/node/text.py +0 -0
  103. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/node/tools/__init__.py +0 -0
  104. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/node/tools/generator.py +0 -0
  105. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/node/update.py +0 -0
  106. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/rules.py +0 -0
  107. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/tools/__init__.py +1 -1
  108. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/tools/buttons.py +0 -0
  109. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/tools/error_handler/__init__.py +0 -0
  110. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/tools/error_handler/abc.py +0 -0
  111. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/tools/error_handler/error.py +0 -0
  112. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/tools/error_handler/error_handler.py +0 -0
  113. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/tools/formatting/__init__.py +0 -0
  114. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/tools/formatting/html.py +0 -0
  115. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/tools/formatting/links.py +0 -0
  116. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/tools/formatting/spec_html_formats.py +0 -0
  117. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/tools/global_context/__init__.py +0 -0
  118. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/tools/global_context/abc.py +0 -0
  119. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/tools/global_context/global_context.py +0 -0
  120. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/tools/global_context/telegrinder_ctx.py +0 -0
  121. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/tools/i18n/__init__.py +0 -0
  122. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/tools/i18n/base.py +0 -0
  123. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/tools/i18n/middleware/__init__.py +0 -0
  124. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/tools/i18n/middleware/base.py +0 -0
  125. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/tools/i18n/simple.py +0 -0
  126. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/tools/kb_set/__init__.py +0 -0
  127. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/tools/kb_set/base.py +0 -0
  128. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/tools/kb_set/yaml.py +0 -0
  129. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/tools/keyboard.py +0 -0
  130. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/tools/loop_wrapper/__init__.py +0 -0
  131. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/tools/loop_wrapper/abc.py +0 -0
  132. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/tools/loop_wrapper/loop_wrapper.py +0 -0
  133. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/tools/magic.py +0 -0
  134. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/tools/parse_mode.py +0 -0
  135. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/types/__init__.py +0 -0
  136. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/types/enums.py +0 -0
  137. {telegrinder-0.1.dev167 → telegrinder-0.1.dev168}/telegrinder/verification_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: telegrinder
3
- Version: 0.1.dev167
3
+ Version: 0.1.dev168
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
@@ -49,23 +49,23 @@ Still in development.
49
49
 
50
50
  Install using pip:
51
51
 
52
- ```
52
+ ```console
53
53
  pip install telegrinder
54
54
  ```
55
55
 
56
56
  Using poetry:
57
57
 
58
- ```
58
+ ```console
59
59
  poetry add telegrinder
60
60
  ```
61
61
 
62
62
  Install from github:
63
63
 
64
- ```
64
+ ```console
65
65
  pip install -U https://github.com/timoniq/telegrinder/archive/dev.zip
66
66
  ```
67
67
 
68
- ```
68
+ ```console
69
69
  poetry add git+https://github.com/timoniq/telegrinder.git#dev
70
70
  ```
71
71
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "telegrinder"
3
- version = "0.1.dev167"
3
+ version = "0.1.dev168"
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>"]
@@ -46,6 +46,11 @@ typing-extensions = "^4.10.0"
46
46
  fntypes = "^0.1.2.post1"
47
47
 
48
48
  [tool.poetry.group.dev.dependencies]
49
+ ruff = "^0.4.4"
50
+ basedpyright = "^1.12.1"
51
+ isort = "5.13.2"
52
+ sort-all = "1.2.0"
53
+ black = "^24.4.2"
49
54
  pytest = "^8.0.0"
50
55
  pytest-asyncio = "^0.23.5"
51
56
  pytest-cov = "^4.0.0"
@@ -58,7 +63,7 @@ requires = ["poetry-core>=1.0.0"]
58
63
  build-backend = "poetry.core.masonry.api"
59
64
 
60
65
  [tool.ruff]
61
- line-length = 100
66
+ line-length = 110
62
67
  target-version = "py311"
63
68
  lint.select = ["I", "N", "ANN", "COM", "YTT", "TCH", "SIM"]
64
69
  lint.ignore = [
@@ -67,8 +72,8 @@ lint.ignore = [
67
72
  ]
68
73
 
69
74
  [tool.black]
70
- line-length = 90
71
- target-version = ["py311"]
75
+ line-length = 110
76
+ target-version = ["py311", "py312"]
72
77
 
73
78
  [tool.pytest.ini_options]
74
79
  asyncio_mode = "auto"
@@ -77,4 +82,7 @@ asyncio_mode = "auto"
77
82
  "__init__.py" = ["F401", "F403"]
78
83
 
79
84
  [tool.ruff.lint.flake8-quotes]
80
- docstring-quotes = "double"
85
+ docstring-quotes = "double"
86
+
87
+ [tool.basedpyright]
88
+ typeCheckingMode = "basic"
@@ -14,23 +14,23 @@ Still in development.
14
14
 
15
15
  Install using pip:
16
16
 
17
- ```
17
+ ```console
18
18
  pip install telegrinder
19
19
  ```
20
20
 
21
21
  Using poetry:
22
22
 
23
- ```
23
+ ```console
24
24
  poetry add telegrinder
25
25
  ```
26
26
 
27
27
  Install from github:
28
28
 
29
- ```
29
+ ```console
30
30
  pip install -U https://github.com/timoniq/telegrinder/archive/dev.zip
31
31
  ```
32
32
 
33
- ```
33
+ ```console
34
34
  poetry add git+https://github.com/timoniq/telegrinder.git#dev
35
35
  ```
36
36
 
@@ -217,7 +217,7 @@ class CallbackQueryCute(BaseCute[CallbackQuery], CallbackQuery, kw_only=True, di
217
217
  )
218
218
  async def edit_text(
219
219
  self,
220
- text: str | None,
220
+ text: str,
221
221
  inline_message_id: int | None = None,
222
222
  chat_id: int | str | None = None,
223
223
  message_id: int | None = None,
@@ -135,7 +135,9 @@ class Dispatch(
135
135
 
136
136
  async def feed(self, event: Update, api: ABCAPI) -> bool:
137
137
  logger.debug("Processing update (update_id={})", event.update_id)
138
- await self.raw_event.process(event, api)
138
+ loop = asyncio.get_running_loop()
139
+ loop.create_task(self.raw_event.process(event, api))
140
+
139
141
  for view in self.get_views().values():
140
142
  if await view.check(event):
141
143
  logger.debug(
@@ -143,10 +145,9 @@ class Dispatch(
143
145
  event.update_id,
144
146
  view.__class__.__name__,
145
147
  )
146
- await view.process(event, api)
148
+ loop.create_task(view.process(event, api))
147
149
  return True
148
150
 
149
- loop = asyncio.get_running_loop()
150
151
  ctx = Context()
151
152
  found = False
152
153
  for handler in self.default_handlers:
@@ -46,7 +46,7 @@ class FuncHandler(ABCHandler[EventT], typing.Generic[EventT, F, ErrorHandlerT]):
46
46
  self.dataclass,
47
47
  self.error_handler,
48
48
  )
49
-
49
+
50
50
  async def check(self, api: ABCAPI, event: Update, ctx: Context | None = None) -> bool:
51
51
  if self.update_type is not None and self.update_type != event.update_type.unwrap_or_none():
52
52
  return False
@@ -67,13 +67,64 @@ class BaseView(ABCView, typing.Generic[EventType]):
67
67
  return Some(generic_type)
68
68
  return Nothing()
69
69
 
70
- @classmethod
71
- def get_raw_event(cls, update: Update) -> Option[Model]:
70
+ @staticmethod
71
+ def get_raw_event(update: Update) -> Option[Model]:
72
72
  match update.update_type:
73
73
  case Some(update_type):
74
74
  return getattr(update, update_type.value)
75
75
  case _:
76
76
  return Nothing()
77
+
78
+ @typing.overload
79
+ @classmethod
80
+ def to_handler(
81
+ cls,
82
+ *rules: ABCRule[EventType],
83
+ ) -> typing.Callable[
84
+ [FuncType[EventType]],
85
+ FuncHandler[EventType, FuncType[EventType], ErrorHandler[EventType]],
86
+ ]: ...
87
+
88
+ @typing.overload
89
+ @classmethod
90
+ def to_handler(
91
+ cls,
92
+ *rules: ABCRule[EventType],
93
+ error_handler: ErrorHandlerT,
94
+ is_blocking: bool = True,
95
+ ) -> typing.Callable[
96
+ [FuncType[EventType]], FuncHandler[EventType, FuncType[EventType], ErrorHandlerT]
97
+ ]: ...
98
+
99
+ @typing.overload
100
+ @classmethod
101
+ def to_handler(
102
+ cls,
103
+ *rules: ABCRule[EventType],
104
+ error_handler: typing.Literal[None] = None,
105
+ is_blocking: bool = True,
106
+ ) -> typing.Callable[
107
+ [FuncType[EventType]],
108
+ FuncHandler[EventType, FuncType[EventType], ErrorHandler[EventType]],
109
+ ]: ...
110
+
111
+ @classmethod
112
+ def to_handler( # type: ignore
113
+ cls,
114
+ *rules: ABCRule[EventType],
115
+ error_handler: ABCErrorHandler | None = None,
116
+ is_blocking: bool = True,
117
+ ):
118
+ def wrapper(func: FuncType[EventType]):
119
+ return FuncHandler(
120
+ func,
121
+ list(rules),
122
+ is_blocking=is_blocking,
123
+ dataclass=None,
124
+ error_handler=error_handler or ErrorHandler(),
125
+ )
126
+
127
+ return wrapper
77
128
 
78
129
  @typing.overload
79
130
  def __call__(
@@ -39,7 +39,7 @@ class WaiterMachine:
39
39
  ) -> None:
40
40
  view_name = state_view.__class__.__name__
41
41
  if view_name not in self.storage:
42
- raise LookupError("No record of view {!r} found".format(view_name))
42
+ raise LookupError("No record of view {!r} found.".format(view_name))
43
43
 
44
44
  short_state = self.storage[view_name].pop(id, None)
45
45
  if short_state is None:
@@ -49,13 +49,7 @@ class WaiterMachine:
49
49
  )
50
50
  )
51
51
 
52
- waiters = typing.cast(
53
- typing.Iterable[asyncio.Future[typing.Any]],
54
- short_state.event._waiters, # type: ignore
55
- )
56
- for future in waiters:
57
- future.cancel()
58
-
52
+ short_state.cancel()
59
53
  await self.call_behaviour(
60
54
  state_view,
61
55
  short_state.event,
@@ -69,9 +63,9 @@ class WaiterMachine:
69
63
  state_view: "BaseStateView[EventModel]",
70
64
  linked: EventModel | tuple[ABCAPI, Identificator],
71
65
  *rules: ABCRule[EventModel],
72
- default: Behaviour = None,
73
- on_drop: Behaviour = None,
74
- expiration: datetime.timedelta | int | None = None,
66
+ default: Behaviour[EventModel] | None = None,
67
+ on_drop: Behaviour[EventModel] | None = None,
68
+ expiration: datetime.timedelta | float | None = None,
75
69
  ) -> tuple[EventModel, Context]:
76
70
  if isinstance(expiration, int | float):
77
71
  expiration = datetime.timedelta(seconds=expiration)
@@ -86,6 +80,7 @@ class WaiterMachine:
86
80
  if not key:
87
81
  raise RuntimeError("Unable to get state key.")
88
82
 
83
+ view_name = state_view.__class__.__name__
89
84
  event = asyncio.Event()
90
85
  short_state = ShortState(
91
86
  key,
@@ -96,17 +91,17 @@ class WaiterMachine:
96
91
  default_behaviour=default,
97
92
  on_drop_behaviour=on_drop,
98
93
  )
99
- view_name = state_view.__class__.__name__
94
+
100
95
  if view_name not in self.storage:
101
96
  state_view.middlewares.insert(0, WaiterMiddleware(self, state_view))
102
97
  self.storage[view_name] = LimitedDict()
103
98
 
104
- self.storage[view_name][key] = short_state
99
+ if (deleted_short_state := self.storage[view_name].set(key, short_state)) is not None:
100
+ deleted_short_state.cancel()
101
+
105
102
  await event.wait()
106
-
107
- e, ctx = getattr(event, "context")
108
103
  self.storage[view_name].pop(key, None)
109
- return e, ctx
104
+ return getattr(event, "context")
110
105
 
111
106
  async def call_behaviour(
112
107
  self,
@@ -118,8 +113,10 @@ class WaiterMachine:
118
113
  ) -> None:
119
114
  # TODO: support param view as a behaviour
120
115
 
121
- ctx = Context(**context)
116
+ if behaviour is None:
117
+ return
122
118
 
119
+ ctx = Context(**context)
123
120
  if isinstance(event, asyncio.Event):
124
121
  event, preset_ctx = typing.cast(
125
122
  tuple[EventModel, Context],
@@ -127,9 +124,6 @@ class WaiterMachine:
127
124
  )
128
125
  ctx.update(preset_ctx)
129
126
 
130
- if behaviour is None:
131
- return
132
-
133
127
  if await behaviour.check(event.api, update, ctx):
134
128
  await behaviour.run(event, ctx)
135
129
 
@@ -34,6 +34,16 @@ class ShortState(typing.Generic[EventModel]):
34
34
 
35
35
  def __post_init__(self, expiration: datetime.timedelta | None = None) -> None:
36
36
  self.expiration_date = (datetime.datetime.now() - expiration) if expiration is not None else None
37
+
38
+ def cancel(self) -> None:
39
+ """Cancel schedule waiters."""
40
+
41
+ waiters = typing.cast(
42
+ typing.Iterable[asyncio.Future[typing.Any]],
43
+ self.event._waiters, # type: ignore
44
+ )
45
+ for future in waiters:
46
+ future.cancel()
37
47
 
38
48
 
39
49
  __all__ = ("ShortState",)
@@ -16,10 +16,8 @@ from .markup import Markup, PatternLike, check_string
16
16
 
17
17
  CallbackQuery: typing.TypeAlias = CallbackQueryCute
18
18
  Validator: typing.TypeAlias = typing.Callable[[typing.Any], bool | typing.Awaitable[bool]]
19
- MapDict: typing.TypeAlias = dict[
20
- str, "typing.Any | type[typing.Any] | Validator | list[MapDict] | MapDict"
21
- ]
22
- CallbackMap: typing.TypeAlias = list[tuple[str, "typing.Any | type | Validator | CallbackMap"]]
19
+ MapDict: typing.TypeAlias = dict[str, "typing.Any | type[typing.Any] | Validator | list[MapDict] | MapDict"]
20
+ CallbackMap: typing.TypeAlias = list[tuple[str, "typing.Any | type[typing.Any] | Validator | CallbackMap"]]
23
21
  CallbackMapStrict: typing.TypeAlias = list[tuple[str, "Validator | CallbackMapStrict"]]
24
22
 
25
23
 
@@ -41,7 +39,7 @@ class CallbackQueryDataRule(CallbackQueryRule, abc.ABC, requires=[HasData()]):
41
39
 
42
40
 
43
41
  class CallbackDataMap(CallbackQueryDataRule):
44
- def __init__(self, mapping: MapDict) -> None:
42
+ def __init__(self, mapping: MapDict, /) -> None:
45
43
  self.mapping = self.transform_to_callbacks(
46
44
  self.transform_to_map(mapping),
47
45
  )
@@ -86,12 +84,12 @@ class CallbackDataMap(CallbackQueryDataRule):
86
84
  result = validator(value)
87
85
  if inspect.isawaitable(result):
88
86
  result = await result
89
- return result # type: ignore
87
+ return result
90
88
 
91
89
  return False
92
90
 
93
91
  @classmethod
94
- async def match(cls, callback_data: dict, callback_map: CallbackMapStrict) -> bool:
92
+ async def match(cls, callback_data: dict[str, typing.Any], callback_map: CallbackMapStrict) -> bool:
95
93
  """Matches callback_data with callback_map recursively."""
96
94
 
97
95
  for key, validator in callback_map:
@@ -121,7 +119,7 @@ class CallbackDataMap(CallbackQueryDataRule):
121
119
 
122
120
 
123
121
  class CallbackDataEq(CallbackQueryDataRule):
124
- def __init__(self, value: str):
122
+ def __init__(self, value: str, /) -> None:
125
123
  self.value = value
126
124
 
127
125
  async def check(self, event: CallbackQuery, ctx: Context) -> bool:
@@ -129,7 +127,7 @@ class CallbackDataEq(CallbackQueryDataRule):
129
127
 
130
128
 
131
129
  class CallbackDataJsonEq(CallbackQueryDataRule):
132
- def __init__(self, d: dict[str, typing.Any]):
130
+ def __init__(self, d: dict[str, typing.Any], /) -> None:
133
131
  self.d = d
134
132
 
135
133
  async def check(self, event: CallbackQuery, ctx: Context) -> bool:
@@ -137,7 +135,7 @@ class CallbackDataJsonEq(CallbackQueryDataRule):
137
135
 
138
136
 
139
137
  class CallbackDataJsonModel(CallbackQueryDataRule):
140
- def __init__(self, model: type[msgspec.Struct] | type[DataclassInstance]):
138
+ def __init__(self, model: type[msgspec.Struct] | type[DataclassInstance], /) -> None:
141
139
  self.model = model
142
140
 
143
141
  async def check(self, event: CallbackQuery, ctx: Context) -> bool:
@@ -148,7 +146,7 @@ class CallbackDataJsonModel(CallbackQueryDataRule):
148
146
 
149
147
 
150
148
  class CallbackDataMarkup(CallbackQueryDataRule):
151
- def __init__(self, patterns: PatternLike | list[PatternLike]):
149
+ def __init__(self, patterns: PatternLike | list[PatternLike], /) -> None:
152
150
  self.patterns = Markup(patterns).patterns
153
151
 
154
152
  async def check(self, event: CallbackQuery, ctx: Context) -> bool:
@@ -33,13 +33,13 @@ class Checkbox(ABCScenario[CallbackQueryCute]):
33
33
  self,
34
34
  waiter_machine: WaiterMachine,
35
35
  chat_id: int,
36
- msg: str,
36
+ message: str,
37
37
  *,
38
38
  ready_text: str = "Ready",
39
39
  max_in_row: int = 3,
40
40
  ) -> None:
41
41
  self.chat_id = chat_id
42
- self.msg = msg
42
+ self.message = message
43
43
  self.choices: list[Choice] = []
44
44
  self.ready = ready_text
45
45
  self.max_in_row = max_in_row
@@ -58,7 +58,7 @@ class Checkbox(ABCScenario[CallbackQueryCute]):
58
58
  self.waiter_machine,
59
59
  self.ready,
60
60
  self.chat_id,
61
- self.msg,
61
+ self.message,
62
62
  )
63
63
 
64
64
  def get_markup(self) -> InlineKeyboardMarkup:
@@ -101,7 +101,7 @@ class Checkbox(ABCScenario[CallbackQueryCute]):
101
101
  # Toggle choice
102
102
  self.choices[i].is_picked = not self.choices[i].is_picked
103
103
  await cb.edit_text(
104
- text=self.msg,
104
+ text=self.message,
105
105
  parse_mode=self.PARSE_MODE,
106
106
  reply_markup=self.get_markup(),
107
107
  )
@@ -118,7 +118,7 @@ class Checkbox(ABCScenario[CallbackQueryCute]):
118
118
  message = (
119
119
  await api.send_message(
120
120
  chat_id=self.chat_id,
121
- text=self.msg,
121
+ text=self.message,
122
122
  parse_mode=self.PARSE_MODE,
123
123
  reply_markup=self.get_markup(),
124
124
  )
@@ -1,6 +1,7 @@
1
1
  import typing
2
2
 
3
3
  from telegrinder.bot.cute_types import CallbackQueryCute
4
+ from telegrinder.bot.dispatch.waiter_machine import WaiterMachine
4
5
 
5
6
  from .checkbox import Checkbox
6
7
 
@@ -22,9 +23,9 @@ class SingleChoice(Checkbox):
22
23
  if choice.code == code:
23
24
  self.choices[i].is_picked = True
24
25
  await cb.ctx_api.edit_message_text(
26
+ text=self.message,
25
27
  chat_id=cb.message.unwrap().v.chat.id,
26
28
  message_id=cb.message.unwrap().v.message_id,
27
- text=self.msg,
28
29
  parse_mode=self.PARSE_MODE,
29
30
  reply_markup=self.get_markup(),
30
31
  )
@@ -36,10 +37,10 @@ class SingleChoice(Checkbox):
36
37
  api: "API",
37
38
  view: "BaseStateView[CallbackQueryCute]",
38
39
  ) -> tuple[str, int]:
39
- if len([choice for choice in self.choices if choice.is_picked]) != 1:
40
+ if len(tuple(choice for choice in self.choices if choice.is_picked)) != 1:
40
41
  raise ValueError("Exactly one choice must be picked")
41
42
  choices, m_id = await super().wait(api, view)
42
- return list(choices.keys())[list(choices.values()).index(True)], m_id
43
+ return tuple(choices.keys())[tuple(choices.values()).index(True)], m_id
43
44
 
44
45
 
45
46
  __all__ = ("SingleChoice",)
@@ -11,12 +11,22 @@ class LimitedDict(UserDict[KT, VT]):
11
11
  self.maxlimit = maxlimit
12
12
  self.queue: deque[KT] = deque(maxlen=maxlimit)
13
13
 
14
- def __setitem__(self, key: KT, value: VT, /) -> None:
14
+ def set(self, key: KT, value: VT, /) -> VT | None:
15
+ """Set item in the dictionary.
16
+ Returns a value that was deleted when the limit in the dictionary
17
+ was reached, otherwise None.
18
+ """
19
+
20
+ deleted_item = None
15
21
  if len(self.queue) >= self.maxlimit:
16
- self.pop(self.queue.popleft(), None)
22
+ deleted_item = self.pop(self.queue.popleft(), None)
17
23
  if key not in self.queue:
18
24
  self.queue.append(key)
19
25
  super().__setitem__(key, value)
26
+ return deleted_item
27
+
28
+ def __setitem__(self, key: KT, value: VT, /) -> None:
29
+ self.set(key, value)
20
30
 
21
31
  def __delitem__(self, key: KT) -> None:
22
32
  if key in self.queue: