telegrinder 0.3.4__py3-none-any.whl → 0.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of telegrinder might be problematic. Click here for more details.

Files changed (192) hide show
  1. telegrinder/__init__.py +148 -149
  2. telegrinder/api/__init__.py +9 -8
  3. telegrinder/api/api.py +101 -93
  4. telegrinder/api/error.py +20 -16
  5. telegrinder/api/response.py +20 -20
  6. telegrinder/api/token.py +36 -36
  7. telegrinder/bot/__init__.py +72 -66
  8. telegrinder/bot/bot.py +83 -76
  9. telegrinder/bot/cute_types/__init__.py +19 -17
  10. telegrinder/bot/cute_types/base.py +184 -258
  11. telegrinder/bot/cute_types/callback_query.py +400 -385
  12. telegrinder/bot/cute_types/chat_join_request.py +62 -61
  13. telegrinder/bot/cute_types/chat_member_updated.py +157 -160
  14. telegrinder/bot/cute_types/inline_query.py +44 -43
  15. telegrinder/bot/cute_types/message.py +2590 -2637
  16. telegrinder/bot/cute_types/pre_checkout_query.py +42 -0
  17. telegrinder/bot/cute_types/update.py +112 -104
  18. telegrinder/bot/cute_types/utils.py +62 -95
  19. telegrinder/bot/dispatch/__init__.py +59 -55
  20. telegrinder/bot/dispatch/abc.py +76 -77
  21. telegrinder/bot/dispatch/context.py +96 -98
  22. telegrinder/bot/dispatch/dispatch.py +254 -202
  23. telegrinder/bot/dispatch/handler/__init__.py +13 -13
  24. telegrinder/bot/dispatch/handler/abc.py +23 -24
  25. telegrinder/bot/dispatch/handler/audio_reply.py +44 -44
  26. telegrinder/bot/dispatch/handler/base.py +57 -57
  27. telegrinder/bot/dispatch/handler/document_reply.py +44 -44
  28. telegrinder/bot/dispatch/handler/func.py +129 -135
  29. telegrinder/bot/dispatch/handler/media_group_reply.py +44 -43
  30. telegrinder/bot/dispatch/handler/message_reply.py +36 -36
  31. telegrinder/bot/dispatch/handler/photo_reply.py +44 -44
  32. telegrinder/bot/dispatch/handler/sticker_reply.py +37 -37
  33. telegrinder/bot/dispatch/handler/video_reply.py +44 -44
  34. telegrinder/bot/dispatch/middleware/__init__.py +3 -3
  35. telegrinder/bot/dispatch/middleware/abc.py +97 -22
  36. telegrinder/bot/dispatch/middleware/global_middleware.py +70 -0
  37. telegrinder/bot/dispatch/process.py +151 -157
  38. telegrinder/bot/dispatch/return_manager/__init__.py +15 -13
  39. telegrinder/bot/dispatch/return_manager/abc.py +104 -108
  40. telegrinder/bot/dispatch/return_manager/callback_query.py +20 -20
  41. telegrinder/bot/dispatch/return_manager/inline_query.py +15 -15
  42. telegrinder/bot/dispatch/return_manager/message.py +36 -36
  43. telegrinder/bot/dispatch/return_manager/pre_checkout_query.py +20 -0
  44. telegrinder/bot/dispatch/view/__init__.py +15 -13
  45. telegrinder/bot/dispatch/view/abc.py +45 -41
  46. telegrinder/bot/dispatch/view/base.py +231 -200
  47. telegrinder/bot/dispatch/view/box.py +140 -129
  48. telegrinder/bot/dispatch/view/callback_query.py +16 -17
  49. telegrinder/bot/dispatch/view/chat_join_request.py +11 -16
  50. telegrinder/bot/dispatch/view/chat_member.py +37 -39
  51. telegrinder/bot/dispatch/view/inline_query.py +16 -17
  52. telegrinder/bot/dispatch/view/message.py +43 -44
  53. telegrinder/bot/dispatch/view/pre_checkout_query.py +16 -0
  54. telegrinder/bot/dispatch/view/raw.py +116 -114
  55. telegrinder/bot/dispatch/waiter_machine/__init__.py +17 -17
  56. telegrinder/bot/dispatch/waiter_machine/actions.py +14 -13
  57. telegrinder/bot/dispatch/waiter_machine/hasher/__init__.py +8 -8
  58. telegrinder/bot/dispatch/waiter_machine/hasher/callback.py +55 -55
  59. telegrinder/bot/dispatch/waiter_machine/hasher/hasher.py +59 -57
  60. telegrinder/bot/dispatch/waiter_machine/hasher/message.py +51 -51
  61. telegrinder/bot/dispatch/waiter_machine/hasher/state.py +20 -19
  62. telegrinder/bot/dispatch/waiter_machine/machine.py +251 -172
  63. telegrinder/bot/dispatch/waiter_machine/middleware.py +94 -89
  64. telegrinder/bot/dispatch/waiter_machine/short_state.py +57 -68
  65. telegrinder/bot/polling/__init__.py +4 -4
  66. telegrinder/bot/polling/abc.py +25 -25
  67. telegrinder/bot/polling/polling.py +139 -131
  68. telegrinder/bot/rules/__init__.py +85 -62
  69. telegrinder/bot/rules/abc.py +213 -206
  70. telegrinder/bot/rules/callback_data.py +122 -163
  71. telegrinder/bot/rules/chat_join.py +45 -43
  72. telegrinder/bot/rules/command.py +126 -126
  73. telegrinder/bot/rules/enum_text.py +33 -36
  74. telegrinder/bot/rules/func.py +28 -26
  75. telegrinder/bot/rules/fuzzy.py +24 -24
  76. telegrinder/bot/rules/id.py +24 -0
  77. telegrinder/bot/rules/inline.py +58 -56
  78. telegrinder/bot/rules/integer.py +21 -20
  79. telegrinder/bot/rules/is_from.py +127 -127
  80. telegrinder/bot/rules/logic.py +18 -0
  81. telegrinder/bot/rules/markup.py +42 -43
  82. telegrinder/bot/rules/mention.py +14 -14
  83. telegrinder/bot/rules/message.py +15 -17
  84. telegrinder/bot/rules/message_entities.py +33 -35
  85. telegrinder/bot/rules/node.py +33 -27
  86. telegrinder/bot/rules/payload.py +81 -0
  87. telegrinder/bot/rules/payment_invoice.py +29 -0
  88. telegrinder/bot/rules/regex.py +36 -37
  89. telegrinder/bot/rules/rule_enum.py +72 -72
  90. telegrinder/bot/rules/start.py +42 -42
  91. telegrinder/bot/rules/state.py +35 -37
  92. telegrinder/bot/rules/text.py +38 -33
  93. telegrinder/bot/rules/update.py +15 -15
  94. telegrinder/bot/scenario/__init__.py +5 -5
  95. telegrinder/bot/scenario/abc.py +17 -19
  96. telegrinder/bot/scenario/checkbox.py +174 -176
  97. telegrinder/bot/scenario/choice.py +48 -51
  98. telegrinder/client/__init__.py +12 -4
  99. telegrinder/client/abc.py +100 -75
  100. telegrinder/client/aiohttp.py +134 -130
  101. telegrinder/client/form_data.py +31 -0
  102. telegrinder/client/sonic.py +212 -0
  103. telegrinder/model.py +208 -315
  104. telegrinder/modules.py +239 -237
  105. telegrinder/msgspec_json.py +14 -14
  106. telegrinder/msgspec_utils.py +478 -410
  107. telegrinder/node/__init__.py +86 -25
  108. telegrinder/node/attachment.py +163 -87
  109. telegrinder/node/base.py +288 -160
  110. telegrinder/node/callback_query.py +54 -53
  111. telegrinder/node/command.py +34 -33
  112. telegrinder/node/composer.py +163 -198
  113. telegrinder/node/container.py +33 -27
  114. telegrinder/node/either.py +82 -0
  115. telegrinder/node/event.py +54 -65
  116. telegrinder/node/file.py +51 -0
  117. telegrinder/node/me.py +15 -16
  118. telegrinder/node/payload.py +78 -0
  119. telegrinder/node/polymorphic.py +67 -48
  120. telegrinder/node/rule.py +72 -76
  121. telegrinder/node/scope.py +36 -38
  122. telegrinder/node/source.py +87 -71
  123. telegrinder/node/text.py +53 -41
  124. telegrinder/node/tools/__init__.py +3 -3
  125. telegrinder/node/tools/generator.py +36 -40
  126. telegrinder/py.typed +0 -0
  127. telegrinder/rules.py +1 -62
  128. telegrinder/tools/__init__.py +152 -93
  129. telegrinder/tools/adapter/__init__.py +19 -0
  130. telegrinder/tools/adapter/abc.py +49 -0
  131. telegrinder/tools/adapter/dataclass.py +56 -0
  132. telegrinder/{bot/rules → tools}/adapter/errors.py +5 -5
  133. telegrinder/{bot/rules → tools}/adapter/event.py +63 -65
  134. telegrinder/{bot/rules → tools}/adapter/node.py +46 -48
  135. telegrinder/{bot/rules → tools}/adapter/raw_event.py +27 -27
  136. telegrinder/{bot/rules → tools}/adapter/raw_update.py +30 -30
  137. telegrinder/tools/buttons.py +106 -80
  138. telegrinder/tools/callback_data_serilization/__init__.py +5 -0
  139. telegrinder/tools/callback_data_serilization/abc.py +51 -0
  140. telegrinder/tools/callback_data_serilization/json_ser.py +60 -0
  141. telegrinder/tools/callback_data_serilization/msgpack_ser.py +172 -0
  142. telegrinder/tools/error_handler/__init__.py +7 -7
  143. telegrinder/tools/error_handler/abc.py +30 -33
  144. telegrinder/tools/error_handler/error.py +9 -9
  145. telegrinder/tools/error_handler/error_handler.py +179 -193
  146. telegrinder/tools/formatting/__init__.py +83 -63
  147. telegrinder/tools/formatting/deep_links.py +541 -0
  148. telegrinder/tools/formatting/{html.py → html_formatter.py} +266 -294
  149. telegrinder/tools/formatting/spec_html_formats.py +71 -117
  150. telegrinder/tools/functional.py +8 -12
  151. telegrinder/tools/global_context/__init__.py +7 -7
  152. telegrinder/tools/global_context/abc.py +63 -63
  153. telegrinder/tools/global_context/global_context.py +387 -412
  154. telegrinder/tools/global_context/telegrinder_ctx.py +27 -27
  155. telegrinder/tools/i18n/__init__.py +7 -7
  156. telegrinder/tools/i18n/abc.py +30 -30
  157. telegrinder/tools/i18n/middleware/__init__.py +3 -3
  158. telegrinder/tools/i18n/middleware/abc.py +22 -25
  159. telegrinder/tools/i18n/simple.py +43 -43
  160. telegrinder/tools/input_file_directory.py +30 -0
  161. telegrinder/tools/keyboard.py +128 -128
  162. telegrinder/tools/lifespan.py +105 -0
  163. telegrinder/tools/limited_dict.py +32 -37
  164. telegrinder/tools/loop_wrapper/__init__.py +4 -4
  165. telegrinder/tools/loop_wrapper/abc.py +20 -15
  166. telegrinder/tools/loop_wrapper/loop_wrapper.py +169 -224
  167. telegrinder/tools/magic.py +307 -157
  168. telegrinder/tools/parse_mode.py +6 -6
  169. telegrinder/tools/state_storage/__init__.py +4 -4
  170. telegrinder/tools/state_storage/abc.py +31 -35
  171. telegrinder/tools/state_storage/memory.py +25 -25
  172. telegrinder/tools/strings.py +13 -0
  173. telegrinder/types/__init__.py +268 -260
  174. telegrinder/types/enums.py +711 -701
  175. telegrinder/types/input_file.py +51 -0
  176. telegrinder/types/methods.py +5055 -4633
  177. telegrinder/types/objects.py +7058 -6950
  178. telegrinder/verification_utils.py +30 -32
  179. {telegrinder-0.3.4.dist-info → telegrinder-0.4.0.dist-info}/LICENSE +22 -22
  180. telegrinder-0.4.0.dist-info/METADATA +144 -0
  181. telegrinder-0.4.0.dist-info/RECORD +182 -0
  182. {telegrinder-0.3.4.dist-info → telegrinder-0.4.0.dist-info}/WHEEL +1 -1
  183. telegrinder/bot/rules/adapter/__init__.py +0 -17
  184. telegrinder/bot/rules/adapter/abc.py +0 -31
  185. telegrinder/node/message.py +0 -14
  186. telegrinder/node/update.py +0 -15
  187. telegrinder/tools/formatting/links.py +0 -38
  188. telegrinder/tools/kb_set/__init__.py +0 -4
  189. telegrinder/tools/kb_set/base.py +0 -15
  190. telegrinder/tools/kb_set/yaml.py +0 -63
  191. telegrinder-0.3.4.dist-info/METADATA +0 -110
  192. telegrinder-0.3.4.dist-info/RECORD +0 -165
@@ -1,44 +1,44 @@
1
- import typing
2
-
3
- from telegrinder.api.api import API
4
- from telegrinder.bot.cute_types.message import MessageCute
5
- from telegrinder.bot.dispatch.context import Context
6
- from telegrinder.bot.dispatch.handler.base import BaseReplyHandler
7
- from telegrinder.bot.rules.abc import ABCRule
8
- from telegrinder.types.objects import InputFile
9
-
10
-
11
- class PhotoReplyHandler(BaseReplyHandler):
12
- def __init__(
13
- self,
14
- photo: InputFile | str,
15
- *rules: ABCRule,
16
- caption: str | None = None,
17
- parse_mode: str | None = None,
18
- is_blocking: bool = True,
19
- as_reply: bool = False,
20
- preset_context: Context | None = None,
21
- **default_params: typing.Any,
22
- ) -> None:
23
- self.photo = photo
24
- self.parse_mode = parse_mode
25
- self.caption = caption
26
- super().__init__(
27
- *rules,
28
- is_blocking=is_blocking,
29
- as_reply=as_reply,
30
- preset_context=preset_context,
31
- **default_params,
32
- )
33
-
34
- async def run(self, _: API, event: MessageCute, __: Context) -> typing.Any:
35
- method = event.answer_photo if not self.as_reply else event.reply_photo
36
- await method(
37
- photo=self.photo,
38
- parse_mode=self.parse_mode,
39
- caption=self.caption,
40
- **self.default_params,
41
- )
42
-
43
-
44
- __all__ = ("PhotoReplyHandler",)
1
+ import typing
2
+
3
+ from telegrinder.api.api import API
4
+ from telegrinder.bot.cute_types.message import MessageCute
5
+ from telegrinder.bot.dispatch.context import Context
6
+ from telegrinder.bot.dispatch.handler.base import BaseReplyHandler
7
+ from telegrinder.bot.rules.abc import ABCRule
8
+ from telegrinder.types.objects import InputFile
9
+
10
+
11
+ class PhotoReplyHandler(BaseReplyHandler):
12
+ def __init__(
13
+ self,
14
+ photo: InputFile | str,
15
+ *rules: ABCRule,
16
+ caption: str | None = None,
17
+ parse_mode: str | None = None,
18
+ final: bool = True,
19
+ as_reply: bool = False,
20
+ preset_context: Context | None = None,
21
+ **default_params: typing.Any,
22
+ ) -> None:
23
+ self.photo = photo
24
+ self.parse_mode = parse_mode
25
+ self.caption = caption
26
+ super().__init__(
27
+ *rules,
28
+ final=final,
29
+ as_reply=as_reply,
30
+ preset_context=preset_context,
31
+ **default_params,
32
+ )
33
+
34
+ async def run(self, _: API, event: MessageCute, __: Context) -> typing.Any:
35
+ method = event.answer_photo if not self.as_reply else event.reply_photo
36
+ await method(
37
+ photo=self.photo,
38
+ parse_mode=self.parse_mode,
39
+ caption=self.caption,
40
+ **self.default_params,
41
+ )
42
+
43
+
44
+ __all__ = ("PhotoReplyHandler",)
@@ -1,37 +1,37 @@
1
- import typing
2
-
3
- from telegrinder.api.api import API
4
- from telegrinder.bot.cute_types.message import MessageCute
5
- from telegrinder.bot.dispatch.context import Context
6
- from telegrinder.bot.dispatch.handler.base import BaseReplyHandler
7
- from telegrinder.bot.rules.abc import ABCRule
8
- from telegrinder.types.objects import InputFile
9
-
10
-
11
- class StickerReplyHandler(BaseReplyHandler):
12
- def __init__(
13
- self,
14
- sticker: InputFile | str,
15
- *rules: ABCRule,
16
- emoji: str | None = None,
17
- is_blocking: bool = True,
18
- as_reply: bool = False,
19
- preset_context: Context | None = None,
20
- **default_params: typing.Any,
21
- ) -> None:
22
- self.sticker = sticker
23
- self.emoji = emoji
24
- super().__init__(
25
- *rules,
26
- is_blocking=is_blocking,
27
- as_reply=as_reply,
28
- preset_context=preset_context,
29
- **default_params,
30
- )
31
-
32
- async def run(self, _: API, event: MessageCute, __: Context) -> typing.Any:
33
- method = event.answer_sticker if not self.as_reply else event.reply_sticker
34
- await method(sticker=self.sticker, emoji=self.emoji, **self.default_params)
35
-
36
-
37
- __all__ = ("StickerReplyHandler",)
1
+ import typing
2
+
3
+ from telegrinder.api.api import API
4
+ from telegrinder.bot.cute_types.message import MessageCute
5
+ from telegrinder.bot.dispatch.context import Context
6
+ from telegrinder.bot.dispatch.handler.base import BaseReplyHandler
7
+ from telegrinder.bot.rules.abc import ABCRule
8
+ from telegrinder.types.objects import InputFile
9
+
10
+
11
+ class StickerReplyHandler(BaseReplyHandler):
12
+ def __init__(
13
+ self,
14
+ sticker: InputFile | str,
15
+ *rules: ABCRule,
16
+ emoji: str | None = None,
17
+ final: bool = True,
18
+ as_reply: bool = False,
19
+ preset_context: Context | None = None,
20
+ **default_params: typing.Any,
21
+ ) -> None:
22
+ self.sticker = sticker
23
+ self.emoji = emoji
24
+ super().__init__(
25
+ *rules,
26
+ final=final,
27
+ as_reply=as_reply,
28
+ preset_context=preset_context,
29
+ **default_params,
30
+ )
31
+
32
+ async def run(self, _: API, event: MessageCute, __: Context) -> typing.Any:
33
+ method = event.answer_sticker if not self.as_reply else event.reply_sticker
34
+ await method(sticker=self.sticker, emoji=self.emoji, **self.default_params)
35
+
36
+
37
+ __all__ = ("StickerReplyHandler",)
@@ -1,44 +1,44 @@
1
- import typing
2
-
3
- from telegrinder.api.api import API
4
- from telegrinder.bot.cute_types.message import MessageCute
5
- from telegrinder.bot.dispatch.context import Context
6
- from telegrinder.bot.dispatch.handler.base import BaseReplyHandler
7
- from telegrinder.bot.rules.abc import ABCRule
8
- from telegrinder.types.objects import InputFile
9
-
10
-
11
- class VideoReplyHandler(BaseReplyHandler):
12
- def __init__(
13
- self,
14
- video: InputFile | str,
15
- *rules: ABCRule,
16
- caption: str | None = None,
17
- parse_mode: str | None = None,
18
- is_blocking: bool = True,
19
- as_reply: bool = False,
20
- preset_context: Context | None = None,
21
- **default_params: typing.Any,
22
- ) -> None:
23
- self.video = video
24
- self.parse_mode = parse_mode
25
- self.caption = caption
26
- super().__init__(
27
- *rules,
28
- is_blocking=is_blocking,
29
- as_reply=as_reply,
30
- preset_context=preset_context,
31
- **default_params,
32
- )
33
-
34
- async def run(self, _: API, event: MessageCute, __: Context) -> typing.Any:
35
- method = event.answer_video if not self.as_reply else event.reply_video
36
- await method(
37
- video=self.video,
38
- parse_mode=self.parse_mode,
39
- caption=self.caption,
40
- **self.default_params,
41
- )
42
-
43
-
44
- __all__ = ("VideoReplyHandler",)
1
+ import typing
2
+
3
+ from telegrinder.api.api import API
4
+ from telegrinder.bot.cute_types.message import MessageCute
5
+ from telegrinder.bot.dispatch.context import Context
6
+ from telegrinder.bot.dispatch.handler.base import BaseReplyHandler
7
+ from telegrinder.bot.rules.abc import ABCRule
8
+ from telegrinder.types.objects import InputFile
9
+
10
+
11
+ class VideoReplyHandler(BaseReplyHandler):
12
+ def __init__(
13
+ self,
14
+ video: InputFile | str,
15
+ *rules: ABCRule,
16
+ caption: str | None = None,
17
+ parse_mode: str | None = None,
18
+ final: bool = True,
19
+ as_reply: bool = False,
20
+ preset_context: Context | None = None,
21
+ **default_params: typing.Any,
22
+ ) -> None:
23
+ self.video = video
24
+ self.parse_mode = parse_mode
25
+ self.caption = caption
26
+ super().__init__(
27
+ *rules,
28
+ final=final,
29
+ as_reply=as_reply,
30
+ preset_context=preset_context,
31
+ **default_params,
32
+ )
33
+
34
+ async def run(self, _: API, event: MessageCute, __: Context) -> typing.Any:
35
+ method = event.answer_video if not self.as_reply else event.reply_video
36
+ await method(
37
+ video=self.video,
38
+ parse_mode=self.parse_mode,
39
+ caption=self.caption,
40
+ **self.default_params,
41
+ )
42
+
43
+
44
+ __all__ = ("VideoReplyHandler",)
@@ -1,3 +1,3 @@
1
- from telegrinder.bot.dispatch.middleware.abc import ABCMiddleware
2
-
3
- __all__ = ("ABCMiddleware",)
1
+ from telegrinder.bot.dispatch.middleware.abc import ABCMiddleware
2
+
3
+ __all__ = ("ABCMiddleware",)
@@ -1,22 +1,97 @@
1
- import typing
2
- from abc import ABC
3
-
4
- from telegrinder.bot.dispatch.context import Context
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
10
-
11
- Event = typing.TypeVar("Event", bound=Model)
12
-
13
-
14
- class ABCMiddleware(ABC, typing.Generic[Event]):
15
- adapter: "ABCAdapter[Update, Event] | None" = None
16
-
17
- async def pre(self, event: Event, ctx: Context) -> bool: ...
18
-
19
- async def post(self, event: Event, responses: list[typing.Any], ctx: Context) -> None: ...
20
-
21
-
22
- __all__ = ("ABCMiddleware",)
1
+ from __future__ import annotations
2
+
3
+ from abc import ABC
4
+
5
+ import typing_extensions as typing
6
+ from fntypes import Some
7
+
8
+ from telegrinder.api import API
9
+ from telegrinder.bot.cute_types.base import BaseCute
10
+ from telegrinder.bot.dispatch.context import Context
11
+ from telegrinder.model import Model
12
+ from telegrinder.modules import logger
13
+ from telegrinder.tools.adapter.abc import ABCAdapter, run_adapter
14
+ from telegrinder.tools.lifespan import Lifespan
15
+ from telegrinder.types.objects import Update
16
+
17
+ ToEvent = typing.TypeVar("ToEvent", bound=Model, default=typing.Any)
18
+
19
+
20
+ async def run_middleware[Event: Model, R: bool | None](
21
+ method: typing.Callable[typing.Concatenate[Event, Context, ...], typing.Awaitable[R]],
22
+ api: API[typing.Any],
23
+ event: Event,
24
+ ctx: Context,
25
+ raw_event: Update | None = None,
26
+ adapter: "ABCAdapter[Update, Event] | None" = None,
27
+ *args: typing.Any,
28
+ **kwargs: typing.Any,
29
+ ) -> R:
30
+ if adapter is not None:
31
+ if raw_event is None:
32
+ raise RuntimeError("raw_event must be specified to apply adapter")
33
+ match await run_adapter(adapter, api, raw_event, ctx):
34
+ case Some(val):
35
+ event = val
36
+ case _:
37
+ return False # type: ignore
38
+
39
+ logger.debug("Running {}-middleware {!r}...", method.__name__, method.__qualname__.split(".")[0])
40
+ return await method(event, ctx, *args, **kwargs) # type: ignore
41
+
42
+
43
+ class ABCMiddleware[Event: Model | BaseCute](ABC):
44
+ adapter: ABCAdapter[Update, Event] | None = None
45
+
46
+ def __repr__(self) -> str:
47
+ name = f"middleware {self.__class__.__name__!r}:"
48
+ has_pre = self.pre.__qualname__.split(".")[0] != "ABCMiddleware"
49
+ has_post = self.post.__qualname__.split(".")[0] != "ABCMiddleware"
50
+
51
+ if has_post:
52
+ name = "post-" + name
53
+ if has_pre:
54
+ name = "pre-" + name
55
+
56
+ return "<{} with adapter={!r}>".format(name, self.adapter)
57
+
58
+ async def pre(self, event: Event, ctx: Context) -> bool: ...
59
+
60
+ async def post(self, event: Event, ctx: Context) -> None: ...
61
+
62
+ @typing.overload
63
+ def to_lifespan(self, event: Event, ctx: Context | None = None, *, api: API) -> Lifespan: ...
64
+
65
+ @typing.overload
66
+ def to_lifespan(self, event: Event, ctx: Context | None = None) -> Lifespan: ...
67
+
68
+ def to_lifespan(
69
+ self,
70
+ event: Event,
71
+ ctx: Context | None = None,
72
+ api: API | None = None,
73
+ **add_context: typing.Any,
74
+ ) -> Lifespan:
75
+ if api is None:
76
+ if not isinstance(event, BaseCute):
77
+ raise LookupError("Cannot get api, please pass as kwarg or provide BaseCute api-bound event")
78
+ api = event.api
79
+
80
+ ctx = ctx or Context()
81
+ ctx |= add_context
82
+ return Lifespan(
83
+ startup_tasks=[run_middleware(self.pre, api, event, raw_event=None, ctx=ctx, adapter=None)],
84
+ shutdown_tasks=[
85
+ run_middleware(
86
+ self.post,
87
+ api,
88
+ event,
89
+ raw_event=None,
90
+ ctx=ctx,
91
+ adapter=None,
92
+ )
93
+ ],
94
+ )
95
+
96
+
97
+ __all__ = ("ABCMiddleware", "run_middleware")
@@ -0,0 +1,70 @@
1
+ import inspect
2
+ import typing
3
+ from contextlib import contextmanager
4
+
5
+ from telegrinder.api import API
6
+ from telegrinder.bot.cute_types.update import UpdateCute
7
+ from telegrinder.bot.dispatch.context import Context
8
+ from telegrinder.bot.dispatch.middleware.abc import ABCMiddleware
9
+ from telegrinder.bot.rules.abc import ABCRule, check_rule
10
+ from telegrinder.node import IsNode, compose_nodes
11
+ from telegrinder.tools.adapter.abc import ABCAdapter
12
+ from telegrinder.tools.adapter.raw_update import RawUpdateAdapter
13
+ from telegrinder.types import Update
14
+
15
+
16
+ class GlobalMiddleware(ABCMiddleware):
17
+ adapter = RawUpdateAdapter()
18
+
19
+ def __init__(self):
20
+ self.filters: set[ABCRule] = set()
21
+ self.source_filters: dict[ABCAdapter | IsNode, dict[typing.Any, ABCRule]] = {}
22
+
23
+ async def pre(self, event: UpdateCute, ctx: Context) -> bool:
24
+ for filter in self.filters:
25
+ if not await check_rule(event.api, filter, event, ctx):
26
+ return False
27
+
28
+ # Simple implication.... Grouped by source categories
29
+ for source, identifiers in self.source_filters.items():
30
+ if isinstance(source, ABCAdapter):
31
+ result = source.adapt(event.api, event, ctx)
32
+ if inspect.isawaitable(result):
33
+ result = await result
34
+
35
+ result = result.unwrap_or_none()
36
+ if result is None:
37
+ return True
38
+
39
+ else:
40
+ result = await compose_nodes({"value": source}, ctx, {Update: event, API: event.api})
41
+ if result := result.unwrap():
42
+ result = result.values["value"]
43
+ else:
44
+ return True
45
+
46
+ if result in identifiers:
47
+ return await check_rule(event.api, identifiers[result], event, ctx)
48
+
49
+ return True
50
+
51
+ @contextmanager
52
+ def apply_filters(
53
+ self,
54
+ *filters: ABCRule,
55
+ source_filter: tuple[ABCAdapter | IsNode, typing.Any, ABCRule] | None = None,
56
+ ):
57
+ if source_filter is not None:
58
+ self.source_filters.setdefault(source_filter[0], {})
59
+ self.source_filters[source_filter[0]].update({source_filter[1]: source_filter[2]})
60
+
61
+ self.filters |= set(filters)
62
+ yield
63
+ self.filters.difference_update(filters)
64
+
65
+ if source_filter is not None: # noqa: SIM102
66
+ if identifiers := self.source_filters.get(source_filter[0]):
67
+ identifiers.pop(source_filter[1], None)
68
+
69
+
70
+ __all__ = ("GlobalMiddleware",)