telegrinder 0.3.3__tar.gz → 0.3.4__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 (167) hide show
  1. {telegrinder-0.3.3 → telegrinder-0.3.4}/PKG-INFO +1 -1
  2. {telegrinder-0.3.3 → telegrinder-0.3.4}/pyproject.toml +1 -1
  3. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/cute_types/update.py +0 -5
  4. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/dispatch/middleware/abc.py +6 -0
  5. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/dispatch/process.py +30 -5
  6. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/dispatch/waiter_machine/machine.py +6 -1
  7. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/rules/abc.py +7 -7
  8. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/rules/adapter/__init__.py +9 -6
  9. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/rules/adapter/abc.py +4 -2
  10. telegrinder-0.3.4/telegrinder/bot/rules/adapter/event.py +65 -0
  11. telegrinder-0.3.4/telegrinder/bot/rules/adapter/raw_event.py +27 -0
  12. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/rules/adapter/raw_update.py +1 -1
  13. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/rules/callback_data.py +7 -7
  14. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/rules/chat_join.py +3 -3
  15. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/rules/inline.py +4 -4
  16. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/scenario/checkbox.py +16 -7
  17. telegrinder-0.3.4/telegrinder/bot/scenario/choice.py +51 -0
  18. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/model.py +39 -14
  19. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/modules.py +2 -2
  20. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/msgspec_utils.py +1 -1
  21. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/node/__init__.py +20 -20
  22. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/node/base.py +9 -9
  23. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/rules.py +5 -5
  24. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/tools/keyboard.py +4 -4
  25. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/types/objects.py +736 -2347
  26. telegrinder-0.3.3/telegrinder/bot/rules/adapter/event.py +0 -67
  27. telegrinder-0.3.3/telegrinder/bot/scenario/choice.py +0 -46
  28. {telegrinder-0.3.3 → telegrinder-0.3.4}/LICENSE +0 -0
  29. {telegrinder-0.3.3 → telegrinder-0.3.4}/readme.md +0 -0
  30. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/__init__.py +0 -0
  31. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/api/__init__.py +0 -0
  32. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/api/api.py +0 -0
  33. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/api/error.py +0 -0
  34. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/api/response.py +0 -0
  35. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/api/token.py +0 -0
  36. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/__init__.py +0 -0
  37. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/bot.py +0 -0
  38. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/cute_types/__init__.py +0 -0
  39. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/cute_types/base.py +0 -0
  40. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/cute_types/callback_query.py +0 -0
  41. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/cute_types/chat_join_request.py +0 -0
  42. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/cute_types/chat_member_updated.py +0 -0
  43. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/cute_types/inline_query.py +0 -0
  44. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/cute_types/message.py +0 -0
  45. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/cute_types/utils.py +0 -0
  46. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/dispatch/__init__.py +0 -0
  47. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/dispatch/abc.py +0 -0
  48. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/dispatch/context.py +0 -0
  49. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/dispatch/dispatch.py +0 -0
  50. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/dispatch/handler/__init__.py +0 -0
  51. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/dispatch/handler/abc.py +0 -0
  52. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/dispatch/handler/audio_reply.py +0 -0
  53. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/dispatch/handler/base.py +0 -0
  54. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/dispatch/handler/document_reply.py +0 -0
  55. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/dispatch/handler/func.py +0 -0
  56. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/dispatch/handler/media_group_reply.py +0 -0
  57. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/dispatch/handler/message_reply.py +0 -0
  58. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/dispatch/handler/photo_reply.py +0 -0
  59. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/dispatch/handler/sticker_reply.py +0 -0
  60. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/dispatch/handler/video_reply.py +0 -0
  61. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/dispatch/middleware/__init__.py +0 -0
  62. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/dispatch/return_manager/__init__.py +0 -0
  63. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/dispatch/return_manager/abc.py +0 -0
  64. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/dispatch/return_manager/callback_query.py +0 -0
  65. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/dispatch/return_manager/inline_query.py +0 -0
  66. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/dispatch/return_manager/message.py +0 -0
  67. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/dispatch/view/__init__.py +0 -0
  68. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/dispatch/view/abc.py +0 -0
  69. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/dispatch/view/base.py +0 -0
  70. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/dispatch/view/box.py +0 -0
  71. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/dispatch/view/callback_query.py +0 -0
  72. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/dispatch/view/chat_join_request.py +0 -0
  73. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/dispatch/view/chat_member.py +0 -0
  74. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/dispatch/view/inline_query.py +0 -0
  75. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/dispatch/view/message.py +0 -0
  76. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/dispatch/view/raw.py +0 -0
  77. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/dispatch/waiter_machine/__init__.py +0 -0
  78. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/dispatch/waiter_machine/actions.py +0 -0
  79. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/dispatch/waiter_machine/hasher/__init__.py +0 -0
  80. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/dispatch/waiter_machine/hasher/callback.py +0 -0
  81. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/dispatch/waiter_machine/hasher/hasher.py +0 -0
  82. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/dispatch/waiter_machine/hasher/message.py +0 -0
  83. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/dispatch/waiter_machine/hasher/state.py +0 -0
  84. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/dispatch/waiter_machine/middleware.py +0 -0
  85. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/dispatch/waiter_machine/short_state.py +0 -0
  86. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/polling/__init__.py +0 -0
  87. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/polling/abc.py +0 -0
  88. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/polling/polling.py +0 -0
  89. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/rules/__init__.py +0 -0
  90. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/rules/adapter/errors.py +0 -0
  91. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/rules/adapter/node.py +0 -0
  92. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/rules/command.py +0 -0
  93. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/rules/enum_text.py +0 -0
  94. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/rules/func.py +0 -0
  95. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/rules/fuzzy.py +0 -0
  96. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/rules/integer.py +0 -0
  97. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/rules/is_from.py +0 -0
  98. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/rules/markup.py +0 -0
  99. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/rules/mention.py +0 -0
  100. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/rules/message.py +0 -0
  101. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/rules/message_entities.py +0 -0
  102. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/rules/node.py +0 -0
  103. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/rules/regex.py +0 -0
  104. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/rules/rule_enum.py +0 -0
  105. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/rules/start.py +0 -0
  106. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/rules/state.py +0 -0
  107. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/rules/text.py +0 -0
  108. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/rules/update.py +0 -0
  109. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/scenario/__init__.py +0 -0
  110. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/bot/scenario/abc.py +0 -0
  111. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/client/__init__.py +0 -0
  112. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/client/abc.py +0 -0
  113. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/client/aiohttp.py +0 -0
  114. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/msgspec_json.py +0 -0
  115. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/node/attachment.py +0 -0
  116. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/node/callback_query.py +0 -0
  117. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/node/command.py +0 -0
  118. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/node/composer.py +0 -0
  119. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/node/container.py +0 -0
  120. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/node/event.py +0 -0
  121. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/node/me.py +0 -0
  122. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/node/message.py +0 -0
  123. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/node/polymorphic.py +0 -0
  124. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/node/rule.py +0 -0
  125. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/node/scope.py +0 -0
  126. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/node/source.py +0 -0
  127. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/node/text.py +0 -0
  128. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/node/tools/__init__.py +0 -0
  129. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/node/tools/generator.py +0 -0
  130. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/node/update.py +0 -0
  131. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/py.typed +0 -0
  132. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/tools/__init__.py +0 -0
  133. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/tools/buttons.py +0 -0
  134. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/tools/error_handler/__init__.py +0 -0
  135. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/tools/error_handler/abc.py +0 -0
  136. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/tools/error_handler/error.py +0 -0
  137. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/tools/error_handler/error_handler.py +0 -0
  138. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/tools/formatting/__init__.py +0 -0
  139. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/tools/formatting/html.py +0 -0
  140. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/tools/formatting/links.py +0 -0
  141. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/tools/formatting/spec_html_formats.py +0 -0
  142. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/tools/functional.py +0 -0
  143. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/tools/global_context/__init__.py +0 -0
  144. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/tools/global_context/abc.py +0 -0
  145. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/tools/global_context/global_context.py +0 -0
  146. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/tools/global_context/telegrinder_ctx.py +0 -0
  147. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/tools/i18n/__init__.py +0 -0
  148. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/tools/i18n/abc.py +0 -0
  149. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/tools/i18n/middleware/__init__.py +0 -0
  150. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/tools/i18n/middleware/abc.py +0 -0
  151. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/tools/i18n/simple.py +0 -0
  152. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/tools/kb_set/__init__.py +0 -0
  153. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/tools/kb_set/base.py +0 -0
  154. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/tools/kb_set/yaml.py +0 -0
  155. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/tools/limited_dict.py +0 -0
  156. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/tools/loop_wrapper/__init__.py +0 -0
  157. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/tools/loop_wrapper/abc.py +0 -0
  158. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/tools/loop_wrapper/loop_wrapper.py +0 -0
  159. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/tools/magic.py +0 -0
  160. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/tools/parse_mode.py +0 -0
  161. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/tools/state_storage/__init__.py +0 -0
  162. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/tools/state_storage/abc.py +0 -0
  163. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/tools/state_storage/memory.py +0 -0
  164. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/types/__init__.py +0 -0
  165. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/types/enums.py +0 -0
  166. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/types/methods.py +0 -0
  167. {telegrinder-0.3.3 → telegrinder-0.3.4}/telegrinder/verification_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: telegrinder
3
- Version: 0.3.3
3
+ Version: 0.3.4
4
4
  Summary: Modern visionary telegram bot framework.
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.3.3"
3
+ version = "0.3.4"
4
4
  description = "Modern visionary telegram bot framework."
5
5
  authors = ["timoniq <tesseradecades@mail.ru>"]
6
6
  maintainers = ["luwqz1 <howluwqz1@gmail.com>"]
@@ -1,5 +1,4 @@
1
1
  import typing
2
- from functools import cached_property
3
2
 
4
3
  from fntypes.co import Nothing, Some
5
4
 
@@ -96,10 +95,6 @@ class UpdateCute(BaseCute[Update], Update, kw_only=True):
96
95
  """Optional. A request to join the chat has been sent. The bot must have the can_invite_users
97
96
  administrator right in the chat to receive these updates."""
98
97
 
99
- @cached_property
100
- def incoming_update(self) -> Model:
101
- return getattr(self, self.update_type.value).unwrap()
102
-
103
98
  def get_event(self, event_model: type[EventModel]) -> Option[EventModel]:
104
99
  if isinstance(self.incoming_update, event_model):
105
100
  return Some(self.incoming_update)
@@ -3,11 +3,17 @@ from abc import ABC
3
3
 
4
4
  from telegrinder.bot.dispatch.context import Context
5
5
  from telegrinder.model import Model
6
+ from telegrinder.types.objects import Update
7
+
8
+ if typing.TYPE_CHECKING:
9
+ from telegrinder.bot.rules.adapter.abc import ABCAdapter
6
10
 
7
11
  Event = typing.TypeVar("Event", bound=Model)
8
12
 
9
13
 
10
14
  class ABCMiddleware(ABC, typing.Generic[Event]):
15
+ adapter: "ABCAdapter[Update, Event] | None" = None
16
+
11
17
  async def pre(self, event: Event, ctx: Context) -> bool: ...
12
18
 
13
19
  async def post(self, event: Event, responses: list[typing.Any], ctx: Context) -> None: ...
@@ -1,5 +1,7 @@
1
+ import inspect
1
2
  import typing
2
3
 
4
+ from fntypes.option import Nothing, Option, Some
3
5
  from fntypes.result import Error, Ok
4
6
 
5
7
  from telegrinder.api.api import API
@@ -16,10 +18,27 @@ from telegrinder.types.objects import Update
16
18
  if typing.TYPE_CHECKING:
17
19
  from telegrinder.bot.dispatch.handler.abc import ABCHandler
18
20
  from telegrinder.bot.rules.abc import ABCRule
21
+ from telegrinder.bot.rules.adapter.abc import ABCAdapter
19
22
 
23
+ T = typing.TypeVar("T")
20
24
  Event = typing.TypeVar("Event", bound=Model)
21
25
 
22
26
 
27
+ async def run_adapter(
28
+ adapter: "ABCAdapter[Update, T]",
29
+ api: API,
30
+ update: Update,
31
+ context: Context,
32
+ ) -> Option[T]:
33
+ adapt_result = adapter.adapt(api, update, context)
34
+ match await adapt_result if inspect.isawaitable(adapt_result) else adapt_result:
35
+ case Ok(value):
36
+ return Some(value)
37
+ case Error(err):
38
+ logger.debug("Adapter failed with error message: {!r}", str(err))
39
+ return Nothing()
40
+
41
+
23
42
  async def process_inner(
24
43
  api: API,
25
44
  event: Event,
@@ -34,6 +53,13 @@ async def process_inner(
34
53
 
35
54
  logger.debug("Run pre middlewares...")
36
55
  for middleware in middlewares:
56
+ if middleware.adapter is not None:
57
+ match await run_adapter(middleware.adapter, api, raw_event, ctx):
58
+ case Some(val):
59
+ event = val
60
+ case Nothing():
61
+ return False
62
+
37
63
  middleware_result = await middleware.pre(event, ctx)
38
64
  logger.debug("Middleware {!r} returned: {!r}", middleware.__class__.__qualname__, middleware_result)
39
65
  if middleware_result is False:
@@ -83,11 +109,10 @@ async def check_rule(
83
109
  Returns check result."""
84
110
 
85
111
  # Running adapter
86
- match await rule.adapter.adapt(api, update, ctx):
87
- case Ok(value):
88
- adapted_value = value
89
- case Error(err):
90
- logger.debug("Adapter failed with error message: {!r}", str(err))
112
+ match await run_adapter(rule.adapter, api, update, ctx):
113
+ case Some(val):
114
+ adapted_value = val
115
+ case Nothing():
91
116
  return False
92
117
 
93
118
  # Preparing update
@@ -45,10 +45,15 @@ class WaiterMachine:
45
45
  self.base_state_lifetime,
46
46
  )
47
47
 
48
+ def create_middleware(self, view: BaseStateView[EventModel]) -> WaiterMiddleware[EventModel]:
49
+ hasher = StateViewHasher(view)
50
+ self.storage[hasher] = LimitedDict(maxlimit=self.max_storage_size)
51
+ return WaiterMiddleware(self, hasher)
52
+
48
53
  async def drop_all(self) -> None:
49
54
  """Drops all waiters in storage."""
50
55
 
51
- for hasher in self.storage:
56
+ for hasher in self.storage.copy():
52
57
  for ident, short_state in self.storage[hasher].items():
53
58
  if short_state.context:
54
59
  await self.drop(hasher, ident)
@@ -202,12 +202,12 @@ class Always(ABCRule):
202
202
 
203
203
 
204
204
  __all__ = (
205
- "ABCRule",
206
- "Always",
207
- "AndRule",
208
- "CheckResult",
209
- "Never",
210
- "NotRule",
211
- "OrRule",
205
+ "ABCRule",
206
+ "Always",
207
+ "AndRule",
208
+ "CheckResult",
209
+ "Never",
210
+ "NotRule",
211
+ "OrRule",
212
212
  "with_caching_translations",
213
213
  )
@@ -1,14 +1,17 @@
1
- from telegrinder.bot.rules.adapter.abc import ABCAdapter, Event
1
+ from telegrinder.bot.rules.adapter.abc import ABCAdapter, AdaptResult, Event
2
2
  from telegrinder.bot.rules.adapter.errors import AdapterError
3
3
  from telegrinder.bot.rules.adapter.event import EventAdapter
4
4
  from telegrinder.bot.rules.adapter.node import NodeAdapter
5
+ from telegrinder.bot.rules.adapter.raw_event import RawEventAdapter
5
6
  from telegrinder.bot.rules.adapter.raw_update import RawUpdateAdapter
6
7
 
7
8
  __all__ = (
8
- "ABCAdapter",
9
- "AdapterError",
10
- "Event",
11
- "EventAdapter",
12
- "NodeAdapter",
9
+ "ABCAdapter",
10
+ "AdaptResult",
11
+ "AdapterError",
12
+ "Event",
13
+ "EventAdapter",
14
+ "NodeAdapter",
15
+ "RawEventAdapter",
13
16
  "RawUpdateAdapter",
14
17
  )
@@ -12,12 +12,14 @@ from telegrinder.model import Model
12
12
  From = typing.TypeVar("From", bound=Model)
13
13
  To = typing.TypeVar("To")
14
14
 
15
+ AdaptResult: typing.TypeAlias = Result[To, AdapterError] | typing.Awaitable[Result[To, AdapterError]]
16
+
15
17
 
16
18
  class ABCAdapter(abc.ABC, typing.Generic[From, To]):
17
19
  ADAPTED_VALUE_KEY: str | None = None
18
20
 
19
21
  @abc.abstractmethod
20
- async def adapt(self, api: API, update: From, context: Context) -> Result[To, AdapterError]:
22
+ def adapt(self, api: API, update: From, context: Context) -> AdaptResult[To]:
21
23
  pass
22
24
 
23
25
 
@@ -26,4 +28,4 @@ class Event(typing.Generic[To]):
26
28
  obj: To
27
29
 
28
30
 
29
- __all__ = ("ABCAdapter", "Event")
31
+ __all__ = ("ABCAdapter", "AdaptResult", "Event")
@@ -0,0 +1,65 @@
1
+ import typing
2
+
3
+ from fntypes.result import Error, Ok, Result
4
+
5
+ from telegrinder.api.api import API
6
+ from telegrinder.bot.cute_types.base import BaseCute
7
+ from telegrinder.bot.cute_types.update import UpdateCute
8
+ from telegrinder.bot.dispatch.context import Context
9
+ from telegrinder.bot.rules.adapter.abc import ABCAdapter
10
+ from telegrinder.bot.rules.adapter.errors import AdapterError
11
+ from telegrinder.bot.rules.adapter.raw_update import RawUpdateAdapter
12
+ from telegrinder.types.enums import UpdateType
13
+ from telegrinder.types.objects import Model, Update
14
+
15
+ ToCute = typing.TypeVar("ToCute", bound=BaseCute)
16
+
17
+
18
+ class EventAdapter(ABCAdapter[Update, ToCute]):
19
+ ADAPTED_VALUE_KEY: str = "_adapted_cute_event"
20
+
21
+ def __init__(self, event: UpdateType | type[Model], cute_model: type[ToCute]) -> None:
22
+ self.event = event
23
+ self.cute_model = cute_model
24
+
25
+ def __repr__(self) -> str:
26
+ raw_update_type = (
27
+ f"Update -> {self.event.__name__}"
28
+ if isinstance(self.event, type)
29
+ else f"Update.{self.event.value}"
30
+ )
31
+ return "<{}: adapt {} -> {}>".format(
32
+ self.__class__.__name__,
33
+ raw_update_type,
34
+ self.cute_model.__name__,
35
+ )
36
+
37
+ def get_event(self, update: UpdateCute) -> Model | None:
38
+ if isinstance(self.event, UpdateType) and self.event == update.update_type:
39
+ return update.incoming_update
40
+
41
+ if not isinstance(self.event, UpdateType) and (event := update.get_event(self.event)):
42
+ return event.unwrap()
43
+
44
+ return None
45
+
46
+ def adapt(self, api: API, update: Update, context: Context) -> Result[ToCute, AdapterError]:
47
+ match RawUpdateAdapter().adapt(api, update, context):
48
+ case Ok(update_cute) if event := self.get_event(update_cute):
49
+ if self.ADAPTED_VALUE_KEY in context:
50
+ return Ok(context[self.ADAPTED_VALUE_KEY])
51
+
52
+ adapted = (
53
+ typing.cast(ToCute, event)
54
+ if isinstance(event, BaseCute)
55
+ else self.cute_model.from_update(event, bound_api=api)
56
+ )
57
+ context[self.ADAPTED_VALUE_KEY] = adapted
58
+ return Ok(adapted)
59
+ case Error(_) as err:
60
+ return err
61
+ case _:
62
+ return Error(AdapterError(f"Update is not an {self.event!r}."))
63
+
64
+
65
+ __all__ = ("EventAdapter",)
@@ -0,0 +1,27 @@
1
+ from fntypes.result import Error, Ok, Result
2
+
3
+ from telegrinder.api.api import API
4
+ from telegrinder.bot.dispatch.context import Context
5
+ from telegrinder.bot.rules.adapter.abc import ABCAdapter
6
+ from telegrinder.bot.rules.adapter.errors import AdapterError
7
+ from telegrinder.model import Model
8
+ from telegrinder.types.objects import Update
9
+
10
+
11
+ class RawEventAdapter(ABCAdapter[Update, Model]):
12
+ def __init__(self, event_model: type[Model], /) -> None:
13
+ self.event_model = event_model
14
+
15
+ def __repr__(self) -> str:
16
+ return "<{}: adapt Update -> {}>".format(
17
+ self.__class__.__name__,
18
+ self.event_model.__name__,
19
+ )
20
+
21
+ def adapt(self, api: API, update: Update, context: Context) -> Result[Model, AdapterError]:
22
+ if isinstance(update.incoming_update, self.event_model):
23
+ return Ok(update.incoming_update)
24
+ return Error(AdapterError(f"Update is not an {self.event_model.__name__!r}."))
25
+
26
+
27
+ __all__ = ("RawEventAdapter",)
@@ -14,7 +14,7 @@ class RawUpdateAdapter(ABCAdapter[Update, UpdateCute]):
14
14
  def __repr__(self) -> str:
15
15
  return f"<{self.__class__.__name__}: adapt Update -> UpdateCute>"
16
16
 
17
- async def adapt(
17
+ def adapt(
18
18
  self,
19
19
  api: API,
20
20
  update: Update,
@@ -159,12 +159,12 @@ class CallbackDataMarkup(CallbackQueryDataRule):
159
159
 
160
160
 
161
161
  __all__ = (
162
- "CallbackDataEq",
163
- "CallbackDataJsonEq",
164
- "CallbackDataJsonModel",
165
- "CallbackDataMap",
166
- "CallbackDataMarkup",
167
- "CallbackQueryDataRule",
168
- "CallbackQueryRule",
162
+ "CallbackDataEq",
163
+ "CallbackDataJsonEq",
164
+ "CallbackDataJsonModel",
165
+ "CallbackDataMap",
166
+ "CallbackDataMarkup",
167
+ "CallbackQueryDataRule",
168
+ "CallbackQueryRule",
169
169
  "HasData",
170
170
  )
@@ -39,8 +39,8 @@ class InviteLinkByCreator(ChatJoinRequestRule, requires=[HasInviteLink()]):
39
39
 
40
40
 
41
41
  __all__ = (
42
- "ChatJoinRequestRule",
43
- "HasInviteLink",
44
- "InviteLinkByCreator",
42
+ "ChatJoinRequestRule",
43
+ "HasInviteLink",
44
+ "InviteLinkByCreator",
45
45
  "InviteLinkName",
46
46
  )
@@ -52,9 +52,9 @@ class InlineQueryMarkup(InlineQueryRule):
52
52
 
53
53
 
54
54
  __all__ = (
55
- "HasLocation",
56
- "InlineQueryChatType",
57
- "InlineQueryMarkup",
58
- "InlineQueryRule",
55
+ "HasLocation",
56
+ "InlineQueryChatType",
57
+ "InlineQueryMarkup",
58
+ "InlineQueryRule",
59
59
  "InlineQueryText",
60
60
  )
@@ -1,4 +1,5 @@
1
1
  import dataclasses
2
+ import enum
2
3
  import secrets
3
4
  import typing
4
5
 
@@ -17,6 +18,11 @@ if typing.TYPE_CHECKING:
17
18
  Key = typing.TypeVar("Key", bound=typing.Hashable)
18
19
 
19
20
 
21
+ class ChoiceCode(enum.StrEnum):
22
+ READY = "ready"
23
+ CANCEL = "cancel"
24
+
25
+
20
26
  @dataclasses.dataclass(slots=True)
21
27
  class Choice(typing.Generic[Key]):
22
28
  key: Key
@@ -79,10 +85,11 @@ class _Checkbox(ABCScenario[CallbackQueryCute]):
79
85
  )
80
86
  kb.row()
81
87
 
82
- kb.add(InlineButton(self.ready, callback_data=self.random_code + "/ready"))
88
+ kb.add(InlineButton(self.ready, callback_data=self.random_code + "/" + ChoiceCode.READY))
83
89
  if self.cancel_text is not None:
84
90
  kb.row()
85
- kb.add(InlineButton(self.cancel_text, callback_data=self.random_code + "/cancel"))
91
+ kb.add(InlineButton(self.cancel_text, callback_data=self.random_code + "/" + ChoiceCode.CANCEL))
92
+
86
93
  return kb.get_markup()
87
94
 
88
95
  def add_option(
@@ -100,11 +107,13 @@ class _Checkbox(ABCScenario[CallbackQueryCute]):
100
107
 
101
108
  async def handle(self, cb: CallbackQueryCute) -> bool:
102
109
  code = cb.data.unwrap().replace(self.random_code + "/", "", 1)
103
- if code == "ready":
104
- return False
105
- elif code == "cancel":
106
- self.choices = []
107
- return False
110
+
111
+ match code:
112
+ case ChoiceCode.READY:
113
+ return False
114
+ case ChoiceCode.CANCEL:
115
+ self.choices = []
116
+ return False
108
117
 
109
118
  for i, choice in enumerate(self.choices):
110
119
  if choice.code == code:
@@ -0,0 +1,51 @@
1
+ import typing
2
+
3
+ from telegrinder.bot.cute_types.callback_query import CallbackQueryCute
4
+ from telegrinder.bot.dispatch.waiter_machine.hasher.hasher import Hasher
5
+ from telegrinder.bot.scenario.checkbox import Checkbox, ChoiceCode
6
+
7
+ if typing.TYPE_CHECKING:
8
+ from telegrinder.api.api import API
9
+ from telegrinder.bot.dispatch.view.base import BaseStateView
10
+ from telegrinder.bot.scenario.checkbox import Key
11
+
12
+ class Choice(Checkbox[Key], typing.Generic[Key]):
13
+ async def wait(
14
+ self,
15
+ hasher: Hasher[CallbackQueryCute, int],
16
+ api: API,
17
+ view: BaseStateView[CallbackQueryCute],
18
+ ) -> tuple[Key, int]: ...
19
+
20
+ else:
21
+
22
+ class Choice(Checkbox):
23
+ async def handle(self, cb):
24
+ code = cb.data.unwrap().replace(self.random_code + "/", "", 1)
25
+ if code == ChoiceCode.READY:
26
+ return False
27
+
28
+ for choice in self.choices:
29
+ choice.is_picked = False
30
+
31
+ for i, choice in enumerate(self.choices):
32
+ if choice.code == code:
33
+ self.choices[i].is_picked = True
34
+ await cb.ctx_api.edit_message_text(
35
+ text=self.message,
36
+ chat_id=cb.message.unwrap().v.chat.id,
37
+ message_id=cb.message.unwrap().v.message_id,
38
+ parse_mode=self.PARSE_MODE,
39
+ reply_markup=self.get_markup(),
40
+ )
41
+
42
+ return True
43
+
44
+ async def wait(self, hasher, api, view):
45
+ if len(tuple(choice for choice in self.choices if choice.is_picked)) != 1:
46
+ raise ValueError("Exactly one choice must be picked")
47
+ choices, m_id = await super().wait(hasher, api, view)
48
+ return tuple(choices.keys())[tuple(choices.values()).index(True)], m_id
49
+
50
+
51
+ __all__ = ("Choice",)
@@ -1,6 +1,8 @@
1
+ import base64
1
2
  import dataclasses
2
3
  import enum
3
4
  import keyword
5
+ import os
4
6
  import secrets
5
7
  import typing
6
8
  from datetime import datetime
@@ -15,6 +17,7 @@ if typing.TYPE_CHECKING:
15
17
  from telegrinder.api.error import APIError
16
18
 
17
19
  T = typing.TypeVar("T")
20
+ P = typing.ParamSpec("P")
18
21
 
19
22
  UnionType: typing.TypeAlias = typing.Annotated[tuple[T, ...], ...]
20
23
 
@@ -46,6 +49,15 @@ def full_result(
46
49
  return result.map(lambda v: decoder.decode(v, type=full_t))
47
50
 
48
51
 
52
+ def generate_random_id(length_bytes: int) -> str:
53
+ if length_bytes < 1 or length_bytes > 64:
54
+ raise ValueError("Length of bytes must be between 1 and 64.")
55
+
56
+ random_bytes = os.urandom(length_bytes)
57
+ random_id = base64.urlsafe_b64encode(random_bytes).rstrip(b"=").decode("utf-8")
58
+ return random_id
59
+
60
+
49
61
  def get_params(params: dict[str, typing.Any]) -> dict[str, typing.Any]:
50
62
  validated_params = {}
51
63
  for k, v in (
@@ -60,10 +72,6 @@ def get_params(params: dict[str, typing.Any]) -> dict[str, typing.Any]:
60
72
  return validated_params
61
73
 
62
74
 
63
- class From(typing.Generic[T]):
64
- def __new__(cls, _: T, /) -> typing.Any: ...
65
-
66
-
67
75
  if typing.TYPE_CHECKING:
68
76
 
69
77
  @typing.overload
@@ -72,6 +80,13 @@ if typing.TYPE_CHECKING:
72
80
  @typing.overload
73
81
  def field(*, default: typing.Any, name: str | None = ...) -> typing.Any: ...
74
82
 
83
+ @typing.overload
84
+ def field(
85
+ *,
86
+ default_factory: typing.Callable[[], typing.Any],
87
+ name: str | None = None,
88
+ ) -> typing.Any: ...
89
+
75
90
  @typing.overload
76
91
  def field(
77
92
  *,
@@ -102,9 +117,14 @@ if typing.TYPE_CHECKING:
102
117
  name=...,
103
118
  converter=...,
104
119
  ) -> typing.Any: ...
120
+
121
+ class From(typing.Generic[T]):
122
+ def __new__(cls, _: T, /) -> typing.Any: ...
105
123
  else:
106
124
  from msgspec import field as _field
107
125
 
126
+ From = typing.Annotated[T, ...]
127
+
108
128
  def field(**kwargs):
109
129
  kwargs.pop("converter", None)
110
130
  return _field(**kwargs)
@@ -113,12 +133,16 @@ else:
113
133
  @typing.dataclass_transform(field_specifiers=(field,))
114
134
  class Model(msgspec.Struct, **MODEL_CONFIG):
115
135
  @classmethod
116
- def from_data(cls, data: dict[str, typing.Any]) -> typing.Self:
117
- return decoder.convert(data, type=cls)
136
+ def from_data(cls: typing.Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> T:
137
+ return decoder.convert(msgspec.structs.asdict(cls(*args, **kwargs)), type=cls) # type: ignore
138
+
139
+ @classmethod
140
+ def from_dict(cls, obj: dict[str, typing.Any], /) -> typing.Self:
141
+ return decoder.convert(obj, type=cls)
118
142
 
119
143
  @classmethod
120
- def from_bytes(cls, data: bytes) -> typing.Self:
121
- return decoder.decode(data, type=cls)
144
+ def from_bytes(cls, obj: bytes, /) -> typing.Self:
145
+ return decoder.decode(obj, type=cls)
122
146
 
123
147
  def _to_dict(
124
148
  self,
@@ -285,11 +309,12 @@ else:
285
309
 
286
310
 
287
311
  __all__ = (
288
- "DataConverter",
289
- "MODEL_CONFIG",
290
- "Model",
291
- "ProxiedDict",
292
- "Proxy",
293
- "full_result",
312
+ "DataConverter",
313
+ "MODEL_CONFIG",
314
+ "Model",
315
+ "ProxiedDict",
316
+ "Proxy",
317
+ "full_result",
318
+ "generate_random_id",
294
319
  "get_params",
295
320
  )
@@ -36,8 +36,8 @@ class LoggerModule(typing.Protocol):
36
36
 
37
37
  logger: LoggerModule
38
38
  logging_level = os.getenv("LOGGER_LEVEL", default="DEBUG").upper()
39
- logging_module = choice_in_order(["loguru"], default="logging")
40
- asyncio_module = choice_in_order(["uvloop"], default="asyncio")
39
+ logging_module = choice_in_order(["loguru"], default="logging", do_import=False)
40
+ asyncio_module = choice_in_order(["uvloop"], default="asyncio", do_import=False)
41
41
 
42
42
  if logging_module == "loguru":
43
43
  import os
@@ -96,7 +96,7 @@ def msgspec_to_builtins(
96
96
 
97
97
 
98
98
  def option_dec_hook(tp: type[Option[typing.Any]], obj: typing.Any) -> Option[typing.Any]:
99
- if obj is None:
99
+ if obj is None or isinstance(obj, fntypes.Nothing):
100
100
  return Nothing
101
101
 
102
102
  (value_type,) = typing.get_args(tp) or (typing.Any,)
@@ -1,21 +1,21 @@
1
- from .attachment import Attachment, Audio, Photo, Video
2
- from .base import ComposeError, DataNode, Name, Node, ScalarNode, is_node
3
- from .callback_query import CallbackQueryData, CallbackQueryNode, Field
4
- from .command import CommandInfo
5
- from .composer import Composition, NodeCollection, NodeSession, compose_node, compose_nodes
6
- from .container import ContainerNode
7
- from .event import EventNode
8
- from .me import Me
9
- from .message import MessageNode
10
- from .polymorphic import Polymorphic, impl
11
- from .rule import RuleChain
12
- from .scope import GLOBAL, PER_CALL, PER_EVENT, NodeScope, global_node, per_call, per_event
13
- from .source import ChatSource, Source, UserSource
14
- from .text import Text, TextInteger, TextLiteral
15
- from .tools import generate_node
16
- from .update import UpdateNode
17
-
18
- __all__ = (
1
+ from .attachment import Attachment, Audio, Photo, Video
2
+ from .base import ComposeError, DataNode, Name, Node, ScalarNode, is_node
3
+ from .callback_query import CallbackQueryData, CallbackQueryNode, Field
4
+ from .command import CommandInfo
5
+ from .composer import Composition, NodeCollection, NodeSession, compose_node, compose_nodes
6
+ from .container import ContainerNode
7
+ from .event import EventNode
8
+ from .me import Me
9
+ from .message import MessageNode
10
+ from .polymorphic import Polymorphic, impl
11
+ from .rule import RuleChain
12
+ from .scope import GLOBAL, PER_CALL, PER_EVENT, NodeScope, global_node, per_call, per_event
13
+ from .source import ChatSource, Source, UserSource
14
+ from .text import Text, TextInteger, TextLiteral
15
+ from .tools import generate_node
16
+ from .update import UpdateNode
17
+
18
+ __all__ = (
19
19
  "Attachment",
20
20
  "Audio",
21
21
  "CallbackQueryData",
@@ -56,5 +56,5 @@ __all__ = (
56
56
  "impl",
57
57
  "is_node",
58
58
  "per_call",
59
- "per_event",
60
- )
59
+ "per_event",
60
+ )
@@ -153,14 +153,14 @@ class Name(ScalarNode, str):
153
153
 
154
154
 
155
155
  __all__ = (
156
- "ComposeError",
157
- "FactoryNode",
158
- "DataNode",
159
- "Name",
160
- "Node",
161
- "SCALAR_NODE",
162
- "ScalarNode",
163
- "ScalarNodeProto",
164
- "get_nodes",
156
+ "ComposeError",
157
+ "DataNode",
158
+ "FactoryNode",
159
+ "Name",
160
+ "Node",
161
+ "SCALAR_NODE",
162
+ "ScalarNode",
163
+ "ScalarNodeProto",
164
+ "get_nodes",
165
165
  "is_node",
166
166
  )