telegrinder 0.1.dev166__py3-none-any.whl → 0.1.dev167__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 (62) hide show
  1. telegrinder/__init__.py +0 -2
  2. telegrinder/bot/__init__.py +0 -2
  3. telegrinder/bot/bot.py +1 -3
  4. telegrinder/bot/cute_types/base.py +3 -12
  5. telegrinder/bot/cute_types/callback_query.py +1 -3
  6. telegrinder/bot/cute_types/chat_join_request.py +3 -1
  7. telegrinder/bot/cute_types/chat_member_updated.py +3 -1
  8. telegrinder/bot/cute_types/message.py +10 -31
  9. telegrinder/bot/cute_types/utils.py +1 -3
  10. telegrinder/bot/dispatch/__init__.py +1 -2
  11. telegrinder/bot/dispatch/composition.py +1 -3
  12. telegrinder/bot/dispatch/dispatch.py +1 -3
  13. telegrinder/bot/dispatch/handler/func.py +3 -10
  14. telegrinder/bot/dispatch/return_manager/abc.py +9 -13
  15. telegrinder/bot/dispatch/return_manager/message.py +5 -7
  16. telegrinder/bot/dispatch/view/abc.py +1 -3
  17. telegrinder/bot/dispatch/view/box.py +3 -11
  18. telegrinder/bot/dispatch/view/raw.py +2 -6
  19. telegrinder/bot/dispatch/waiter_machine/__init__.py +1 -2
  20. telegrinder/bot/dispatch/waiter_machine/machine.py +35 -74
  21. telegrinder/bot/dispatch/waiter_machine/middleware.py +12 -5
  22. telegrinder/bot/dispatch/waiter_machine/short_state.py +6 -6
  23. telegrinder/bot/polling/polling.py +2 -6
  24. telegrinder/bot/rules/adapter/event.py +1 -3
  25. telegrinder/bot/rules/callback_data.py +3 -1
  26. telegrinder/bot/rules/fuzzy.py +1 -2
  27. telegrinder/bot/rules/is_from.py +6 -4
  28. telegrinder/bot/rules/markup.py +1 -2
  29. telegrinder/bot/rules/mention.py +1 -4
  30. telegrinder/bot/rules/regex.py +1 -2
  31. telegrinder/bot/rules/rule_enum.py +1 -3
  32. telegrinder/bot/rules/start.py +1 -3
  33. telegrinder/bot/scenario/checkbox.py +1 -5
  34. telegrinder/client/aiohttp.py +1 -3
  35. telegrinder/model.py +4 -3
  36. telegrinder/modules.py +1 -3
  37. telegrinder/msgspec_utils.py +1 -3
  38. telegrinder/node/attachment.py +18 -14
  39. telegrinder/node/base.py +4 -11
  40. telegrinder/node/composer.py +1 -3
  41. telegrinder/node/message.py +3 -1
  42. telegrinder/node/source.py +3 -1
  43. telegrinder/node/text.py +3 -1
  44. telegrinder/tools/__init__.py +2 -0
  45. telegrinder/tools/buttons.py +4 -6
  46. telegrinder/tools/error_handler/abc.py +1 -3
  47. telegrinder/tools/error_handler/error.py +3 -6
  48. telegrinder/tools/error_handler/error_handler.py +17 -13
  49. telegrinder/tools/formatting/html.py +2 -6
  50. telegrinder/tools/formatting/links.py +1 -3
  51. telegrinder/tools/global_context/abc.py +1 -3
  52. telegrinder/tools/global_context/global_context.py +13 -31
  53. telegrinder/tools/i18n/middleware/base.py +1 -3
  54. telegrinder/tools/limited_dict.py +27 -0
  55. telegrinder/tools/loop_wrapper/loop_wrapper.py +3 -7
  56. telegrinder/types/__init__.py +30 -0
  57. telegrinder/types/objects.py +4 -4
  58. telegrinder/verification_utils.py +2 -1
  59. {telegrinder-0.1.dev166.dist-info → telegrinder-0.1.dev167.dist-info}/METADATA +1 -1
  60. {telegrinder-0.1.dev166.dist-info → telegrinder-0.1.dev167.dist-info}/RECORD +62 -61
  61. {telegrinder-0.1.dev166.dist-info → telegrinder-0.1.dev167.dist-info}/LICENSE +0 -0
  62. {telegrinder-0.1.dev166.dist-info → telegrinder-0.1.dev167.dist-info}/WHEEL +0 -0
telegrinder/__init__.py CHANGED
@@ -69,7 +69,6 @@ from .bot import (
69
69
  Polling,
70
70
  RawEventView,
71
71
  ShortState,
72
- ShortStateStorage,
73
72
  SingleChoice,
74
73
  Telegrinder,
75
74
  UpdateCute,
@@ -189,7 +188,6 @@ __all__ = (
189
188
  "RawEventView",
190
189
  "RowButtons",
191
190
  "ShortState",
192
- "ShortStateStorage",
193
191
  "SimpleI18n",
194
192
  "SimpleTranslator",
195
193
  "SingleChoice",
@@ -33,7 +33,6 @@ from .dispatch import (
33
33
  MessageView,
34
34
  RawEventView,
35
35
  ShortState,
36
- ShortStateStorage,
37
36
  ViewBox,
38
37
  WaiterMachine,
39
38
  register_manager,
@@ -80,7 +79,6 @@ __all__ = (
80
79
  "Polling",
81
80
  "RawEventView",
82
81
  "ShortState",
83
- "ShortStateStorage",
84
82
  "SingleChoice",
85
83
  "Telegrinder",
86
84
  "UpdateCute",
telegrinder/bot/bot.py CHANGED
@@ -57,9 +57,7 @@ class Telegrinder(typing.Generic[DispatchT, PollingT, LoopWrapperT]):
57
57
 
58
58
  def run_forever(self, *, offset: int = 0, skip_updates: bool = False) -> None:
59
59
  logger.debug("Running blocking polling (id={})", self.api.id)
60
- self.loop_wrapper.add_task(
61
- self.run_polling(offset=offset, skip_updates=skip_updates)
62
- )
60
+ self.loop_wrapper.add_task(self.run_polling(offset=offset, skip_updates=skip_updates))
63
61
  self.loop_wrapper.run_event_loop()
64
62
 
65
63
 
@@ -74,9 +74,7 @@ def compose_method_params(
74
74
  if param_name not in params:
75
75
  if param_name in validators and not validators[param_name](update):
76
76
  continue
77
- params[param_name] = getattr(
78
- update, param if isinstance(param, str) else param[1]
79
- )
77
+ params[param_name] = getattr(update, param if isinstance(param, str) else param[1])
80
78
 
81
79
  return params
82
80
 
@@ -105,21 +103,14 @@ def shortcut(
105
103
  index = 0
106
104
 
107
105
  for k, p in signature_params.items():
108
- if (
109
- p.kind in (p.POSITIONAL_OR_KEYWORD, p.POSITIONAL_ONLY)
110
- and len(args) > index
111
- ):
106
+ if p.kind in (p.POSITIONAL_OR_KEYWORD, p.POSITIONAL_ONLY) and len(args) > index:
112
107
  params[k] = args[index]
113
108
  index += 1
114
109
  continue
115
110
  if p.kind in (p.VAR_KEYWORD, p.VAR_POSITIONAL):
116
111
  params[k] = kwargs.copy() if p.kind is p.VAR_KEYWORD else args[index:]
117
112
  continue
118
- params[k] = (
119
- kwargs.pop(k, p.default)
120
- if p.default is not p.empty
121
- else kwargs.pop(k)
122
- )
113
+ params[k] = kwargs.pop(k, p.default) if p.default is not p.empty else kwargs.pop(k)
123
114
 
124
115
  return await executor(self, method_name, get_params(params))
125
116
 
@@ -44,9 +44,7 @@ class CallbackQueryCute(BaseCute[CallbackQuery], CallbackQuery, kw_only=True, di
44
44
  by the bot with the callback button that originated the query."""
45
45
 
46
46
  return self.message.map(
47
- lambda m: m.only()
48
- .map(lambda m: m.is_topic_message.unwrap_or(False))
49
- .unwrap_or(False)
47
+ lambda m: m.only().map(lambda m: m.is_topic_message.unwrap_or(False)).unwrap_or(False)
50
48
  )
51
49
 
52
50
  @property
@@ -9,7 +9,9 @@ from .base import BaseCute, shortcut
9
9
  from .chat_member_updated import ChatMemberShortcuts, chat_member_interaction
10
10
 
11
11
 
12
- class ChatJoinRequestCute(BaseCute[ChatJoinRequest], ChatJoinRequest, ChatMemberShortcuts, kw_only=True):
12
+ class ChatJoinRequestCute(
13
+ BaseCute[ChatJoinRequest], ChatJoinRequest, ChatMemberShortcuts, kw_only=True
14
+ ):
13
15
  api: ABCAPI
14
16
 
15
17
  @property
@@ -232,7 +232,9 @@ class ChatMemberShortcuts:
232
232
  ...
233
233
 
234
234
 
235
- class ChatMemberUpdatedCute(BaseCute[ChatMemberUpdated], ChatMemberUpdated, ChatMemberShortcuts, kw_only=True):
235
+ class ChatMemberUpdatedCute(
236
+ BaseCute[ChatMemberUpdated], ChatMemberUpdated, ChatMemberShortcuts, kw_only=True
237
+ ):
236
238
  api: ABCAPI
237
239
 
238
240
  @property
@@ -71,16 +71,12 @@ async def execute_method_answer(
71
71
  link_preview_options = params.get("link_preview_options")
72
72
 
73
73
  if reply_parameters is not None and isinstance(reply_parameters, dict):
74
- reply_parameters.setdefault(
75
- "message_id", params.get("message_id", message.message_id)
76
- )
74
+ reply_parameters.setdefault("message_id", params.get("message_id", message.message_id))
77
75
  reply_parameters.setdefault("chat_id", params.get("chat_id"))
78
76
  params["reply_parameters"] = compose_reply_params(**reply_parameters)
79
77
 
80
78
  if link_preview_options is not None and isinstance(link_preview_options, dict):
81
- params["link_preview_options"] = compose_link_preview_options(
82
- **link_preview_options
83
- )
79
+ params["link_preview_options"] = compose_link_preview_options(**link_preview_options)
84
80
 
85
81
  result = await getattr(message.ctx_api, method_name)(**params)
86
82
  return result.map(
@@ -124,8 +120,7 @@ async def execute_method_edit(
124
120
  "message_thread_id": lambda x: (
125
121
  x.is_topic_message.unwrap_or(False)
126
122
  if isinstance(x, MessageCute)
127
- else bool(x.message)
128
- and getattr(x.message.unwrap().v, "is_topic_message", False)
123
+ else bool(x.message) and getattr(x.message.unwrap().v, "is_topic_message", False)
129
124
  ),
130
125
  },
131
126
  )
@@ -330,9 +325,7 @@ class MessageCute(BaseCute[Message], Message, kw_only=True):
330
325
  params=get_params(locals()),
331
326
  update=self,
332
327
  default_params={"chat_id", "message_id", "message_thread_id"},
333
- validators={
334
- "message_thread_id": lambda x: x.is_topic_message.unwrap_or(False)
335
- },
328
+ validators={"message_thread_id": lambda x: x.is_topic_message.unwrap_or(False)},
336
329
  )
337
330
  return await self.ctx_api.delete_message(**params)
338
331
 
@@ -450,9 +443,7 @@ class MessageCute(BaseCute[Message], Message, kw_only=True):
450
443
  ("from_chat_id", "chat_id"),
451
444
  "message_thread_id",
452
445
  },
453
- validators={
454
- "message_thread_id": lambda x: x.is_topic_message.unwrap_or(False)
455
- },
446
+ validators={"message_thread_id": lambda x: x.is_topic_message.unwrap_or(False)},
456
447
  )
457
448
  if isinstance(reply_parameters, dict):
458
449
  reply_parameters.setdefault("message_id", params.get("message_id"))
@@ -467,11 +458,7 @@ class MessageCute(BaseCute[Message], Message, kw_only=True):
467
458
  async def react(
468
459
  self,
469
460
  reaction: (
470
- str
471
- | ReactionEmoji
472
- | ReactionType
473
- | list[str | ReactionEmoji | ReactionType]
474
- | None
461
+ str | ReactionEmoji | ReactionType | list[str | ReactionEmoji | ReactionType] | None
475
462
  ) = None,
476
463
  chat_id: int | str | None = None,
477
464
  message_thread_id: int | None = None,
@@ -507,9 +494,7 @@ class MessageCute(BaseCute[Message], Message, kw_only=True):
507
494
  params=get_params(locals()),
508
495
  update=self,
509
496
  default_params={"chat_id", "message_id", "message_thread_id"},
510
- validators={
511
- "message_thread_id": lambda x: x.is_topic_message.unwrap_or(False)
512
- },
497
+ validators={"message_thread_id": lambda x: x.is_topic_message.unwrap_or(False)},
513
498
  )
514
499
  if reaction:
515
500
  params["reaction"] = compose_reactions(
@@ -557,9 +542,7 @@ class MessageCute(BaseCute[Message], Message, kw_only=True):
557
542
  "message_id",
558
543
  "message_thread_id",
559
544
  },
560
- validators={
561
- "message_thread_id": lambda x: x.is_topic_message.unwrap_or(False)
562
- },
545
+ validators={"message_thread_id": lambda x: x.is_topic_message.unwrap_or(False)},
563
546
  )
564
547
  return (await self.ctx_api.forward_message(**params)).map(
565
548
  lambda message: MessageCute.from_update(message, bound_api=self.api),
@@ -599,9 +582,7 @@ class MessageCute(BaseCute[Message], Message, kw_only=True):
599
582
  params=get_params(locals()),
600
583
  update=self,
601
584
  default_params={"chat_id", "message_id", "message_thread_id"},
602
- validators={
603
- "message_thread_id": lambda x: x.is_topic_message.unwrap_or(False)
604
- },
585
+ validators={"message_thread_id": lambda x: x.is_topic_message.unwrap_or(False)},
605
586
  )
606
587
  return await self.ctx_api.pin_chat_message(**params)
607
588
 
@@ -632,9 +613,7 @@ class MessageCute(BaseCute[Message], Message, kw_only=True):
632
613
  params=get_params(locals()),
633
614
  update=self,
634
615
  default_params={"chat_id", "message_id", "message_thread_id"},
635
- validators={
636
- "message_thread_id": lambda x: x.is_topic_message.unwrap_or(False)
637
- },
616
+ validators={"message_thread_id": lambda x: x.is_topic_message.unwrap_or(False)},
638
617
  )
639
618
  return await self.ctx_api.pin_chat_message(**params)
640
619
 
@@ -69,9 +69,7 @@ INPUT_MEDIA_TYPES: typing.Final[dict[str, type[InputMedia]]] = {
69
69
 
70
70
 
71
71
  def compose_reactions(
72
- reactions: (
73
- str | ReactionEmoji | ReactionType | list[str | ReactionEmoji | ReactionType]
74
- ),
72
+ reactions: (str | ReactionEmoji | ReactionType | list[str | ReactionEmoji | ReactionType]),
75
73
  /,
76
74
  ) -> list[ReactionType]:
77
75
  if not isinstance(reactions, list):
@@ -27,7 +27,7 @@ from .view import (
27
27
  RawEventView,
28
28
  ViewBox,
29
29
  )
30
- from .waiter_machine import ShortState, ShortStateStorage, WaiterMachine
30
+ from .waiter_machine import ShortState, WaiterMachine
31
31
 
32
32
  __all__ = (
33
33
  "ABCDispatch",
@@ -56,7 +56,6 @@ __all__ = (
56
56
  "MessageView",
57
57
  "RawEventView",
58
58
  "ShortState",
59
- "ShortStateStorage",
60
59
  "TelegrinderCtx",
61
60
  "ViewBox",
62
61
  "WaiterMachine",
@@ -78,9 +78,7 @@ class CompositionDispatch(ABCDispatch):
78
78
  def wrapper(func: typing.Callable):
79
79
  composition = Composition(func, is_blocking)
80
80
  if container_nodes:
81
- composition.nodes["container"] = ContainerNode.link_nodes(
82
- list(container_nodes)
83
- )
81
+ composition.nodes["container"] = ContainerNode.link_nodes(list(container_nodes))
84
82
  self.compositions.append(composition)
85
83
  return func
86
84
 
@@ -160,9 +160,7 @@ class Dispatch(
160
160
  def load(self, external: typing.Self) -> None:
161
161
  view_external = external.get_views()
162
162
  for name, view in self.get_views().items():
163
- assert (
164
- name in view_external
165
- ), f"View {name!r} is undefined in external dispatch."
163
+ assert name in view_external, f"View {name!r} is undefined in external dispatch."
166
164
  view.load(view_external[name])
167
165
  setattr(external, name, view)
168
166
 
@@ -18,14 +18,10 @@ if typing.TYPE_CHECKING:
18
18
 
19
19
  F = typing.TypeVar(
20
20
  "F",
21
- bound=typing.Callable[
22
- typing.Concatenate[typing.Any, ...], typing.Awaitable[typing.Any]
23
- ],
21
+ bound=typing.Callable[typing.Concatenate[typing.Any, ...], typing.Awaitable[typing.Any]],
24
22
  )
25
23
  EventT = typing.TypeVar("EventT", bound=BaseCute)
26
- ErrorHandlerT = typing.TypeVar(
27
- "ErrorHandlerT", bound=ABCErrorHandler, default=ErrorHandler
28
- )
24
+ ErrorHandlerT = typing.TypeVar("ErrorHandlerT", bound=ABCErrorHandler, default=ErrorHandler)
29
25
 
30
26
 
31
27
  @dataclasses.dataclass(repr=False)
@@ -52,10 +48,7 @@ class FuncHandler(ABCHandler[EventT], typing.Generic[EventT, F, ErrorHandlerT]):
52
48
  )
53
49
 
54
50
  async def check(self, api: ABCAPI, event: Update, ctx: Context | None = None) -> bool:
55
- if (
56
- self.update_type is not None
57
- and self.update_type != event.update_type.unwrap_or_none()
58
- ):
51
+ if self.update_type is not None and self.update_type != event.update_type.unwrap_or_none():
59
52
  return False
60
53
  ctx = ctx or Context()
61
54
  temp_ctx = ctx.copy()
@@ -66,9 +66,7 @@ class BaseReturnManager(ABCReturnManager[EventT]):
66
66
 
67
67
  async def run(self, response: typing.Any, event: EventT, ctx: Context) -> None:
68
68
  for manager in self.managers:
69
- if typing.Any in manager.types or any(
70
- type(response) is x for x in manager.types
71
- ):
69
+ if typing.Any in manager.types or any(type(response) is x for x in manager.types):
72
70
  await manager(response, event, ctx)
73
71
 
74
72
  @typing.overload
@@ -79,24 +77,22 @@ class BaseReturnManager(ABCReturnManager[EventT]):
79
77
  ]: ...
80
78
 
81
79
  @typing.overload
82
- def register_manager(self, return_type: tuple[type[T], ...]) -> typing.Callable[
80
+ def register_manager(
81
+ self,
82
+ return_type: tuple[type[T], ...],
83
+ ) -> typing.Callable[
83
84
  [typing.Callable[[tuple[T, ...], EventT, Context], typing.Awaitable[typing.Any]]],
84
85
  Manager,
85
86
  ]: ...
86
87
 
87
88
  def register_manager(
88
- self, return_type: type[T] | tuple[type[T], ...]
89
+ self,
90
+ return_type: type[T] | tuple[type[T], ...],
89
91
  ) -> typing.Callable[
90
- [
91
- typing.Callable[
92
- [T | tuple[T, ...], EventT, Context], typing.Awaitable[typing.Any]
93
- ]
94
- ],
92
+ [typing.Callable[[T | tuple[T, ...], EventT, Context], typing.Awaitable[typing.Any]]],
95
93
  Manager,
96
94
  ]:
97
- def wrapper(
98
- func: typing.Callable[[T, EventT, Context], typing.Awaitable]
99
- ) -> Manager:
95
+ def wrapper(func: typing.Callable[[T, EventT, Context], typing.Awaitable]) -> Manager:
100
96
  manager = Manager(get_union_types(return_type) or (return_type,), func) # type: ignore
101
97
  setattr(self.__class__, func.__name__, manager)
102
98
  return manager
@@ -16,23 +16,21 @@ class MessageReturnManager(BaseReturnManager[MessageCute]):
16
16
  @register_manager(list | tuple)
17
17
  @staticmethod
18
18
  async def seq_manager(
19
- value: list[str] | tuple[str, ...], event: MessageCute, ctx: Context
19
+ value: list[str] | tuple[str, ...],
20
+ event: MessageCute,
21
+ ctx: Context,
20
22
  ) -> None:
21
23
  for message in value:
22
24
  await event.answer(message)
23
25
 
24
26
  @register_manager(dict)
25
27
  @staticmethod
26
- async def dict_manager(
27
- value: dict[str, typing.Any], event: MessageCute, ctx: Context
28
- ) -> None:
28
+ async def dict_manager(value: dict[str, typing.Any], event: MessageCute, ctx: Context) -> None:
29
29
  await event.answer(**value)
30
30
 
31
31
  @register_manager(HTMLFormatter)
32
32
  @staticmethod
33
- async def htmlformatter_manager(
34
- value: HTMLFormatter, event: MessageCute, ctx: Context
35
- ) -> None:
33
+ async def htmlformatter_manager(value: HTMLFormatter, event: MessageCute, ctx: Context) -> None:
36
34
  await event.answer(value, parse_mode=HTMLFormatter.PARSE_MODE)
37
35
 
38
36
 
@@ -63,9 +63,7 @@ class BaseView(ABCView, typing.Generic[EventType]):
63
63
  for base in cls.__dict__.get("__orig_bases__", ()):
64
64
  if issubclass(typing.get_origin(base) or base, ABCView):
65
65
  for generic_type in typing.get_args(base):
66
- if issubclass(
67
- typing.get_origin(generic_type) or generic_type, BaseCute
68
- ):
66
+ if issubclass(typing.get_origin(generic_type) or generic_type, BaseCute):
69
67
  return Some(generic_type)
70
68
  return Nothing()
71
69
 
@@ -12,16 +12,12 @@ from .inline_query import InlineQueryView
12
12
  from .message import MessageView
13
13
  from .raw import RawEventView
14
14
 
15
- CallbackQueryViewT = typing.TypeVar(
16
- "CallbackQueryViewT", bound=ABCView, default=CallbackQueryView
17
- )
15
+ CallbackQueryViewT = typing.TypeVar("CallbackQueryViewT", bound=ABCView, default=CallbackQueryView)
18
16
  ChatJoinRequestViewT = typing.TypeVar(
19
17
  "ChatJoinRequestViewT", bound=ABCView, default=ChatJoinRequestView
20
18
  )
21
19
  ChatMemberViewT = typing.TypeVar("ChatMemberViewT", bound=ABCView, default=ChatMemberView)
22
- InlineQueryViewT = typing.TypeVar(
23
- "InlineQueryViewT", bound=ABCView, default=InlineQueryView
24
- )
20
+ InlineQueryViewT = typing.TypeVar("InlineQueryViewT", bound=ABCView, default=InlineQueryView)
25
21
  MessageViewT = typing.TypeVar("MessageViewT", bound=ABCView, default=MessageView)
26
22
  RawEventViewT = typing.TypeVar("RawEventViewT", bound=ABCView, default=RawEventView)
27
23
 
@@ -100,11 +96,7 @@ class ViewBox(
100
96
  def get_views(self) -> dict[str, ABCView]:
101
97
  """Get all views."""
102
98
 
103
- return {
104
- name: view
105
- for name, view in self.__dict__.items()
106
- if isinstance(view, ABCView)
107
- }
99
+ return {name: view for name, view in self.__dict__.items() if isinstance(view, ABCView)}
108
100
 
109
101
 
110
102
  __all__ = ("ViewBox",)
@@ -41,9 +41,7 @@ class RawEventView(BaseView[UpdateCute]):
41
41
  update_type: UpdateType,
42
42
  *rules: ABCRule[UpdateCute],
43
43
  dataclass: type[T],
44
- ) -> typing.Callable[
45
- [FuncType[T]], FuncHandler[UpdateCute, FuncType[T], ErrorHandler[T]]
46
- ]: ...
44
+ ) -> typing.Callable[[FuncType[T]], FuncHandler[UpdateCute, FuncType[T], ErrorHandler[T]]]: ...
47
45
 
48
46
  @typing.overload
49
47
  def __call__(
@@ -64,9 +62,7 @@ class RawEventView(BaseView[UpdateCute]):
64
62
  dataclass: type[T],
65
63
  error_handler: ErrorHandlerT,
66
64
  is_blocking: bool = True,
67
- ) -> typing.Callable[
68
- [FuncType[T]], FuncHandler[UpdateCute, FuncType[T], ErrorHandlerT]
69
- ]: ...
65
+ ) -> typing.Callable[[FuncType[T]], FuncHandler[UpdateCute, FuncType[T], ErrorHandlerT]]: ...
70
66
 
71
67
  @typing.overload
72
68
  def __call__(
@@ -1,10 +1,9 @@
1
- from .machine import ShortStateStorage, WaiterMachine
1
+ from .machine import WaiterMachine
2
2
  from .middleware import WaiterMiddleware
3
3
  from .short_state import ShortState
4
4
 
5
5
  __all__ = (
6
6
  "ShortState",
7
- "ShortStateStorage",
8
7
  "WaiterMachine",
9
8
  "WaiterMiddleware",
10
9
  )
@@ -1,79 +1,23 @@
1
1
  import asyncio
2
2
  import datetime
3
3
  import typing
4
- from collections import deque
5
4
 
6
5
  from telegrinder.api.abc import ABCAPI
7
6
  from telegrinder.bot.dispatch.context import Context
8
7
  from telegrinder.bot.rules.abc import ABCRule
8
+ from telegrinder.tools.limited_dict import LimitedDict
9
+ from telegrinder.types import Update
9
10
 
10
11
  from .middleware import WaiterMiddleware
11
12
  from .short_state import Behaviour, EventModel, ShortState
12
13
 
13
- T = typing.TypeVar("T")
14
-
15
- Storage: typing.TypeAlias = dict[str, "ShortStateStorage"]
16
- Identificator: typing.TypeAlias = str | int
17
-
18
14
  if typing.TYPE_CHECKING:
19
15
  from telegrinder.bot.dispatch.view.abc import ABCStateView, BaseStateView
20
16
 
17
+ T = typing.TypeVar("T")
21
18
 
22
- class ShortStateStorage(dict[Identificator, ShortState[EventModel]]):
23
- def __init__(self, *, maxlimit: int = 1000) -> None:
24
- super().__init__()
25
- self.maxlimit = maxlimit
26
- self.queue: deque[Identificator] = deque(maxlen=maxlimit)
27
-
28
- def __repr__(self) -> str:
29
- return "<{}: {}, (current={} | maxlimit={})>".format(
30
- self.__class__.__name__,
31
- super().__repr__(),
32
- len(self.queue),
33
- self.maxlimit,
34
- )
35
-
36
- def __setitem__(self, key: Identificator, value: ShortState[EventModel], /) -> None:
37
- self.add(key, value)
38
-
39
- def __delitem__(self, key: Identificator, /) -> None:
40
- self.pop(key, None)
41
-
42
- def add(self, id: Identificator, short_state: ShortState[EventModel]) -> None:
43
- if len(self.queue) >= self.maxlimit:
44
- self.pop(self.queue.popleft(), None)
45
- if id not in self.queue:
46
- self.queue.append(id)
47
- super().__setitem__(id, short_state)
48
-
49
- def clear(self) -> None:
50
- self.queue.clear()
51
- super().clear()
52
-
53
- def setdefault(self, id: Identificator, default: ShortState[EventModel]) -> ShortState[EventModel]:
54
- if id in self:
55
- return self[id]
56
- self.add(id, default)
57
- return default
58
-
59
- def pop(self, id: Identificator, default: T): # type: ignore
60
- if id in self.queue:
61
- self.queue.remove(id)
62
- return super().pop(id, default)
63
-
64
- def popitem(self) -> tuple[Identificator, ShortState[EventModel]]:
65
- item = super().popitem()
66
- self.queue.remove(item[0])
67
- return item
68
-
69
- def update(
70
- self,
71
- mapping: typing.Mapping[Identificator, ShortState[EventModel]] | None = None,
72
- /,
73
- **kwargs: ShortState[EventModel],
74
- ) -> None:
75
- for key, value in (mapping if mapping is not None else kwargs).items():
76
- self.add(key, value)
19
+ Identificator: typing.TypeAlias = str | int
20
+ Storage: typing.TypeAlias = dict[str, LimitedDict[Identificator, ShortState[EventModel]]]
77
21
 
78
22
 
79
23
  class WaiterMachine:
@@ -90,6 +34,7 @@ class WaiterMachine:
90
34
  self,
91
35
  state_view: "ABCStateView[EventModel]",
92
36
  id: Identificator,
37
+ update: Update,
93
38
  **context: typing.Any,
94
39
  ) -> None:
95
40
  view_name = state_view.__class__.__name__
@@ -100,8 +45,7 @@ class WaiterMachine:
100
45
  if short_state is None:
101
46
  raise LookupError(
102
47
  "Waiter with identificator {} is not found for view {!r}".format(
103
- id,
104
- view_name,
48
+ id, view_name
105
49
  )
106
50
  )
107
51
 
@@ -114,8 +58,9 @@ class WaiterMachine:
114
58
 
115
59
  await self.call_behaviour(
116
60
  state_view,
117
- short_state.on_drop_behaviour,
118
61
  short_state.event,
62
+ update,
63
+ behaviour=short_state.on_drop_behaviour,
119
64
  **context,
120
65
  )
121
66
 
@@ -126,14 +71,18 @@ class WaiterMachine:
126
71
  *rules: ABCRule[EventModel],
127
72
  default: Behaviour = None,
128
73
  on_drop: Behaviour = None,
129
- expiration: datetime.timedelta | int | float | None = None,
130
- short_state_storage: ShortStateStorage[EventModel] | None = None,
74
+ expiration: datetime.timedelta | int | None = None,
131
75
  ) -> tuple[EventModel, Context]:
132
76
  if isinstance(expiration, int | float):
133
77
  expiration = datetime.timedelta(seconds=expiration)
134
78
 
135
- api: ABCAPI; key: Identificator
136
- api, key = linked if isinstance(linked, tuple) else (linked.ctx_api, state_view.get_state_key(linked)) # type: ignore
79
+ api: ABCAPI
80
+ key: Identificator
81
+ api, key = (
82
+ linked
83
+ if isinstance(linked, tuple)
84
+ else (linked.ctx_api, state_view.get_state_key(linked))
85
+ ) # type: ignore
137
86
  if not key:
138
87
  raise RuntimeError("Unable to get state key.")
139
88
 
@@ -150,9 +99,9 @@ class WaiterMachine:
150
99
  view_name = state_view.__class__.__name__
151
100
  if view_name not in self.storage:
152
101
  state_view.middlewares.insert(0, WaiterMiddleware(self, state_view))
153
- self.storage[view_name] = short_state_storage or ShortStateStorage()
102
+ self.storage[view_name] = LimitedDict()
154
103
 
155
- self.storage[view_name].add(key, short_state)
104
+ self.storage[view_name][key] = short_state
156
105
  await event.wait()
157
106
 
158
107
  e, ctx = getattr(event, "context")
@@ -162,15 +111,27 @@ class WaiterMachine:
162
111
  async def call_behaviour(
163
112
  self,
164
113
  view: "ABCStateView[EventModel]",
165
- behaviour: Behaviour,
166
114
  event: asyncio.Event | EventModel,
115
+ update: Update,
116
+ behaviour: Behaviour[EventModel] | None = None,
167
117
  **context: typing.Any,
168
118
  ) -> None:
119
+ # TODO: support param view as a behaviour
120
+
121
+ ctx = Context(**context)
122
+
123
+ if isinstance(event, asyncio.Event):
124
+ event, preset_ctx = typing.cast(
125
+ tuple[EventModel, Context],
126
+ getattr(event, "context"),
127
+ )
128
+ ctx.update(preset_ctx)
129
+
169
130
  if behaviour is None:
170
131
  return
171
- # TODO: add behaviour check
172
- # TODO: support view as a behaviour
173
- await behaviour.run(event, context) # type: ignore
174
132
 
133
+ if await behaviour.check(event.api, update, ctx):
134
+ await behaviour.run(event, ctx)
135
+
175
136
 
176
137
  __all__ = ("WaiterMachine",)