aiostep 0.1.0__tar.gz

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.
aiostep-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,10 @@
1
+ Metadata-Version: 2.1
2
+ Name: aiostep
3
+ Version: 0.1.0
4
+ Summary: A Python library to handle steps in aiogram framework.
5
+ Home-page: https://github.com/NasrollahYusefi/aiostep/
6
+ Author: Nasrollah Yusefi
7
+ License: MIT
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Requires-Dist: cachebox
File without changes
@@ -0,0 +1,24 @@
1
+ """
2
+ A Python library to handle steps in aiogram framework.
3
+ """
4
+ __author__ = "Nasrollah Yusefi"
5
+ __version__ = "0.1.0"
6
+
7
+ __all__ = [
8
+ "Listen",
9
+ "change_root_store",
10
+ "register_next_step",
11
+ "unregister_steps",
12
+ "wait_for",
13
+ "clear",
14
+ ]
15
+
16
+ from .steps import (
17
+ MetaStore as MetaStore,
18
+ Listen as Listen,
19
+ change_root_store as change_root_store,
20
+ register_next_step as register_next_step,
21
+ unregister_steps as unregister_steps,
22
+ wait_for as wait_for,
23
+ clear as clear
24
+ )
@@ -0,0 +1,202 @@
1
+ import asyncio
2
+ import typing
3
+ import functools
4
+ import cachebox
5
+
6
+ from aiogram import BaseMiddleware
7
+ from aiogram.types import TelegramObject, Update
8
+
9
+ _MT = typing.Union[asyncio.Future, typing.Callable]
10
+
11
+
12
+ class MetaStore:
13
+ async def set_item(self, key: int, value: _MT) -> None:
14
+ """
15
+ Stores key-value.
16
+ """
17
+ raise NotImplementedError
18
+
19
+ async def pop_item(self, key: int) -> _MT:
20
+ """
21
+ Gives stored key-value.
22
+
23
+ raise KeyError if not found.
24
+ """
25
+ raise NotImplementedError
26
+
27
+ async def clear(self) -> typing.AsyncGenerator[_MT, None]:
28
+ """
29
+ Gives and clears all stored key-value.
30
+ """
31
+ raise NotImplementedError
32
+
33
+
34
+ class _RootStore(MetaStore):
35
+ def __init__(self) -> None:
36
+ self.cache = cachebox.Cache(0)
37
+
38
+ async def set_item(self, key: int, value: _MT) -> None:
39
+ self.cache[key] = value
40
+
41
+ async def pop_item(self, key: int) -> _MT:
42
+ return self.cache.pop(key)
43
+
44
+ async def clear(self) -> typing.AsyncGenerator[_MT, None]:
45
+ for k in self.cache.keys():
46
+ yield self.cache.pop(k)
47
+
48
+
49
+ root = _RootStore()
50
+
51
+
52
+ def change_root_store(store: MetaStore) -> None:
53
+ """
54
+ changes root store.
55
+ """
56
+ global root
57
+ root = store
58
+
59
+
60
+ class Listen(BaseMiddleware):
61
+ """
62
+ aiogram middleware to listen for steps.
63
+
64
+ supported handlers:
65
+ - `MessageHandler`
66
+ - `CallbackQueryHandler`
67
+ - `ChatJoinRequestHandler`
68
+ - `ChatMemberUpdatedHandler`
69
+ - `ChosenInlineResultHandler`
70
+ - `EditedMessageHandler`
71
+ - `InlineQueryHandler`
72
+
73
+ Example::
74
+
75
+ dp = Dispatcher()
76
+ dp.message.outer_middleware(aiostep.Listen(callback_query=False))
77
+ """
78
+
79
+ def __init__(self,
80
+ store: typing.Optional[MetaStore] = None,
81
+ callback_query: typing.Optional[bool] = None) -> None:
82
+ self.store = store or root
83
+ self.callback_query = callback_query
84
+
85
+ async def __call__(
86
+ self,
87
+ handler: typing.Callable[[TelegramObject, typing.Dict[str, typing.Any]], typing.Awaitable[typing.Any]],
88
+ event: TelegramObject,
89
+ data: typing.Dict[str, typing.Any]
90
+ ) -> typing.Any:
91
+
92
+ fn = None
93
+
94
+ if self.callback_query:
95
+ chat_id = event.message.chat.id
96
+ else:
97
+ chat_id = event.chat.id
98
+
99
+ try:
100
+ fn = await self.store.pop_item(event.from_user.id)
101
+ except (KeyError, AttributeError):
102
+ try:
103
+ fn = await self.store.pop_item(chat_id)
104
+ except (KeyError, AttributeError):
105
+ pass
106
+
107
+ if fn is not None:
108
+ if isinstance(fn, asyncio.Future):
109
+ fn.set_result(event)
110
+ return
111
+ await fn(event)
112
+ return
113
+
114
+ return await handler(event, data)
115
+
116
+
117
+ async def register_next_step(
118
+ user_id: int,
119
+ _next: typing.Any,
120
+ store: typing.Optional[MetaStore] = None,
121
+ *,
122
+ args: tuple = (),
123
+ kwargs: dict = {},
124
+ ) -> None:
125
+ """
126
+ register next step for user/chat.
127
+
128
+ Example::
129
+
130
+ async def ask_name(message: Message):
131
+ await message.reply("What is your name?")
132
+ await aiostep.register_next_step(message.from_user.id, ask_age)
133
+
134
+ async def tell_name(msg: Message):
135
+ await message.reply("Your name is: {msg.text}")
136
+ """
137
+ if args or kwargs:
138
+ _next = functools.partial(_next, *args, **kwargs)
139
+
140
+ await (store or root).set_item(user_id, _next)
141
+
142
+
143
+ async def unregister_steps(user_id: int, store: typing.Optional[MetaStore] = None) -> None:
144
+ """
145
+ unregister steps for `user_id`.
146
+
147
+ if step is `asyncio.Future`, cancels that.
148
+ """
149
+ try:
150
+ u = await (store or root).pop_item(user_id)
151
+ except KeyError:
152
+ return
153
+ else:
154
+ if isinstance(u, asyncio.Future):
155
+ u.cancel("cancelled")
156
+
157
+
158
+ async def _wait_future(user_id: int, timeout: typing.Optional[float], store: MetaStore) -> Update:
159
+ fn = asyncio.get_event_loop().create_future()
160
+
161
+ await store.set_item(user_id, fn)
162
+
163
+ try:
164
+ return await asyncio.wait_for(fn, timeout)
165
+ finally:
166
+ await unregister_steps(user_id, store)
167
+
168
+
169
+ async def wait_for(
170
+ user_id: int,
171
+ timeout: typing.Optional[float] = None,
172
+ store: typing.Optional[MetaStore] = None
173
+ ) -> Update:
174
+ """
175
+ wait for update which comming from specific user_id.
176
+
177
+ raise TimeoutError if timed out.
178
+
179
+ Example::
180
+
181
+ async def echo_handler(message: Message):
182
+ await message.reply("Please type something:")
183
+ try:
184
+ response = await aiostep.wait_for(message.from_user.id, timeout=25)
185
+ except TimeoutError:
186
+ await message.reply('You took too long to answer.')
187
+ else:
188
+ await message.reply(f"You typed: {response.text}")
189
+ """
190
+ try:
191
+ return await _wait_future(user_id, timeout, store or root)
192
+ except asyncio.TimeoutError:
193
+ raise TimeoutError
194
+
195
+
196
+ async def clear(store: typing.Optional[MetaStore] = None) -> None:
197
+ """
198
+ Clears all registered key-value's.
199
+ """
200
+ async for i in (store or root).clear(): # type: ignore
201
+ if isinstance(i, asyncio.Future):
202
+ i.cancel()
@@ -0,0 +1,10 @@
1
+ Metadata-Version: 2.1
2
+ Name: aiostep
3
+ Version: 0.1.0
4
+ Summary: A Python library to handle steps in aiogram framework.
5
+ Home-page: https://github.com/NasrollahYusefi/aiostep/
6
+ Author: Nasrollah Yusefi
7
+ License: MIT
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Requires-Dist: cachebox
@@ -0,0 +1,10 @@
1
+ README.md
2
+ pyproject.toml
3
+ setup.py
4
+ aiostep/__init__.py
5
+ aiostep/steps.py
6
+ aiostep.egg-info/PKG-INFO
7
+ aiostep.egg-info/SOURCES.txt
8
+ aiostep.egg-info/dependency_links.txt
9
+ aiostep.egg-info/requires.txt
10
+ aiostep.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ cachebox
@@ -0,0 +1 @@
1
+ aiostep
@@ -0,0 +1,3 @@
1
+ [build-system]
2
+ requires = ['setuptools>=42']
3
+ build-backend = 'setuptools.build_meta'
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
aiostep-0.1.0/setup.py ADDED
@@ -0,0 +1,21 @@
1
+ from setuptools import setup
2
+
3
+ VERSION = "0.1.0"
4
+
5
+ setup(
6
+ name="aiostep",
7
+ version=VERSION,
8
+ description="A Python library to handle steps in aiogram framework.",
9
+ author="Nasrollah Yusefi",
10
+ url="https://github.com/NasrollahYusefi/aiostep/",
11
+ packages=["aiostep"],
12
+ install_requires=[
13
+ "cachebox"
14
+ ],
15
+ license="MIT",
16
+ license_files=["LICENSE"],
17
+ classifiers=[
18
+ "Programming Language :: Python :: 3",
19
+ "License :: OSI Approved :: MIT License",
20
+ ],
21
+ )