telegrinder 0.2.1__py3-none-any.whl → 0.3.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 (85) hide show
  1. telegrinder/__init__.py +45 -12
  2. telegrinder/bot/__init__.py +32 -4
  3. telegrinder/bot/cute_types/callback_query.py +60 -146
  4. telegrinder/bot/cute_types/chat_join_request.py +12 -16
  5. telegrinder/bot/cute_types/chat_member_updated.py +15 -105
  6. telegrinder/bot/cute_types/inline_query.py +5 -14
  7. telegrinder/bot/cute_types/message.py +623 -1238
  8. telegrinder/bot/dispatch/__init__.py +40 -3
  9. telegrinder/bot/dispatch/abc.py +8 -1
  10. telegrinder/bot/dispatch/context.py +2 -2
  11. telegrinder/bot/dispatch/dispatch.py +8 -1
  12. telegrinder/bot/dispatch/handler/__init__.py +17 -1
  13. telegrinder/bot/dispatch/handler/audio_reply.py +44 -0
  14. telegrinder/bot/dispatch/handler/base.py +57 -0
  15. telegrinder/bot/dispatch/handler/document_reply.py +44 -0
  16. telegrinder/bot/dispatch/handler/func.py +3 -3
  17. telegrinder/bot/dispatch/handler/media_group_reply.py +43 -0
  18. telegrinder/bot/dispatch/handler/message_reply.py +12 -35
  19. telegrinder/bot/dispatch/handler/photo_reply.py +44 -0
  20. telegrinder/bot/dispatch/handler/sticker_reply.py +37 -0
  21. telegrinder/bot/dispatch/handler/video_reply.py +44 -0
  22. telegrinder/bot/dispatch/process.py +2 -2
  23. telegrinder/bot/dispatch/return_manager/abc.py +11 -8
  24. telegrinder/bot/dispatch/return_manager/callback_query.py +2 -2
  25. telegrinder/bot/dispatch/return_manager/inline_query.py +2 -2
  26. telegrinder/bot/dispatch/return_manager/message.py +3 -3
  27. telegrinder/bot/dispatch/view/__init__.py +2 -1
  28. telegrinder/bot/dispatch/view/abc.py +2 -181
  29. telegrinder/bot/dispatch/view/base.py +200 -0
  30. telegrinder/bot/dispatch/view/callback_query.py +3 -3
  31. telegrinder/bot/dispatch/view/chat_join_request.py +2 -2
  32. telegrinder/bot/dispatch/view/chat_member.py +2 -3
  33. telegrinder/bot/dispatch/view/inline_query.py +2 -2
  34. telegrinder/bot/dispatch/view/message.py +5 -4
  35. telegrinder/bot/dispatch/view/raw.py +4 -3
  36. telegrinder/bot/dispatch/waiter_machine/__init__.py +18 -0
  37. telegrinder/bot/dispatch/waiter_machine/actions.py +10 -0
  38. telegrinder/bot/dispatch/waiter_machine/hasher/__init__.py +15 -0
  39. telegrinder/bot/dispatch/waiter_machine/hasher/callback.py +60 -0
  40. telegrinder/bot/dispatch/waiter_machine/hasher/hasher.py +49 -0
  41. telegrinder/bot/dispatch/waiter_machine/hasher/message.py +54 -0
  42. telegrinder/bot/dispatch/waiter_machine/hasher/state.py +19 -0
  43. telegrinder/bot/dispatch/waiter_machine/machine.py +88 -101
  44. telegrinder/bot/dispatch/waiter_machine/middleware.py +23 -41
  45. telegrinder/bot/dispatch/waiter_machine/short_state.py +9 -9
  46. telegrinder/bot/polling/polling.py +5 -2
  47. telegrinder/bot/rules/__init__.py +3 -3
  48. telegrinder/bot/rules/abc.py +6 -5
  49. telegrinder/bot/rules/adapter/__init__.py +1 -1
  50. telegrinder/bot/rules/integer.py +1 -1
  51. telegrinder/bot/rules/is_from.py +19 -0
  52. telegrinder/bot/rules/state.py +9 -6
  53. telegrinder/bot/scenario/checkbox.py +5 -5
  54. telegrinder/bot/scenario/choice.py +2 -2
  55. telegrinder/client/aiohttp.py +5 -7
  56. telegrinder/model.py +6 -11
  57. telegrinder/modules.py +16 -25
  58. telegrinder/msgspec_json.py +1 -1
  59. telegrinder/msgspec_utils.py +56 -5
  60. telegrinder/node/base.py +2 -2
  61. telegrinder/node/composer.py +5 -9
  62. telegrinder/node/container.py +6 -1
  63. telegrinder/node/event.py +2 -0
  64. telegrinder/node/polymorphic.py +7 -7
  65. telegrinder/node/rule.py +6 -4
  66. telegrinder/node/scope.py +3 -3
  67. telegrinder/node/source.py +4 -2
  68. telegrinder/node/tools/generator.py +7 -6
  69. telegrinder/rules.py +2 -2
  70. telegrinder/tools/__init__.py +10 -10
  71. telegrinder/tools/functional.py +9 -0
  72. telegrinder/tools/keyboard.py +6 -1
  73. telegrinder/tools/loop_wrapper/loop_wrapper.py +4 -5
  74. telegrinder/tools/magic.py +17 -19
  75. telegrinder/tools/state_storage/__init__.py +3 -3
  76. telegrinder/tools/state_storage/abc.py +12 -10
  77. telegrinder/tools/state_storage/memory.py +9 -6
  78. telegrinder/types/__init__.py +1 -0
  79. telegrinder/types/methods.py +10 -2
  80. telegrinder/types/objects.py +47 -5
  81. {telegrinder-0.2.1.dist-info → telegrinder-0.3.0.dist-info}/METADATA +4 -5
  82. telegrinder-0.3.0.dist-info/RECORD +164 -0
  83. telegrinder-0.2.1.dist-info/RECORD +0 -149
  84. {telegrinder-0.2.1.dist-info → telegrinder-0.3.0.dist-info}/LICENSE +0 -0
  85. {telegrinder-0.2.1.dist-info → telegrinder-0.3.0.dist-info}/WHEEL +0 -0
@@ -1,7 +1,17 @@
1
1
  from telegrinder.bot.dispatch.abc import ABCDispatch
2
2
  from telegrinder.bot.dispatch.context import Context
3
3
  from telegrinder.bot.dispatch.dispatch import Dispatch, TelegrinderContext
4
- from telegrinder.bot.dispatch.handler import ABCHandler, FuncHandler, MessageReplyHandler
4
+ from telegrinder.bot.dispatch.handler import (
5
+ ABCHandler,
6
+ AudioReplyHandler,
7
+ DocumentReplyHandler,
8
+ FuncHandler,
9
+ MediaGroupReplyHandler,
10
+ MessageReplyHandler,
11
+ PhotoReplyHandler,
12
+ StickerReplyHandler,
13
+ VideoReplyHandler,
14
+ )
5
15
  from telegrinder.bot.dispatch.middleware import ABCMiddleware
6
16
  from telegrinder.bot.dispatch.process import check_rule, process_inner
7
17
  from telegrinder.bot.dispatch.return_manager import (
@@ -26,7 +36,19 @@ from telegrinder.bot.dispatch.view import (
26
36
  RawEventView,
27
37
  ViewBox,
28
38
  )
29
- from telegrinder.bot.dispatch.waiter_machine import ShortState, WaiterMachine, clear_wm_storage_worker
39
+ from telegrinder.bot.dispatch.waiter_machine import (
40
+ CALLBACK_QUERY_FOR_MESSAGE,
41
+ CALLBACK_QUERY_FROM_CHAT,
42
+ CALLBACK_QUERY_IN_CHAT_FOR_MESSAGE,
43
+ MESSAGE_FROM_USER,
44
+ MESSAGE_FROM_USER_IN_CHAT,
45
+ MESSAGE_IN_CHAT,
46
+ Hasher,
47
+ ShortState,
48
+ StateViewHasher,
49
+ WaiterMachine,
50
+ clear_wm_storage_worker,
51
+ )
30
52
 
31
53
  __all__ = (
32
54
  "ABCDispatch",
@@ -35,29 +57,44 @@ __all__ = (
35
57
  "ABCReturnManager",
36
58
  "ABCStateView",
37
59
  "ABCView",
60
+ "AudioReplyHandler",
38
61
  "BaseReturnManager",
39
62
  "BaseStateView",
40
63
  "BaseView",
64
+ "CALLBACK_QUERY_FOR_MESSAGE",
65
+ "CALLBACK_QUERY_FROM_CHAT",
66
+ "CALLBACK_QUERY_IN_CHAT_FOR_MESSAGE",
41
67
  "CallbackQueryReturnManager",
42
68
  "CallbackQueryView",
43
69
  "ChatJoinRequestView",
44
70
  "ChatMemberView",
45
71
  "Context",
46
72
  "Dispatch",
73
+ "DocumentReplyHandler",
47
74
  "FuncHandler",
75
+ "Hasher",
48
76
  "InlineQueryReturnManager",
49
77
  "InlineQueryView",
78
+ "MESSAGE_FROM_USER",
79
+ "MESSAGE_FROM_USER_IN_CHAT",
80
+ "MESSAGE_IN_CHAT",
50
81
  "Manager",
82
+ "MediaGroupReplyHandler",
51
83
  "MessageReplyHandler",
52
84
  "MessageReturnManager",
53
85
  "MessageView",
86
+ "PhotoReplyHandler",
54
87
  "RawEventView",
55
88
  "ShortState",
89
+ "StateViewHasher",
90
+ "StickerReplyHandler",
56
91
  "TelegrinderContext",
92
+ "VideoReplyHandler",
57
93
  "ViewBox",
58
94
  "WaiterMachine",
59
95
  "check_rule",
96
+ "clear_wm_storage_worker",
97
+ "clear_wm_storage_worker",
60
98
  "process_inner",
61
99
  "register_manager",
62
- "clear_wm_storage_worker",
63
100
  )
@@ -1,10 +1,14 @@
1
1
  import typing
2
2
  from abc import ABC, abstractmethod
3
3
 
4
- from telegrinder.api import API
4
+ from fntypes import Option
5
+
6
+ from telegrinder.api.api import API
5
7
  from telegrinder.tools.global_context.abc import ABCGlobalContext
6
8
  from telegrinder.types.objects import Update
7
9
 
10
+ T = typing.TypeVar("T")
11
+
8
12
 
9
13
  class ABCDispatch(ABC):
10
14
  @property
@@ -24,5 +28,8 @@ class ABCDispatch(ABC):
24
28
  for external in externals:
25
29
  self.load(external)
26
30
 
31
+ @abstractmethod
32
+ def get_view(self, of_type: type[T]) -> Option[T]: ...
33
+
27
34
 
28
35
  __all__ = ("ABCDispatch",)
@@ -2,7 +2,7 @@ import enum
2
2
  import typing
3
3
  from reprlib import recursive_repr
4
4
 
5
- from telegrinder.types import Update
5
+ from telegrinder.types.objects import Update
6
6
 
7
7
  T = typing.TypeVar("T")
8
8
 
@@ -63,7 +63,7 @@ class Context(dict[str, AnyValue]):
63
63
  return key if isinstance(key, str) else str(key.value)
64
64
 
65
65
  def copy(self) -> typing.Self:
66
- return self.__class__(**self)
66
+ return self.__class__(**dict.copy(self))
67
67
 
68
68
  def set(self, key: Key, value: AnyValue) -> None:
69
69
  self[key] = value
@@ -1,9 +1,10 @@
1
1
  import dataclasses
2
2
  import typing
3
3
 
4
+ from fntypes import Nothing, Option, Some
4
5
  from vbml.patcher import Patcher
5
6
 
6
- from telegrinder.api import API
7
+ from telegrinder.api.api import API
7
8
  from telegrinder.bot.cute_types.base import BaseCute
8
9
  from telegrinder.bot.cute_types.update import UpdateCute
9
10
  from telegrinder.bot.dispatch.abc import ABCDispatch
@@ -188,6 +189,12 @@ class Dispatch(
188
189
  view.load(view_external[name])
189
190
  setattr(external, name, view)
190
191
 
192
+ def get_view(self, of_type: type[T]) -> Option[T]:
193
+ for view in self.get_views().values():
194
+ if isinstance(view, of_type):
195
+ return Some(view)
196
+ return Nothing()
197
+
191
198
  __call__ = handle
192
199
 
193
200
 
@@ -1,5 +1,21 @@
1
1
  from telegrinder.bot.dispatch.handler.abc import ABCHandler
2
+ from telegrinder.bot.dispatch.handler.audio_reply import AudioReplyHandler
3
+ from telegrinder.bot.dispatch.handler.document_reply import DocumentReplyHandler
2
4
  from telegrinder.bot.dispatch.handler.func import FuncHandler
5
+ from telegrinder.bot.dispatch.handler.media_group_reply import MediaGroupReplyHandler
3
6
  from telegrinder.bot.dispatch.handler.message_reply import MessageReplyHandler
7
+ from telegrinder.bot.dispatch.handler.photo_reply import PhotoReplyHandler
8
+ from telegrinder.bot.dispatch.handler.sticker_reply import StickerReplyHandler
9
+ from telegrinder.bot.dispatch.handler.video_reply import VideoReplyHandler
4
10
 
5
- __all__ = ("ABCHandler", "FuncHandler", "MessageReplyHandler")
11
+ __all__ = (
12
+ "ABCHandler",
13
+ "AudioReplyHandler",
14
+ "DocumentReplyHandler",
15
+ "FuncHandler",
16
+ "MediaGroupReplyHandler",
17
+ "MessageReplyHandler",
18
+ "PhotoReplyHandler",
19
+ "StickerReplyHandler",
20
+ "VideoReplyHandler",
21
+ )
@@ -0,0 +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 AudioReplyHandler(BaseReplyHandler):
12
+ def __init__(
13
+ self,
14
+ audio: 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.audio = audio
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_audio if not self.as_reply else event.reply_audio
36
+ await method(
37
+ audio=self.audio,
38
+ parse_mode=self.parse_mode,
39
+ caption=self.caption,
40
+ **self.default_params,
41
+ )
42
+
43
+
44
+ __all__ = ("AudioReplyHandler",)
@@ -0,0 +1,57 @@
1
+ import abc
2
+ import typing
3
+
4
+ from fntypes.result import Result
5
+
6
+ from telegrinder.api.api import API
7
+ from telegrinder.api.error import APIError
8
+ from telegrinder.bot.cute_types.message import MessageCute
9
+ from telegrinder.bot.dispatch.context import Context
10
+ from telegrinder.bot.dispatch.handler.abc import ABCHandler
11
+ from telegrinder.bot.dispatch.process import check_rule
12
+ from telegrinder.bot.rules.abc import ABCRule
13
+ from telegrinder.modules import logger
14
+ from telegrinder.types.objects import Update
15
+
16
+ APIMethod: typing.TypeAlias = typing.Callable[
17
+ typing.Concatenate[MessageCute, ...], typing.Awaitable[Result[typing.Any, APIError]]
18
+ ]
19
+
20
+
21
+ class BaseReplyHandler(ABCHandler[MessageCute], abc.ABC):
22
+ def __init__(
23
+ self,
24
+ *rules: ABCRule,
25
+ is_blocking: bool = True,
26
+ as_reply: bool = False,
27
+ preset_context: Context | None = None,
28
+ **default_params: typing.Any,
29
+ ) -> None:
30
+ self.rules = list(rules)
31
+ self.as_reply = as_reply
32
+ self.is_blocking = is_blocking
33
+ self.default_params = default_params
34
+ self.preset_context = preset_context or Context()
35
+
36
+ def __repr__(self) -> str:
37
+ return f"<{self.__class__.__qualname__}>"
38
+
39
+ async def check(self, api: API, event: Update, ctx: Context | None = None) -> bool:
40
+ ctx = Context(raw_update=event) if ctx is None else ctx
41
+ temp_ctx = ctx.copy()
42
+ temp_ctx |= self.preset_context
43
+
44
+ for rule in self.rules:
45
+ if not await check_rule(api, rule, event, ctx):
46
+ logger.debug("Rule {!r} failed!", rule)
47
+ return False
48
+
49
+ ctx |= temp_ctx
50
+ return True
51
+
52
+ @abc.abstractmethod
53
+ async def run(self, api: API, event: MessageCute, ctx: Context) -> typing.Any:
54
+ pass
55
+
56
+
57
+ __all__ = ("BaseReplyHandler",)
@@ -0,0 +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 DocumentReplyHandler(BaseReplyHandler):
12
+ def __init__(
13
+ self,
14
+ document: 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.document = document
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_document if not self.as_reply else event.reply_document
36
+ await method(
37
+ document=self.document,
38
+ parse_mode=self.parse_mode,
39
+ caption=self.caption,
40
+ **self.default_params,
41
+ )
42
+
43
+
44
+ __all__ = ("DocumentReplyHandler",)
@@ -3,7 +3,7 @@ from functools import cached_property
3
3
 
4
4
  import typing_extensions as typing
5
5
 
6
- from telegrinder.api import API
6
+ from telegrinder.api.api import API
7
7
  from telegrinder.bot.cute_types import BaseCute, UpdateCute
8
8
  from telegrinder.bot.dispatch.context import Context
9
9
  from telegrinder.bot.dispatch.process import check_rule
@@ -72,7 +72,7 @@ class FuncHandler(ABCHandler[Event], typing.Generic[Event, F, ErrorHandlerT]):
72
72
  if nodes:
73
73
  result = await compose_nodes(nodes, ctx, data={Update: event, API: api})
74
74
  if not result:
75
- logger.debug(f"Cannot compose nodes for handler. {result.error}")
75
+ logger.debug(f"Cannot compose nodes for handler. Error: {result.error!r}")
76
76
  return False
77
77
 
78
78
  node_col = result.value
@@ -95,7 +95,7 @@ class FuncHandler(ABCHandler[Event], typing.Generic[Event, F, ErrorHandlerT]):
95
95
  return True
96
96
 
97
97
  async def run(self, api: API, event: Event, ctx: Context) -> typing.Any:
98
- logger.debug(f"Running func handler {self.func}")
98
+ logger.debug(f"Running func handler {self.func.__qualname__!r}")
99
99
  dataclass_type = typing.get_origin(self.dataclass) or self.dataclass
100
100
 
101
101
  if dataclass_type is Update and (event_node := ctx.pop(EVENT_NODE_KEY, None)) is not None:
@@ -0,0 +1,43 @@
1
+ import typing
2
+
3
+ from telegrinder.api.api import API
4
+ from telegrinder.bot.cute_types.message import InputMediaType, 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
+
9
+
10
+ class MediaGroupReplyHandler(BaseReplyHandler):
11
+ def __init__(
12
+ self,
13
+ media: InputMediaType | list[InputMediaType],
14
+ *rules: ABCRule,
15
+ caption: str | list[str] | None = None,
16
+ parse_mode: str | list[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.media = media
23
+ self.parse_mode = parse_mode
24
+ self.caption = caption
25
+ super().__init__(
26
+ *rules,
27
+ is_blocking=is_blocking,
28
+ as_reply=as_reply,
29
+ preset_context=preset_context,
30
+ **default_params,
31
+ )
32
+
33
+ async def run(self, _: API, event: MessageCute, __: Context) -> typing.Any:
34
+ method = event.answer_media_group if not self.as_reply else event.reply_media_group
35
+ await method(
36
+ media=self.media,
37
+ parse_mode=self.parse_mode,
38
+ caption=self.caption,
39
+ **self.default_params,
40
+ )
41
+
42
+
43
+ __all__ = ("MediaGroupReplyHandler",)
@@ -3,57 +3,34 @@ import typing
3
3
  from telegrinder.api.api import API
4
4
  from telegrinder.bot.cute_types.message import MessageCute
5
5
  from telegrinder.bot.dispatch.context import Context
6
- from telegrinder.bot.dispatch.handler.abc import ABCHandler
7
- from telegrinder.bot.dispatch.process import check_rule
6
+ from telegrinder.bot.dispatch.handler.base import BaseReplyHandler
8
7
  from telegrinder.bot.rules.abc import ABCRule
9
- from telegrinder.modules import logger
10
- from telegrinder.types.objects import ReplyParameters, Update
11
8
 
12
9
 
13
- class MessageReplyHandler(ABCHandler[MessageCute]):
10
+ class MessageReplyHandler(BaseReplyHandler):
14
11
  def __init__(
15
12
  self,
16
13
  text: str,
17
14
  *rules: ABCRule,
15
+ parse_mode: str | None = None,
18
16
  is_blocking: bool = True,
19
17
  as_reply: bool = False,
20
18
  preset_context: Context | None = None,
21
19
  **default_params: typing.Any,
22
20
  ) -> None:
23
21
  self.text = text
24
- self.rules = list(rules)
25
- self.as_reply = as_reply
26
- self.is_blocking = is_blocking
27
- self.default_params = default_params
28
- self.preset_context = preset_context or Context()
29
-
30
- def __repr__(self) -> str:
31
- return "<{}: with rules={!r}, {}: {!r}>".format(
32
- ("blocking " if self.is_blocking else "") + self.__class__.__name__,
33
- self.rules,
34
- "answer text as reply" if self.as_reply else "answer text",
35
- self.text,
22
+ self.parse_mode = parse_mode
23
+ super().__init__(
24
+ *rules,
25
+ is_blocking=is_blocking,
26
+ as_reply=as_reply,
27
+ preset_context=preset_context,
28
+ **default_params,
36
29
  )
37
30
 
38
- async def check(self, api: API, event: Update, ctx: Context | None = None) -> bool:
39
- ctx = Context(raw_update=event) if ctx is None else ctx
40
- temp_ctx = ctx.copy()
41
- temp_ctx |= self.preset_context
42
-
43
- for rule in self.rules:
44
- if not await check_rule(api, rule, event, ctx):
45
- logger.debug("Rule {!r} failed!", rule)
46
- return False
47
-
48
- ctx |= temp_ctx
49
- return True
50
-
51
31
  async def run(self, _: API, event: MessageCute, __: Context) -> typing.Any:
52
- await event.answer(
53
- text=self.text,
54
- reply_parameters=ReplyParameters(event.message_id) if self.as_reply else None,
55
- **self.default_params,
56
- )
32
+ method = event.answer if not self.as_reply else event.reply
33
+ await method(text=self.text, parse_mode=self.parse_mode, **self.default_params)
57
34
 
58
35
 
59
36
  __all__ = ("MessageReplyHandler",)
@@ -0,0 +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",)
@@ -0,0 +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",)
@@ -0,0 +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",)
@@ -35,7 +35,7 @@ async def process_inner(
35
35
  logger.debug("Run pre middlewares...")
36
36
  for middleware in middlewares:
37
37
  middleware_result = await middleware.pre(event, ctx)
38
- logger.debug("Middleware {!r} returned {!r}", middleware.__class__.__name__, middleware_result)
38
+ logger.debug("Middleware {!r} returned: {!r}", middleware.__class__.__qualname__, middleware_result)
39
39
  if middleware_result is False:
40
40
  return False
41
41
 
@@ -59,7 +59,7 @@ async def process_inner(
59
59
 
60
60
  logger.debug("Run post middlewares...")
61
61
  for middleware in middlewares:
62
- logger.debug("Run post middleware {!r}", middleware.__class__.__name__)
62
+ logger.debug("Run post middleware {!r}", middleware.__class__.__qualname__)
63
63
  await middleware.post(event, responses, ctx)
64
64
 
65
65
  for session in ctx.get(CONTEXT_STORE_NODES_KEY, {}).values():
@@ -27,13 +27,10 @@ def register_manager(return_type: type[typing.Any] | types.UnionType):
27
27
  @dataclasses.dataclass(frozen=True, slots=True)
28
28
  class Manager:
29
29
  types: tuple[type, ...]
30
- callback: typing.Callable[..., typing.Awaitable]
30
+ callback: typing.Callable[..., typing.Awaitable[typing.Any]]
31
31
 
32
32
  async def __call__(self, *args: typing.Any, **kwargs: typing.Any) -> None:
33
- try:
34
- await self.callback(*args, **kwargs)
35
- except BaseException as ex:
36
- logger.exception(ex)
33
+ await self.callback(*args, **kwargs)
37
34
 
38
35
 
39
36
  class ABCReturnManager(ABC, typing.Generic[Event]):
@@ -51,11 +48,16 @@ class BaseReturnManager(ABCReturnManager[Event]):
51
48
 
52
49
  @property
53
50
  def managers(self) -> list[Manager]:
54
- return [
51
+ managers = self.__dict__.get("managers")
52
+ if managers is not None:
53
+ return managers
54
+ managers_lst = [
55
55
  manager
56
56
  for manager in (vars(BaseReturnManager) | vars(self.__class__)).values()
57
57
  if isinstance(manager, Manager)
58
58
  ]
59
+ self.__dict__["managers"] = managers_lst
60
+ return managers_lst
59
61
 
60
62
  @register_manager(Context)
61
63
  @staticmethod
@@ -73,7 +75,8 @@ class BaseReturnManager(ABCReturnManager[Event]):
73
75
 
74
76
  @typing.overload
75
77
  def register_manager(
76
- self, return_type: type[T]
78
+ self,
79
+ return_type: type[T],
77
80
  ) -> typing.Callable[[typing.Callable[[T, Event, Context], typing.Awaitable[typing.Any]]], Manager]: ...
78
81
 
79
82
  @typing.overload
@@ -94,7 +97,7 @@ class BaseReturnManager(ABCReturnManager[Event]):
94
97
  ]:
95
98
  def wrapper(func: typing.Callable[[T, Event, Context], typing.Awaitable]) -> Manager:
96
99
  manager = Manager(get_union_types(return_type) or (return_type,), func) # type: ignore
97
- setattr(self.__class__, func.__name__, manager)
100
+ self.managers.append(manager)
98
101
  return manager
99
102
 
100
103
  return wrapper
@@ -1,6 +1,6 @@
1
1
  import typing
2
2
 
3
- from telegrinder.bot.cute_types import CallbackQueryCute
3
+ from telegrinder.bot.cute_types.callback_query import CallbackQueryCute
4
4
  from telegrinder.bot.dispatch.context import Context
5
5
  from telegrinder.bot.dispatch.return_manager.abc import BaseReturnManager, register_manager
6
6
 
@@ -11,7 +11,7 @@ class CallbackQueryReturnManager(BaseReturnManager[CallbackQueryCute]):
11
11
  async def str_manager(value: str, event: CallbackQueryCute, ctx: Context) -> None:
12
12
  await event.answer(value)
13
13
 
14
- @register_manager(dict)
14
+ @register_manager(dict[str, typing.Any])
15
15
  @staticmethod
16
16
  async def dict_manager(value: dict[str, typing.Any], event: CallbackQueryCute, ctx: Context) -> None:
17
17
  await event.answer(**value)