telegrinder 0.1.dev20__py3-none-any.whl → 0.1.dev159__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 (132) hide show
  1. telegrinder/__init__.py +129 -22
  2. telegrinder/api/__init__.py +11 -2
  3. telegrinder/api/abc.py +25 -9
  4. telegrinder/api/api.py +47 -28
  5. telegrinder/api/error.py +14 -4
  6. telegrinder/api/response.py +11 -7
  7. telegrinder/bot/__init__.py +68 -7
  8. telegrinder/bot/bot.py +30 -24
  9. telegrinder/bot/cute_types/__init__.py +11 -1
  10. telegrinder/bot/cute_types/base.py +138 -0
  11. telegrinder/bot/cute_types/callback_query.py +458 -15
  12. telegrinder/bot/cute_types/inline_query.py +30 -24
  13. telegrinder/bot/cute_types/message.py +2982 -78
  14. telegrinder/bot/cute_types/update.py +30 -0
  15. telegrinder/bot/cute_types/utils.py +794 -0
  16. telegrinder/bot/dispatch/__init__.py +56 -3
  17. telegrinder/bot/dispatch/abc.py +9 -7
  18. telegrinder/bot/dispatch/composition.py +74 -0
  19. telegrinder/bot/dispatch/context.py +71 -0
  20. telegrinder/bot/dispatch/dispatch.py +86 -49
  21. telegrinder/bot/dispatch/handler/__init__.py +3 -0
  22. telegrinder/bot/dispatch/handler/abc.py +11 -5
  23. telegrinder/bot/dispatch/handler/func.py +41 -32
  24. telegrinder/bot/dispatch/handler/message_reply.py +46 -0
  25. telegrinder/bot/dispatch/middleware/__init__.py +2 -0
  26. telegrinder/bot/dispatch/middleware/abc.py +10 -4
  27. telegrinder/bot/dispatch/process.py +53 -49
  28. telegrinder/bot/dispatch/return_manager/__init__.py +19 -0
  29. telegrinder/bot/dispatch/return_manager/abc.py +95 -0
  30. telegrinder/bot/dispatch/return_manager/callback_query.py +19 -0
  31. telegrinder/bot/dispatch/return_manager/inline_query.py +14 -0
  32. telegrinder/bot/dispatch/return_manager/message.py +25 -0
  33. telegrinder/bot/dispatch/view/__init__.py +14 -2
  34. telegrinder/bot/dispatch/view/abc.py +128 -2
  35. telegrinder/bot/dispatch/view/box.py +38 -0
  36. telegrinder/bot/dispatch/view/callback_query.py +13 -39
  37. telegrinder/bot/dispatch/view/inline_query.py +11 -39
  38. telegrinder/bot/dispatch/view/message.py +11 -47
  39. telegrinder/bot/dispatch/waiter_machine/__init__.py +9 -0
  40. telegrinder/bot/dispatch/waiter_machine/machine.py +116 -0
  41. telegrinder/bot/dispatch/waiter_machine/middleware.py +76 -0
  42. telegrinder/bot/dispatch/waiter_machine/short_state.py +37 -0
  43. telegrinder/bot/polling/__init__.py +2 -0
  44. telegrinder/bot/polling/abc.py +11 -4
  45. telegrinder/bot/polling/polling.py +89 -40
  46. telegrinder/bot/rules/__init__.py +91 -5
  47. telegrinder/bot/rules/abc.py +81 -63
  48. telegrinder/bot/rules/adapter/__init__.py +11 -0
  49. telegrinder/bot/rules/adapter/abc.py +21 -0
  50. telegrinder/bot/rules/adapter/errors.py +5 -0
  51. telegrinder/bot/rules/adapter/event.py +49 -0
  52. telegrinder/bot/rules/adapter/raw_update.py +24 -0
  53. telegrinder/bot/rules/callback_data.py +159 -38
  54. telegrinder/bot/rules/command.py +116 -0
  55. telegrinder/bot/rules/enum_text.py +28 -0
  56. telegrinder/bot/rules/func.py +17 -17
  57. telegrinder/bot/rules/fuzzy.py +13 -10
  58. telegrinder/bot/rules/inline.py +61 -0
  59. telegrinder/bot/rules/integer.py +12 -7
  60. telegrinder/bot/rules/is_from.py +148 -7
  61. telegrinder/bot/rules/markup.py +21 -18
  62. telegrinder/bot/rules/mention.py +17 -0
  63. telegrinder/bot/rules/message_entities.py +33 -0
  64. telegrinder/bot/rules/regex.py +27 -19
  65. telegrinder/bot/rules/rule_enum.py +74 -0
  66. telegrinder/bot/rules/start.py +25 -13
  67. telegrinder/bot/rules/text.py +23 -14
  68. telegrinder/bot/scenario/__init__.py +2 -0
  69. telegrinder/bot/scenario/abc.py +12 -5
  70. telegrinder/bot/scenario/checkbox.py +48 -30
  71. telegrinder/bot/scenario/choice.py +16 -10
  72. telegrinder/client/__init__.py +3 -1
  73. telegrinder/client/abc.py +26 -16
  74. telegrinder/client/aiohttp.py +54 -32
  75. telegrinder/model.py +119 -40
  76. telegrinder/modules.py +189 -21
  77. telegrinder/msgspec_json.py +14 -0
  78. telegrinder/msgspec_utils.py +227 -0
  79. telegrinder/node/__init__.py +31 -0
  80. telegrinder/node/attachment.py +71 -0
  81. telegrinder/node/base.py +93 -0
  82. telegrinder/node/composer.py +71 -0
  83. telegrinder/node/container.py +22 -0
  84. telegrinder/node/message.py +18 -0
  85. telegrinder/node/rule.py +56 -0
  86. telegrinder/node/source.py +31 -0
  87. telegrinder/node/text.py +13 -0
  88. telegrinder/node/tools/__init__.py +3 -0
  89. telegrinder/node/tools/generator.py +40 -0
  90. telegrinder/node/update.py +12 -0
  91. telegrinder/rules.py +1 -1
  92. telegrinder/tools/__init__.py +138 -4
  93. telegrinder/tools/buttons.py +89 -51
  94. telegrinder/tools/error_handler/__init__.py +8 -0
  95. telegrinder/tools/error_handler/abc.py +30 -0
  96. telegrinder/tools/error_handler/error_handler.py +156 -0
  97. telegrinder/tools/formatting/__init__.py +81 -3
  98. telegrinder/tools/formatting/html.py +283 -37
  99. telegrinder/tools/formatting/links.py +32 -0
  100. telegrinder/tools/formatting/spec_html_formats.py +121 -0
  101. telegrinder/tools/global_context/__init__.py +12 -0
  102. telegrinder/tools/global_context/abc.py +66 -0
  103. telegrinder/tools/global_context/global_context.py +451 -0
  104. telegrinder/tools/global_context/telegrinder_ctx.py +25 -0
  105. telegrinder/tools/i18n/__init__.py +12 -0
  106. telegrinder/tools/i18n/base.py +31 -0
  107. telegrinder/tools/i18n/middleware/__init__.py +3 -0
  108. telegrinder/tools/i18n/middleware/base.py +26 -0
  109. telegrinder/tools/i18n/simple.py +48 -0
  110. telegrinder/tools/kb_set/__init__.py +2 -0
  111. telegrinder/tools/kb_set/base.py +3 -0
  112. telegrinder/tools/kb_set/yaml.py +28 -17
  113. telegrinder/tools/keyboard.py +84 -62
  114. telegrinder/tools/loop_wrapper/__init__.py +4 -0
  115. telegrinder/tools/loop_wrapper/abc.py +18 -0
  116. telegrinder/tools/loop_wrapper/loop_wrapper.py +132 -0
  117. telegrinder/tools/magic.py +48 -23
  118. telegrinder/tools/parse_mode.py +1 -2
  119. telegrinder/types/__init__.py +1 -0
  120. telegrinder/types/enums.py +653 -0
  121. telegrinder/types/methods.py +4107 -1279
  122. telegrinder/types/objects.py +4771 -1745
  123. {telegrinder-0.1.dev20.dist-info → telegrinder-0.1.dev159.dist-info}/LICENSE +2 -1
  124. telegrinder-0.1.dev159.dist-info/METADATA +109 -0
  125. telegrinder-0.1.dev159.dist-info/RECORD +126 -0
  126. {telegrinder-0.1.dev20.dist-info → telegrinder-0.1.dev159.dist-info}/WHEEL +1 -1
  127. telegrinder/bot/dispatch/waiter.py +0 -38
  128. telegrinder/result.py +0 -38
  129. telegrinder/tools/formatting/abc.py +0 -52
  130. telegrinder/tools/formatting/markdown.py +0 -57
  131. telegrinder-0.1.dev20.dist-info/METADATA +0 -22
  132. telegrinder-0.1.dev20.dist-info/RECORD +0 -71
@@ -1,61 +1,34 @@
1
1
  import typing
2
2
 
3
- from .waiter import Waiter
4
- from .middleware.abc import ABCMiddleware
5
- from .handler.abc import ABCHandler
6
- from telegrinder.types import Update
7
- from telegrinder.modules import logger
8
-
9
- T = typing.TypeVar("T")
10
- E = typing.TypeVar("E")
11
-
12
-
13
- async def process_waiters(
14
- waiters: typing.Dict[T, Waiter],
15
- key: T,
16
- event: typing.Optional[E],
17
- raw_event: dict,
18
- str_handler: typing.Callable,
19
- ) -> bool:
20
- if key not in waiters:
21
- return False
22
-
23
- logger.debug(
24
- "update {} found in waiter (key={})", event.__class__.__name__, str(key)
25
- )
3
+ from fntypes.result import Error
26
4
 
27
- waiter = waiters[key]
28
- ctx = {}
5
+ from telegrinder.api.abc import ABCAPI
6
+ from telegrinder.bot.cute_types import BaseCute
7
+ from telegrinder.bot.dispatch.context import Context
8
+ from telegrinder.modules import logger
9
+ from telegrinder.tools.i18n.base import I18nEnum
10
+ from telegrinder.types import Update
29
11
 
30
- for rule in waiter.rules:
31
- chk_event = event
32
- if rule.__event__ is None:
33
- chk_event = raw_event
34
- if not await rule.run_check(chk_event, ctx):
35
- if not waiter.default:
36
- return True
37
- elif isinstance(waiter.default, str):
38
- await str_handler(waiter.default)
39
- else:
40
- await waiter.default(event)
41
- return True
12
+ from .middleware.abc import ABCMiddleware
13
+ from .return_manager.abc import ABCReturnManager
42
14
 
43
- logger.debug("waiter set as ready")
15
+ if typing.TYPE_CHECKING:
16
+ from telegrinder.bot.dispatch.handler.abc import ABCHandler
17
+ from telegrinder.bot.rules.abc import ABCRule
44
18
 
45
- waiters.pop(key)
46
- setattr(waiter.event, "e", (event, ctx))
47
- waiter.event.set()
48
- return True
19
+ T = typing.TypeVar("T", bound=BaseCute)
20
+ _ = typing.Any
49
21
 
50
22
 
51
23
  async def process_inner(
52
24
  event: T,
53
25
  raw_event: Update,
54
- middlewares: typing.List[ABCMiddleware[T]],
55
- handlers: typing.List[ABCHandler[T]],
26
+ middlewares: list[ABCMiddleware[T]],
27
+ handlers: list["ABCHandler[T]"],
28
+ return_manager: ABCReturnManager[T],
56
29
  ) -> bool:
57
- logger.debug("processing {}", event.__class__.__name__)
58
- ctx = {}
30
+ logger.debug("Processing {!r}...", event.__class__.__name__)
31
+ ctx = Context(raw_update=raw_event)
59
32
 
60
33
  for middleware in middlewares:
61
34
  if await middleware.pre(event, ctx) is False:
@@ -64,12 +37,12 @@ async def process_inner(
64
37
  found = False
65
38
  responses = []
66
39
  for handler in handlers:
67
- result = await handler.check(event.api, raw_event)
68
- if result:
69
- handler.ctx.update(ctx)
40
+ if await handler.check(event.api, raw_event, ctx):
70
41
  found = True
42
+ handler.ctx |= ctx
71
43
  response = await handler.run(event)
72
44
  responses.append(response)
45
+ await return_manager.run(response, event, ctx)
73
46
  if handler.is_blocking:
74
47
  break
75
48
 
@@ -77,3 +50,34 @@ async def process_inner(
77
50
  await middleware.post(event, responses, ctx)
78
51
 
79
52
  return found
53
+
54
+
55
+ async def check_rule(
56
+ api: ABCAPI,
57
+ rule: "ABCRule",
58
+ update: Update,
59
+ ctx: Context,
60
+ ) -> bool:
61
+ """Checks requirements, adapts update.
62
+ Returns check result."""
63
+
64
+ cute_model = await rule.adapter.adapt(api, update)
65
+ match cute_model:
66
+ case Error(err):
67
+ logger.debug("Adapter failed with error message: {!r}", str(err))
68
+ return False
69
+
70
+ ctx_copy = ctx.copy()
71
+ for requirement in rule.requires:
72
+ if not await check_rule(api, requirement, update, ctx_copy):
73
+ return False
74
+
75
+ ctx |= ctx_copy
76
+
77
+ if I18nEnum.I18N in ctx:
78
+ rule = await rule.translate(ctx.get(I18nEnum.I18N))
79
+
80
+ return await rule.check(cute_model.unwrap(), ctx)
81
+
82
+
83
+ __all__ = ("check_rule", "process_inner")
@@ -0,0 +1,19 @@
1
+ from .abc import (
2
+ ABCReturnManager,
3
+ BaseReturnManager,
4
+ Manager,
5
+ register_manager,
6
+ )
7
+ from .callback_query import CallbackQueryReturnManager
8
+ from .inline_query import InlineQueryReturnManager
9
+ from .message import MessageReturnManager
10
+
11
+ __all__ = (
12
+ "ABCReturnManager",
13
+ "BaseReturnManager",
14
+ "CallbackQueryReturnManager",
15
+ "InlineQueryReturnManager",
16
+ "Manager",
17
+ "MessageReturnManager",
18
+ "register_manager",
19
+ )
@@ -0,0 +1,95 @@
1
+ import dataclasses
2
+ import types
3
+ import typing
4
+ from abc import ABC, abstractmethod
5
+
6
+ from telegrinder.bot.cute_types import BaseCute
7
+ from telegrinder.bot.dispatch.context import Context
8
+ from telegrinder.modules import logger
9
+
10
+ T = typing.TypeVar("T")
11
+ EventT = typing.TypeVar("EventT", bound=BaseCute, contravariant=True)
12
+
13
+
14
+ def get_union_types(t: types.UnionType) -> tuple[type, ...] | None:
15
+ if type(t) in (types.UnionType, typing._UnionGenericAlias): # type: ignore
16
+ return tuple(typing.get_origin(x) or x for x in typing.get_args(t))
17
+ return None
18
+
19
+
20
+ def register_manager(return_type: type | types.UnionType):
21
+ def wrapper(func: typing.Callable[..., typing.Awaitable]):
22
+ return Manager(get_union_types(return_type) or (return_type,), func) # type: ignore
23
+
24
+ return wrapper
25
+
26
+
27
+ @dataclasses.dataclass(frozen=True)
28
+ class Manager:
29
+ types: tuple[type, ...]
30
+ callback: typing.Callable[..., typing.Awaitable]
31
+
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)
37
+
38
+
39
+ class ABCReturnManager(ABC, typing.Generic[EventT]):
40
+ @abstractmethod
41
+ async def run(self, response: typing.Any, event: EventT, ctx: Context) -> None:
42
+ pass
43
+
44
+
45
+ class BaseReturnManager(ABCReturnManager[EventT]):
46
+ @property
47
+ def managers(self) -> list[Manager]:
48
+ return [
49
+ manager
50
+ for manager in (vars(BaseReturnManager) | vars(self.__class__)).values()
51
+ if isinstance(manager, Manager)
52
+ ]
53
+
54
+ @register_manager(Context)
55
+ @staticmethod
56
+ async def ctx_manager(value: Context, event: EventT, ctx: Context) -> None:
57
+ """Basic manager for returning context from handler."""
58
+
59
+ ctx.update(value)
60
+
61
+ async def run(self, response: typing.Any, event: EventT, ctx: Context) -> None:
62
+ for manager in self.managers:
63
+ if typing.Any in manager.types or any(type(response) is x for x in manager.types):
64
+ await manager(response, event, ctx)
65
+
66
+ @typing.overload
67
+ def register_manager(self, return_type: type[T]) -> typing.Callable[
68
+ [typing.Callable[[T, EventT, Context], typing.Awaitable]], Manager
69
+ ]:
70
+ ...
71
+
72
+ @typing.overload
73
+ def register_manager(self, return_type: tuple[type[T], ...]) -> typing.Callable[
74
+ [typing.Callable[[tuple[T, ...], EventT, Context], typing.Awaitable]], Manager
75
+ ]:
76
+ ...
77
+
78
+ def register_manager(self, return_type: type[T] | tuple[type[T], ...]) -> typing.Callable[
79
+ [typing.Callable[[T | tuple[T, ...], EventT, Context], typing.Awaitable]], Manager
80
+ ]:
81
+ def wrapper(func: typing.Callable[[T, EventT, Context], typing.Awaitable]) -> Manager:
82
+ manager = Manager(get_union_types(return_type) or (return_type,), func) # type: ignore
83
+ setattr(self.__class__, func.__name__, manager)
84
+ return manager
85
+
86
+ return wrapper
87
+
88
+
89
+ __all__ = (
90
+ "ABCReturnManager",
91
+ "BaseReturnManager",
92
+ "Manager",
93
+ "register_manager",
94
+ "get_union_types",
95
+ )
@@ -0,0 +1,19 @@
1
+ from telegrinder.bot.cute_types import CallbackQueryCute
2
+ from telegrinder.bot.dispatch.context import Context
3
+
4
+ from .abc import BaseReturnManager, register_manager
5
+
6
+
7
+ class CallbackQueryReturnManager(BaseReturnManager[CallbackQueryCute]):
8
+ @register_manager(str)
9
+ @staticmethod
10
+ async def str_manager(value: str, event: CallbackQueryCute, ctx: Context) -> None:
11
+ await event.answer(value)
12
+
13
+ @register_manager(dict)
14
+ @staticmethod
15
+ async def dict_manager(value: dict, event: CallbackQueryCute, ctx: Context) -> None:
16
+ await event.answer(**value)
17
+
18
+
19
+ __all__ = ("CallbackQueryReturnManager",)
@@ -0,0 +1,14 @@
1
+ from telegrinder.bot.cute_types import InlineQueryCute
2
+ from telegrinder.bot.dispatch.context import Context
3
+
4
+ from .abc import BaseReturnManager, register_manager
5
+
6
+
7
+ class InlineQueryReturnManager(BaseReturnManager[InlineQueryCute]):
8
+ @register_manager(dict)
9
+ @staticmethod
10
+ async def dict_manager(value: dict, event: InlineQueryCute, ctx: Context) -> None:
11
+ await event.answer(**value)
12
+
13
+
14
+ __all__ = ("InlineQueryReturnManager",)
@@ -0,0 +1,25 @@
1
+ from telegrinder.bot.cute_types import MessageCute
2
+ from telegrinder.bot.dispatch.context import Context
3
+
4
+ from .abc import BaseReturnManager, register_manager
5
+
6
+
7
+ class MessageReturnManager(BaseReturnManager[MessageCute]):
8
+ @register_manager(str)
9
+ @staticmethod
10
+ async def str_manager(value: str, event: MessageCute, ctx: Context) -> None:
11
+ await event.answer(value)
12
+
13
+ @register_manager(list | tuple)
14
+ @staticmethod
15
+ async def seq_manager(value: list[str] | tuple[str, ...], event: MessageCute, ctx: Context) -> None:
16
+ for message in value:
17
+ await event.answer(message)
18
+
19
+ @register_manager(dict)
20
+ @staticmethod
21
+ async def dict_manager(value: dict, event: MessageCute, ctx: Context) -> None:
22
+ await event.answer(**value)
23
+
24
+
25
+ __all__ = ("MessageReturnManager",)
@@ -1,4 +1,16 @@
1
- from .abc import ABCView
2
- from .message import MessageView
1
+ from .abc import ABCStateView, ABCView, BaseStateView, BaseView
2
+ from .box import ViewBox
3
3
  from .callback_query import CallbackQueryView
4
4
  from .inline_query import InlineQueryView
5
+ from .message import MessageView
6
+
7
+ __all__ = (
8
+ "ABCView",
9
+ "ABCStateView",
10
+ "BaseView",
11
+ "BaseStateView",
12
+ "CallbackQueryView",
13
+ "InlineQueryView",
14
+ "MessageView",
15
+ "ViewBox",
16
+ )
@@ -1,6 +1,22 @@
1
+ import typing
1
2
  from abc import ABC, abstractmethod
3
+
4
+ from fntypes.co import Nothing, Some
5
+
2
6
  from telegrinder.api.abc import ABCAPI
3
- from telegrinder.types import Update
7
+ from telegrinder.bot.cute_types.base import BaseCute
8
+ from telegrinder.bot.dispatch.handler.abc import ABCHandler
9
+ from telegrinder.bot.dispatch.handler.func import ErrorHandlerT, FuncHandler
10
+ from telegrinder.bot.dispatch.middleware.abc import ABCMiddleware
11
+ from telegrinder.bot.dispatch.process import process_inner
12
+ from telegrinder.bot.dispatch.return_manager.abc import ABCReturnManager
13
+ from telegrinder.bot.rules.abc import ABCRule
14
+ from telegrinder.model import Model
15
+ from telegrinder.msgspec_utils import Option
16
+ from telegrinder.types.objects import Update
17
+
18
+ EventType = typing.TypeVar("EventType", bound=BaseCute)
19
+ MiddlewareT = typing.TypeVar("MiddlewareT", bound=ABCMiddleware)
4
20
 
5
21
 
6
22
  class ABCView(ABC):
@@ -13,5 +29,115 @@ class ABCView(ABC):
13
29
  pass
14
30
 
15
31
  @abstractmethod
16
- async def load(self, external: "ABCView"):
32
+ def load(self, external: typing.Self) -> None:
17
33
  pass
34
+
35
+
36
+ class ABCStateView(ABCView, typing.Generic[EventType]):
37
+ @abstractmethod
38
+ def get_state_key(self, event: EventType) -> int | None:
39
+ pass
40
+
41
+ def __repr__(self) -> str:
42
+ return "<{!r}: {}>".format(
43
+ self.__class__.__name__,
44
+ ", ".join(f"{k}={v!r}" for k, v in self.__dict__.items())
45
+ )
46
+
47
+
48
+ class BaseView(ABCView, typing.Generic[EventType]):
49
+ auto_rules: list[ABCRule[EventType]]
50
+ handlers: list[ABCHandler[EventType]]
51
+ middlewares: list[ABCMiddleware[EventType]]
52
+ return_manager: ABCReturnManager[EventType]
53
+
54
+ @classmethod
55
+ def get_event_type(cls) -> Option[type[EventType]]:
56
+ for base in cls.__dict__.get("__orig_bases__", ()):
57
+ if issubclass(typing.get_origin(base) or base, ABCView):
58
+ for generic_type in typing.get_args(base):
59
+ if issubclass(typing.get_origin(generic_type) or generic_type, BaseCute):
60
+ return Some(generic_type)
61
+ return Nothing()
62
+
63
+ @classmethod
64
+ def get_raw_event(cls, update: Update) -> Option[Model]:
65
+ match update.update_type:
66
+ case Some(update_type):
67
+ return getattr(update, update_type.value)
68
+ case _:
69
+ return Nothing()
70
+
71
+ def __call__(
72
+ self,
73
+ *rules: ABCRule[EventType],
74
+ is_blocking: bool = True,
75
+ error_handler: ErrorHandlerT | None = None,
76
+ ):
77
+ def wrapper(
78
+ func: typing.Callable[
79
+ typing.Concatenate[EventType, ...],
80
+ typing.Coroutine,
81
+ ]
82
+ ):
83
+ func_handler = FuncHandler(
84
+ func,
85
+ [*self.auto_rules, *rules],
86
+ is_blocking,
87
+ dataclass=None,
88
+ error_handler=error_handler,
89
+ )
90
+ self.handlers.append(func_handler)
91
+ return func_handler
92
+
93
+ return wrapper
94
+
95
+ def register_middleware(self, *args: typing.Any, **kwargs: typing.Any):
96
+ def wrapper(cls: type[MiddlewareT]):
97
+ self.middlewares.append(cls(*args, **kwargs))
98
+ return cls
99
+
100
+ return wrapper
101
+
102
+ async def check(self, event: Update) -> bool:
103
+ match self.get_raw_event(event):
104
+ case Some(e) if issubclass(
105
+ self.get_event_type().expect(
106
+ "{!r} has no event type in generic.".format(self.__class__.__name__),
107
+ ),
108
+ e.__class__,
109
+ ):
110
+ return True
111
+ case _:
112
+ return False
113
+
114
+ async def process(self, event: Update, api: ABCAPI) -> bool:
115
+ return await process_inner(
116
+ self.get_event_type().unwrap().from_update(
117
+ update=self.get_raw_event(event).unwrap(),
118
+ bound_api=api,
119
+ ),
120
+ event,
121
+ self.middlewares,
122
+ self.handlers,
123
+ self.return_manager,
124
+ )
125
+
126
+ def load(self, external: typing.Self) -> None:
127
+ self.auto_rules.extend(external.auto_rules)
128
+ self.handlers.extend(external.handlers)
129
+ self.middlewares.extend(external.middlewares)
130
+
131
+
132
+ class BaseStateView(ABCStateView[EventType], BaseView[EventType], ABC, typing.Generic[EventType]):
133
+ @abstractmethod
134
+ def get_state_key(self, event: EventType) -> int | None:
135
+ pass
136
+
137
+
138
+ __all__ = (
139
+ "ABCView",
140
+ "ABCStateView",
141
+ "BaseView",
142
+ "BaseStateView",
143
+ )
@@ -0,0 +1,38 @@
1
+ import dataclasses
2
+
3
+ import typing_extensions as typing
4
+
5
+ from .abc import ABCView
6
+ from .callback_query import CallbackQueryView
7
+ from .inline_query import InlineQueryView
8
+ from .message import MessageView
9
+
10
+ CallbackQueryViewT = typing.TypeVar(
11
+ "CallbackQueryViewT", bound=ABCView, default=CallbackQueryView
12
+ )
13
+ InlineQueryViewT = typing.TypeVar(
14
+ "InlineQueryViewT", bound=ABCView, default=InlineQueryView
15
+ )
16
+ MessageViewT = typing.TypeVar("MessageViewT", bound=ABCView, default=MessageView)
17
+
18
+
19
+ @dataclasses.dataclass(kw_only=True)
20
+ class ViewBox(typing.Generic[CallbackQueryViewT, InlineQueryViewT, MessageViewT]):
21
+ callback_query: CallbackQueryViewT = dataclasses.field( # type: ignore
22
+ default_factory=lambda: CallbackQueryView(),
23
+ )
24
+ inline_query: InlineQueryViewT = dataclasses.field( # type: ignore
25
+ default_factory=lambda: InlineQueryView(),
26
+ )
27
+ message: MessageViewT = dataclasses.field( # type: ignore
28
+ default_factory=lambda: MessageView(),
29
+ )
30
+
31
+ def get_views(self) -> dict[str, ABCView]:
32
+ return {
33
+ name: view for name, view in self.__dict__.items()
34
+ if isinstance(view, ABCView)
35
+ }
36
+
37
+
38
+ __all__ = ("ViewBox",)
@@ -1,46 +1,20 @@
1
- from .abc import ABCView
2
- from telegrinder.bot.dispatch.handler import ABCHandler, FuncHandler
3
- from telegrinder.bot.dispatch.waiter import Waiter
4
- from telegrinder.bot.dispatch.middleware import ABCMiddleware
5
- from telegrinder.bot.rules import ABCRule
6
- from telegrinder.bot.cute_types import CallbackQueryCute
7
- from telegrinder.api.abc import ABCAPI
8
- from telegrinder.bot.dispatch.waiter import WithWaiter
9
- from telegrinder.bot.dispatch.process import process_waiters, process_inner
10
- from telegrinder.types import Update
11
- import typing
12
-
1
+ from fntypes.option import Some
13
2
 
14
- class CallbackQueryView(ABCView, WithWaiter[int, CallbackQueryCute]):
15
- def __init__(self):
16
- self.auto_rules: list[ABCRule] = []
17
- self.handlers: typing.List[ABCHandler[CallbackQueryCute]] = []
18
- self.middlewares: typing.List[ABCMiddleware[CallbackQueryCute]] = []
19
- self.short_waiters: typing.Dict[int, Waiter] = {}
20
-
21
- def __call__(self, *rules: ABCRule, is_blocking: bool = True):
22
- def wrapper(func: typing.Callable[..., typing.Coroutine]):
23
- self.handlers.append(
24
- FuncHandler(func, [*self.auto_rules, *rules], is_blocking, dataclass=None)
25
- )
26
- return func
3
+ from telegrinder.bot.cute_types import CallbackQueryCute
4
+ from telegrinder.bot.dispatch.return_manager import CallbackQueryReturnManager
27
5
 
28
- return wrapper
6
+ from .abc import BaseStateView
29
7
 
30
- async def check(self, event: Update) -> bool:
31
- return bool(event.callback_query)
32
8
 
33
- async def process(self, event: Update, api: ABCAPI):
34
- query = CallbackQueryCute(**event.callback_query.to_dict(), api=api)
9
+ class CallbackQueryView(BaseStateView[CallbackQueryCute]):
10
+ def __init__(self) -> None:
11
+ self.auto_rules = []
12
+ self.handlers = []
13
+ self.middlewares = []
14
+ self.return_manager = CallbackQueryReturnManager()
35
15
 
36
- if await process_waiters(
37
- self.short_waiters, query.message.message_id, query, event, query.answer
38
- ):
39
- return
16
+ def get_state_key(self, event: CallbackQueryCute) -> int | None:
17
+ return event.message.map(lambda variative: variative.v.message_id).unwrap_or_none()
40
18
 
41
- return await process_inner(query, event, self.middlewares, self.handlers)
42
19
 
43
- def load(self, external: "CallbackQueryView"):
44
- self.handlers.extend(external.handlers)
45
- self.middlewares.extend(external.middlewares)
46
- external.short_waiters = self.short_waiters
20
+ __all__ = ("CallbackQueryView",)
@@ -1,46 +1,18 @@
1
- from .abc import ABCView
2
- from telegrinder.bot.dispatch.handler import ABCHandler, FuncHandler
3
- from telegrinder.bot.dispatch.waiter import Waiter
4
- from telegrinder.bot.dispatch.middleware import ABCMiddleware
5
- from telegrinder.bot.rules import ABCRule
6
1
  from telegrinder.bot.cute_types import InlineQueryCute
7
- from telegrinder.api.abc import ABCAPI
8
- from telegrinder.bot.dispatch.waiter import WithWaiter
9
- from telegrinder.bot.dispatch.process import process_waiters, process_inner
10
- from telegrinder.types import Update
11
- import typing
2
+ from telegrinder.bot.dispatch.return_manager import InlineQueryReturnManager
12
3
 
4
+ from .abc import BaseStateView
13
5
 
14
- class InlineQueryView(ABCView, WithWaiter[str, InlineQueryCute]):
15
- def __init__(self):
16
- self.auto_rules: list[ABCRule] = []
17
- self.handlers: typing.List[ABCHandler[InlineQueryCute]] = []
18
- self.middlewares: typing.List[ABCMiddleware[InlineQueryCute]] = []
19
- self.short_waiters: typing.Dict[str, Waiter] = {}
20
6
 
21
- def __call__(self, *rules: ABCRule, is_blocking: bool = True):
22
- def wrapper(func: typing.Callable[..., typing.Coroutine]):
23
- self.handlers.append(
24
- FuncHandler(func, [*self.auto_rules, *rules], is_blocking, dataclass=None)
25
- )
26
- return func
7
+ class InlineQueryView(BaseStateView[InlineQueryCute]):
8
+ def __init__(self) -> None:
9
+ self.auto_rules = []
10
+ self.handlers = []
11
+ self.middlewares = []
12
+ self.return_manager = InlineQueryReturnManager()
27
13
 
28
- return wrapper
14
+ def get_state_key(self, event: InlineQueryCute) -> int | None:
15
+ return event.from_.id
29
16
 
30
- def load(self, external: "InlineQueryView"):
31
- self.handlers.extend(external.handlers)
32
- self.middlewares.extend(external.middlewares)
33
- external.short_waiters = self.short_waiters
34
17
 
35
- async def check(self, event: Update) -> bool:
36
- return bool(event.inline_query)
37
-
38
- async def process(self, event: Update, api: ABCAPI):
39
- query = InlineQueryCute(**event.inline_query.to_dict(), api=api)
40
-
41
- if await process_waiters(
42
- self.short_waiters, query.id, query, event, query.answer
43
- ):
44
- return
45
-
46
- return await process_inner(query, event, self.middlewares, self.handlers)
18
+ __all__ = ("InlineQueryView",)