telegrinder 0.2.2__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 (31) hide show
  1. telegrinder/__init__.py +24 -2
  2. telegrinder/bot/__init__.py +16 -0
  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 +14 -104
  6. telegrinder/bot/cute_types/inline_query.py +5 -14
  7. telegrinder/bot/cute_types/message.py +605 -1227
  8. telegrinder/bot/dispatch/__init__.py +22 -1
  9. telegrinder/bot/dispatch/abc.py +7 -0
  10. telegrinder/bot/dispatch/dispatch.py +7 -0
  11. telegrinder/bot/dispatch/waiter_machine/__init__.py +18 -0
  12. telegrinder/bot/dispatch/waiter_machine/actions.py +10 -0
  13. telegrinder/bot/dispatch/waiter_machine/hasher/__init__.py +15 -0
  14. telegrinder/bot/dispatch/waiter_machine/hasher/callback.py +60 -0
  15. telegrinder/bot/dispatch/waiter_machine/hasher/hasher.py +49 -0
  16. telegrinder/bot/dispatch/waiter_machine/hasher/message.py +54 -0
  17. telegrinder/bot/dispatch/waiter_machine/hasher/state.py +19 -0
  18. telegrinder/bot/dispatch/waiter_machine/machine.py +87 -99
  19. telegrinder/bot/dispatch/waiter_machine/middleware.py +23 -35
  20. telegrinder/bot/dispatch/waiter_machine/short_state.py +9 -9
  21. telegrinder/bot/scenario/checkbox.py +2 -2
  22. telegrinder/model.py +6 -4
  23. telegrinder/msgspec_json.py +1 -1
  24. telegrinder/msgspec_utils.py +51 -0
  25. telegrinder/node/event.py +2 -0
  26. telegrinder/tools/functional.py +9 -0
  27. telegrinder/tools/state_storage/memory.py +3 -3
  28. {telegrinder-0.2.2.dist-info → telegrinder-0.3.0.dist-info}/METADATA +2 -2
  29. {telegrinder-0.2.2.dist-info → telegrinder-0.3.0.dist-info}/RECORD +31 -24
  30. {telegrinder-0.2.2.dist-info → telegrinder-0.3.0.dist-info}/LICENSE +0 -0
  31. {telegrinder-0.2.2.dist-info → telegrinder-0.3.0.dist-info}/WHEEL +0 -0
@@ -36,7 +36,19 @@ from telegrinder.bot.dispatch.view import (
36
36
  RawEventView,
37
37
  ViewBox,
38
38
  )
39
- 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
+ )
40
52
 
41
53
  __all__ = (
42
54
  "ABCDispatch",
@@ -49,6 +61,9 @@ __all__ = (
49
61
  "BaseReturnManager",
50
62
  "BaseStateView",
51
63
  "BaseView",
64
+ "CALLBACK_QUERY_FOR_MESSAGE",
65
+ "CALLBACK_QUERY_FROM_CHAT",
66
+ "CALLBACK_QUERY_IN_CHAT_FOR_MESSAGE",
52
67
  "CallbackQueryReturnManager",
53
68
  "CallbackQueryView",
54
69
  "ChatJoinRequestView",
@@ -57,8 +72,12 @@ __all__ = (
57
72
  "Dispatch",
58
73
  "DocumentReplyHandler",
59
74
  "FuncHandler",
75
+ "Hasher",
60
76
  "InlineQueryReturnManager",
61
77
  "InlineQueryView",
78
+ "MESSAGE_FROM_USER",
79
+ "MESSAGE_FROM_USER_IN_CHAT",
80
+ "MESSAGE_IN_CHAT",
62
81
  "Manager",
63
82
  "MediaGroupReplyHandler",
64
83
  "MessageReplyHandler",
@@ -67,6 +86,7 @@ __all__ = (
67
86
  "PhotoReplyHandler",
68
87
  "RawEventView",
69
88
  "ShortState",
89
+ "StateViewHasher",
70
90
  "StickerReplyHandler",
71
91
  "TelegrinderContext",
72
92
  "VideoReplyHandler",
@@ -74,6 +94,7 @@ __all__ = (
74
94
  "WaiterMachine",
75
95
  "check_rule",
76
96
  "clear_wm_storage_worker",
97
+ "clear_wm_storage_worker",
77
98
  "process_inner",
78
99
  "register_manager",
79
100
  )
@@ -1,10 +1,14 @@
1
1
  import typing
2
2
  from abc import ABC, abstractmethod
3
3
 
4
+ from fntypes import Option
5
+
4
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",)
@@ -1,6 +1,7 @@
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
7
  from telegrinder.api.api import API
@@ -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,9 +1,27 @@
1
+ from telegrinder.bot.dispatch.waiter_machine.hasher import (
2
+ CALLBACK_QUERY_FOR_MESSAGE,
3
+ CALLBACK_QUERY_FROM_CHAT,
4
+ CALLBACK_QUERY_IN_CHAT_FOR_MESSAGE,
5
+ MESSAGE_FROM_USER,
6
+ MESSAGE_FROM_USER_IN_CHAT,
7
+ MESSAGE_IN_CHAT,
8
+ Hasher,
9
+ StateViewHasher,
10
+ )
1
11
  from telegrinder.bot.dispatch.waiter_machine.machine import WaiterMachine, clear_wm_storage_worker
2
12
  from telegrinder.bot.dispatch.waiter_machine.middleware import WaiterMiddleware
3
13
  from telegrinder.bot.dispatch.waiter_machine.short_state import ShortState
4
14
 
5
15
  __all__ = (
16
+ "CALLBACK_QUERY_FOR_MESSAGE",
17
+ "CALLBACK_QUERY_FROM_CHAT",
18
+ "CALLBACK_QUERY_IN_CHAT_FOR_MESSAGE",
19
+ "Hasher",
20
+ "MESSAGE_FROM_USER",
21
+ "MESSAGE_FROM_USER_IN_CHAT",
22
+ "MESSAGE_IN_CHAT",
6
23
  "ShortState",
24
+ "StateViewHasher",
7
25
  "WaiterMachine",
8
26
  "WaiterMiddleware",
9
27
  "clear_wm_storage_worker",
@@ -0,0 +1,10 @@
1
+ import typing
2
+
3
+ from telegrinder.bot.dispatch.handler.abc import ABCHandler
4
+
5
+ from .short_state import EventModel, ShortState
6
+
7
+
8
+ class WaiterActions(typing.TypedDict, typing.Generic[EventModel]):
9
+ on_miss: typing.NotRequired[ABCHandler[EventModel]]
10
+ on_drop: typing.NotRequired[typing.Callable[[ShortState[EventModel]], None]]
@@ -0,0 +1,15 @@
1
+ from .callback import CALLBACK_QUERY_FOR_MESSAGE, CALLBACK_QUERY_FROM_CHAT, CALLBACK_QUERY_IN_CHAT_FOR_MESSAGE
2
+ from .hasher import Hasher
3
+ from .message import MESSAGE_FROM_USER, MESSAGE_FROM_USER_IN_CHAT, MESSAGE_IN_CHAT
4
+ from .state import StateViewHasher
5
+
6
+ __all__ = (
7
+ "CALLBACK_QUERY_FOR_MESSAGE",
8
+ "CALLBACK_QUERY_FROM_CHAT",
9
+ "CALLBACK_QUERY_IN_CHAT_FOR_MESSAGE",
10
+ "Hasher",
11
+ "MESSAGE_FROM_USER",
12
+ "MESSAGE_FROM_USER_IN_CHAT",
13
+ "MESSAGE_IN_CHAT",
14
+ "StateViewHasher",
15
+ )
@@ -0,0 +1,60 @@
1
+ from fntypes import Some
2
+
3
+ from telegrinder.bot.cute_types import CallbackQueryCute as CallbackQuery
4
+ from telegrinder.bot.dispatch.view import CallbackQueryView
5
+
6
+ from .hasher import Hasher
7
+
8
+
9
+ def from_chat_hash(chat_id: int) -> int:
10
+ return chat_id
11
+
12
+
13
+ def get_chat_from_event(event: CallbackQuery) -> int | None:
14
+ return event.chat.and_then(lambda chat: Some(chat.id)).unwrap_or_none()
15
+
16
+
17
+ CALLBACK_QUERY_FROM_CHAT = Hasher(
18
+ view=CallbackQueryView,
19
+ get_hash_from_data=from_chat_hash,
20
+ get_data_from_event=get_chat_from_event,
21
+ )
22
+
23
+
24
+ def for_message_hash(message_id: int) -> int:
25
+ return message_id
26
+
27
+
28
+ def get_message_for_event(event: CallbackQuery) -> int | None:
29
+ return event.message_id.unwrap_or_none()
30
+
31
+
32
+ CALLBACK_QUERY_FOR_MESSAGE = Hasher(
33
+ view=CallbackQueryView,
34
+ get_hash_from_data=for_message_hash,
35
+ get_data_from_event=get_message_for_event,
36
+ )
37
+
38
+
39
+ def for_message_in_chat(chat_and_message: tuple[int, int]) -> str:
40
+ return f"{chat_and_message[0]}_{chat_and_message[1]}"
41
+
42
+
43
+ def get_chat_and_message_for_event(event: CallbackQuery) -> tuple[int, int] | None:
44
+ if not event.message_id or not event.chat:
45
+ return None
46
+ return event.chat.unwrap().id, event.message_id.unwrap()
47
+
48
+
49
+ CALLBACK_QUERY_IN_CHAT_FOR_MESSAGE = Hasher(
50
+ view=CallbackQueryView,
51
+ get_hash_from_data=for_message_in_chat,
52
+ get_data_from_event=get_chat_and_message_for_event,
53
+ )
54
+
55
+
56
+ __all__ = (
57
+ "CALLBACK_QUERY_FOR_MESSAGE",
58
+ "CALLBACK_QUERY_FROM_CHAT",
59
+ "CALLBACK_QUERY_IN_CHAT_FOR_MESSAGE",
60
+ )
@@ -0,0 +1,49 @@
1
+ import typing
2
+
3
+ from fntypes import Option
4
+
5
+ from telegrinder.bot.cute_types import BaseCute
6
+ from telegrinder.bot.dispatch.view.base import BaseView
7
+ from telegrinder.tools.functional import from_optional
8
+
9
+ Event = typing.TypeVar("Event", bound=BaseCute)
10
+ Data = typing.TypeVar("Data")
11
+
12
+ ECHO = lambda x: x
13
+
14
+
15
+ class Hasher(typing.Generic[Event, Data]):
16
+ def __init__(
17
+ self,
18
+ view: type[BaseView[Event]],
19
+ get_hash_from_data: typing.Callable[[Data], typing.Hashable | None] | None = None,
20
+ get_data_from_event: typing.Callable[[Event], Data | None] | None = None,
21
+ ):
22
+ self.view = view
23
+ self._get_hash_from_data = get_hash_from_data
24
+ self._get_data_from_event = get_data_from_event
25
+
26
+ def get_name(self) -> str:
27
+ return f"{self.view.__class__.__name__}_{id(self)}"
28
+
29
+ def get_hash_from_data(self, data: Data) -> Option[typing.Hashable]:
30
+ if self._get_hash_from_data is None:
31
+ raise NotImplementedError
32
+ return from_optional(self._get_hash_from_data(data))
33
+
34
+ def get_data_from_event(self, event: Event) -> Option[Data]:
35
+ if not self._get_data_from_event:
36
+ raise NotImplementedError
37
+ return from_optional(self._get_data_from_event(event))
38
+
39
+ def get_hash_from_data_from_event(self, event: Event) -> Option[typing.Hashable]:
40
+ return self.get_data_from_event(event).and_then(self.get_hash_from_data) # type: ignore
41
+
42
+ def __hash__(self) -> int:
43
+ return hash(self.get_name())
44
+
45
+ def __repr__(self) -> str:
46
+ return f"<Hasher {self.get_name()}>"
47
+
48
+
49
+ __all__ = ("Hasher",)
@@ -0,0 +1,54 @@
1
+ from telegrinder.bot.cute_types import MessageCute as Message
2
+ from telegrinder.bot.dispatch.view import MessageView
3
+
4
+ from .hasher import Hasher
5
+
6
+
7
+ def from_chat_hash(chat_id: int) -> int:
8
+ return chat_id
9
+
10
+
11
+ def get_chat_from_event(event: Message) -> int:
12
+ return event.chat.id
13
+
14
+
15
+ MESSAGE_IN_CHAT = Hasher(
16
+ view=MessageView, get_hash_from_data=from_chat_hash, get_data_from_event=get_chat_from_event
17
+ )
18
+
19
+
20
+ def from_user_hash(from_id: int) -> int:
21
+ return from_id
22
+
23
+
24
+ def get_user_from_event(event: Message) -> int:
25
+ return event.from_user.id
26
+
27
+
28
+ MESSAGE_FROM_USER = Hasher(
29
+ view=MessageView,
30
+ get_hash_from_data=from_user_hash,
31
+ get_data_from_event=get_user_from_event,
32
+ )
33
+
34
+
35
+ def from_user_in_chat_hash(chat_and_user: tuple[int, int]) -> str:
36
+ return f"{chat_and_user[0]}_{chat_and_user[1]}"
37
+
38
+
39
+ def get_user_in_chat_from_event(event: Message) -> tuple[int, int]:
40
+ return event.chat.id, event.from_user.id
41
+
42
+
43
+ MESSAGE_FROM_USER_IN_CHAT = Hasher(
44
+ view=MessageView,
45
+ get_hash_from_data=from_user_in_chat_hash,
46
+ get_data_from_event=get_user_in_chat_from_event,
47
+ )
48
+
49
+
50
+ __all__ = (
51
+ "MESSAGE_FROM_USER",
52
+ "MESSAGE_FROM_USER_IN_CHAT",
53
+ "MESSAGE_IN_CHAT",
54
+ )
@@ -0,0 +1,19 @@
1
+ from fntypes import Option
2
+
3
+ from telegrinder.bot.dispatch.view import BaseStateView
4
+ from telegrinder.tools.functional import from_optional
5
+
6
+ from .hasher import ECHO, Event, Hasher
7
+
8
+
9
+ class StateViewHasher(Hasher[Event, int]):
10
+ view: BaseStateView
11
+
12
+ def __init__(self, view: type[BaseStateView[Event]]):
13
+ super().__init__(view, get_hash_from_data=ECHO)
14
+
15
+ def get_data_from_event(self, event: Event) -> Option[int]:
16
+ return from_optional(self.view.get_state_key(event))
17
+
18
+
19
+ __all__ = ("StateViewHasher",)
@@ -2,35 +2,42 @@ import asyncio
2
2
  import datetime
3
3
  import typing
4
4
 
5
- from telegrinder.api.api import API
6
- from telegrinder.bot.dispatch.context import Context
7
- from telegrinder.bot.dispatch.view.abc import ABCStateView
8
- from telegrinder.bot.dispatch.view.base import BaseStateView
5
+ from telegrinder.bot.dispatch.abc import ABCDispatch
6
+ from telegrinder.bot.dispatch.view.base import BaseStateView, BaseView
9
7
  from telegrinder.bot.dispatch.waiter_machine.middleware import WaiterMiddleware
10
8
  from telegrinder.bot.dispatch.waiter_machine.short_state import (
11
- Behaviour,
12
9
  EventModel,
13
10
  ShortState,
14
11
  ShortStateContext,
15
12
  )
16
13
  from telegrinder.bot.rules.abc import ABCRule
17
14
  from telegrinder.tools.limited_dict import LimitedDict
18
- from telegrinder.types import Update
19
15
 
20
- if typing.TYPE_CHECKING:
21
- from telegrinder.bot.dispatch import Dispatch
16
+ from .actions import WaiterActions
17
+ from .hasher import Hasher, StateViewHasher
22
18
 
23
19
  T = typing.TypeVar("T")
20
+ HasherData = typing.TypeVar("HasherData")
24
21
 
25
- Identificator: typing.TypeAlias = str | int
26
- Storage: typing.TypeAlias = dict[str, LimitedDict[Identificator, ShortState[EventModel]]]
22
+
23
+ Storage: typing.TypeAlias = dict[
24
+ Hasher[EventModel, HasherData], LimitedDict[typing.Hashable, ShortState[EventModel]]
25
+ ]
27
26
 
28
27
  WEEK: typing.Final[datetime.timedelta] = datetime.timedelta(days=7)
29
28
 
30
29
 
31
30
  class WaiterMachine:
32
- def __init__(self, *, max_storage_size: int = 1000) -> None:
31
+ def __init__(
32
+ self,
33
+ dispatch: ABCDispatch | None = None,
34
+ *,
35
+ max_storage_size: int = 1000,
36
+ base_state_lifetime: datetime.timedelta = WEEK,
37
+ ) -> None:
38
+ self.dispatch = dispatch
33
39
  self.max_storage_size = max_storage_size
40
+ self.base_state_lifetime = base_state_lifetime
34
41
  self.storage: Storage = {}
35
42
 
36
43
  def __repr__(self) -> str:
@@ -46,142 +53,123 @@ class WaiterMachine:
46
53
 
47
54
  async def drop(
48
55
  self,
49
- state_view: "ABCStateView[EventModel] | str",
50
- id: Identificator,
51
- event: EventModel,
52
- update: Update,
56
+ hasher: Hasher[EventModel, HasherData],
57
+ id: HasherData,
53
58
  **context: typing.Any,
54
59
  ) -> None:
55
- view_name = state_view if isinstance(state_view, str) else state_view.__class__.__name__
56
- if view_name not in self.storage:
57
- raise LookupError("No record of view {!r} found.".format(view_name))
60
+ if hasher not in self.storage:
61
+ raise LookupError("No record of hasher {!r} found.".format(hasher))
58
62
 
59
- short_state = self.storage[view_name].pop(id, None)
63
+ waiter_id: typing.Hashable = hasher.get_hash_from_data(id).expect(
64
+ RuntimeError("Couldn't create hash from data")
65
+ )
66
+ short_state = self.storage[hasher].pop(waiter_id, None)
60
67
  if short_state is None:
61
- raise LookupError("Waiter with identificator {} is not found for view {!r}".format(id, view_name))
68
+ raise LookupError(
69
+ "Waiter with identificator {} is not found for hasher {!r}".format(waiter_id, hasher)
70
+ )
71
+
72
+ if on_drop := short_state.actions.get("on_drop"):
73
+ on_drop(short_state, **context)
62
74
 
63
75
  short_state.cancel()
64
- await self.call_behaviour(
65
- event,
66
- update,
67
- behaviour=short_state.on_drop_behaviour,
68
- **context,
69
- )
70
76
 
71
77
  async def drop_all(self) -> None:
72
78
  """Drops all waiters in storage."""
73
79
 
74
- for view_name in self.storage:
75
- for ident, short_state in self.storage[view_name].items():
80
+ for hasher in self.storage:
81
+ for ident, short_state in self.storage[hasher].items():
76
82
  if short_state.context:
77
83
  await self.drop(
78
- view_name, ident, short_state.context.event, short_state.context.context.raw_update
84
+ hasher,
85
+ ident,
79
86
  )
80
87
  else:
81
88
  short_state.cancel()
82
89
 
83
- async def wait(
90
+ async def wait_from_event(
84
91
  self,
85
- state_view: "BaseStateView[EventModel]",
86
- linked: EventModel | tuple[API, Identificator],
87
- *rules: ABCRule,
88
- default: Behaviour[EventModel] | None = None,
89
- on_drop: Behaviour[EventModel] | None = None,
90
- exit: Behaviour[EventModel] | None = None,
91
- expiration: datetime.timedelta | float | None = None,
92
+ view: BaseStateView[EventModel],
93
+ event: EventModel,
94
+ *,
95
+ filter: ABCRule | None = None,
96
+ release: ABCRule | None = None,
97
+ lifetime: datetime.timedelta | float | None = None,
98
+ **actions: typing.Unpack[WaiterActions],
92
99
  ) -> ShortStateContext[EventModel]:
93
- if isinstance(expiration, int | float):
94
- expiration = datetime.timedelta(seconds=expiration)
100
+ hasher = StateViewHasher(view.__class__)
101
+ return await self.wait(
102
+ hasher=hasher,
103
+ data=hasher.get_data_from_event(event).expect(
104
+ RuntimeError("Hasher couldn't create data from event.")
105
+ ),
106
+ filter=filter,
107
+ release=release,
108
+ lifetime=lifetime,
109
+ **actions,
110
+ )
95
111
 
96
- api: API
97
- key: Identificator
98
- api, key = linked if isinstance(linked, tuple) else (linked.ctx_api, state_view.get_state_key(linked)) # type: ignore
99
- if not key:
100
- raise RuntimeError("Unable to get state key.")
112
+ async def wait(
113
+ self,
114
+ hasher: Hasher[EventModel, HasherData],
115
+ data: HasherData,
116
+ *,
117
+ filter: ABCRule | None = None,
118
+ release: ABCRule | None = None,
119
+ lifetime: datetime.timedelta | float | None = None,
120
+ **actions: typing.Unpack[WaiterActions],
121
+ ) -> ShortStateContext[EventModel]:
122
+ if isinstance(lifetime, int | float):
123
+ lifetime = datetime.timedelta(seconds=lifetime)
101
124
 
102
- view_name = state_view.__class__.__name__
103
125
  event = asyncio.Event()
104
126
  short_state = ShortState[EventModel](
105
- key,
106
- api,
107
- event,
108
- rules,
109
- expiration=expiration,
110
- default_behaviour=default,
111
- on_drop_behaviour=on_drop,
112
- exit_behaviour=exit,
127
+ filter=filter,
128
+ release=release,
129
+ event=event,
130
+ lifetime=lifetime or self.base_state_lifetime,
131
+ actions=actions,
113
132
  )
133
+ waiter_hash = hasher.get_hash_from_data(data).expect(RuntimeError("Hasher couldn't create hash."))
114
134
 
115
- if view_name not in self.storage:
116
- state_view.middlewares.insert(0, WaiterMiddleware(self, state_view))
117
- self.storage[view_name] = LimitedDict(maxlimit=self.max_storage_size)
135
+ if hasher not in self.storage:
136
+ if self.dispatch:
137
+ view: BaseView[EventModel] = self.dispatch.get_view(hasher.view).expect(
138
+ RuntimeError(f"View {hasher.view.__name__} is not defined in dispatch")
139
+ )
140
+ view.middlewares.insert(0, WaiterMiddleware(self, hasher))
141
+ self.storage[hasher] = LimitedDict(maxlimit=self.max_storage_size)
118
142
 
119
- if (deleted_short_state := self.storage[view_name].set(key, short_state)) is not None:
143
+ if (deleted_short_state := self.storage[hasher].set(waiter_hash, short_state)) is not None:
120
144
  deleted_short_state.cancel()
121
145
 
122
146
  await event.wait()
123
- self.storage[view_name].pop(key, None)
147
+ self.storage[hasher].pop(waiter_hash, None)
124
148
  assert short_state.context is not None
125
149
  return short_state.context
126
150
 
127
- async def call_behaviour(
128
- self,
129
- event: EventModel,
130
- update: Update,
131
- behaviour: Behaviour[EventModel] | None = None,
132
- **context: typing.Any,
133
- ) -> bool:
134
- # TODO: support view as a behaviour
135
-
136
- if behaviour is None:
137
- return False
138
-
139
- ctx = Context(**context)
140
- if await behaviour.check(event.api, update, ctx):
141
- await behaviour.run(event.api, event, ctx)
142
- return True
143
-
144
- return False
145
-
146
151
  async def clear_storage(
147
152
  self,
148
- views: typing.Iterable[ABCStateView[EventModel]],
149
- absolutely_dead_time: datetime.timedelta = WEEK,
150
153
  ) -> None:
151
- """Clears storage.
154
+ """Clears storage."""
152
155
 
153
- :param absolutely_dead_time: timedelta when state can be forgotten.
154
- """
155
-
156
- for view in views:
157
- view_name = view.__class__.__name__
156
+ for hasher in self.storage:
158
157
  now = datetime.datetime.now()
159
- for ident, short_state in self.storage.get(view_name, {}).copy().items():
158
+ for ident, short_state in self.storage.get(hasher, {}).copy().items():
160
159
  if short_state.expiration_date is not None and now > short_state.expiration_date:
161
- assert short_state.context # FIXME: why???
162
160
  await self.drop(
163
- view,
161
+ hasher,
164
162
  ident,
165
- event=short_state.context.event,
166
- update=short_state.context.context.raw_update,
167
163
  force=True,
168
164
  )
169
- elif short_state.creation_date + absolutely_dead_time < now:
170
- short_state.cancel()
171
- del self.storage[view_name][short_state.key]
172
165
 
173
166
 
174
167
  async def clear_wm_storage_worker(
175
168
  wm: WaiterMachine,
176
- dp: "Dispatch",
177
169
  interval_seconds: int = 60,
178
- absolutely_dead_time: datetime.timedelta = WEEK,
179
170
  ) -> typing.NoReturn:
180
171
  while True:
181
- await wm.clear_storage(
182
- views=[view for view in dp.get_views().values() if isinstance(view, ABCStateView)],
183
- absolutely_dead_time=absolutely_dead_time,
184
- )
172
+ await wm.clear_storage()
185
173
  await asyncio.sleep(interval_seconds)
186
174
 
187
175