pyroflow 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.
Files changed (44) hide show
  1. pyroflow-0.1.0/LICENSE +21 -0
  2. pyroflow-0.1.0/PKG-INFO +271 -0
  3. pyroflow-0.1.0/README.md +237 -0
  4. pyroflow-0.1.0/pyproject.toml +78 -0
  5. pyroflow-0.1.0/pyroflow/__init__.py +14 -0
  6. pyroflow-0.1.0/pyroflow/__meta__.py +1 -0
  7. pyroflow-0.1.0/pyroflow/client.py +413 -0
  8. pyroflow-0.1.0/pyroflow/dispatcher.py +503 -0
  9. pyroflow-0.1.0/pyroflow/enums.py +10 -0
  10. pyroflow-0.1.0/pyroflow/errors.py +20 -0
  11. pyroflow-0.1.0/pyroflow/listener_coordinator/__init__.py +11 -0
  12. pyroflow-0.1.0/pyroflow/listener_coordinator/listener_coordinator.py +151 -0
  13. pyroflow-0.1.0/pyroflow/listener_coordinator/memory_listener_coordinator.py +74 -0
  14. pyroflow-0.1.0/pyroflow/listener_coordinator/redis_listener_coordinator.py +280 -0
  15. pyroflow-0.1.0/pyroflow/models.py +64 -0
  16. pyroflow-0.1.0/pyroflow/types.py +304 -0
  17. pyroflow-0.1.0/pyroflow/typings.py +15 -0
  18. pyroflow-0.1.0/pyroflow/update_coordinated/__init__.py +11 -0
  19. pyroflow-0.1.0/pyroflow/update_coordinated/callback_query_coordinated.py +31 -0
  20. pyroflow-0.1.0/pyroflow/update_coordinated/message_coordinated.py +38 -0
  21. pyroflow-0.1.0/pyroflow/update_coordinated/update_coordinated.py +204 -0
  22. pyroflow-0.1.0/pyroflow/update_coordinator/__init__.py +11 -0
  23. pyroflow-0.1.0/pyroflow/update_coordinator/memory_update_coordinator.py +43 -0
  24. pyroflow-0.1.0/pyroflow/update_coordinator/redis_update_coordinator.py +54 -0
  25. pyroflow-0.1.0/pyroflow/update_coordinator/update_coordinator.py +118 -0
  26. pyroflow-0.1.0/pyroflow/update_history/__init__.py +12 -0
  27. pyroflow-0.1.0/pyroflow/update_history/callback_query_history.py +56 -0
  28. pyroflow-0.1.0/pyroflow/update_history/message_history.py +6 -0
  29. pyroflow-0.1.0/pyroflow/update_history/update_history.py +306 -0
  30. pyroflow-0.1.0/pyroflow/update_history_store/__init__.py +9 -0
  31. pyroflow-0.1.0/pyroflow/update_history_store/memory_update_history_store.py +117 -0
  32. pyroflow-0.1.0/pyroflow/update_history_store/update_history_store.py +93 -0
  33. pyroflow-0.1.0/pyroflow/update_listener/__init__.py +11 -0
  34. pyroflow-0.1.0/pyroflow/update_listener/callback_query_listener.py +82 -0
  35. pyroflow-0.1.0/pyroflow/update_listener/message_listener.py +48 -0
  36. pyroflow-0.1.0/pyroflow/update_listener/update_listener.py +391 -0
  37. pyroflow-0.1.0/pyroflow/utils/__init__.py +19 -0
  38. pyroflow-0.1.0/pyroflow/utils/async_tools.py +220 -0
  39. pyroflow-0.1.0/pyroflow/utils/classes.py +100 -0
  40. pyroflow-0.1.0/pyroflow/utils/enums.py +15 -0
  41. pyroflow-0.1.0/pyroflow/utils/iter_tools.py +30 -0
  42. pyroflow-0.1.0/pyroflow/utils/misc_tools.py +67 -0
  43. pyroflow-0.1.0/pyroflow/utils/typings.py +88 -0
  44. pyroflow-0.1.0/pyroflow/utils/validate_tools.py +30 -0
pyroflow-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Abdullah (github.com/eeeob)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,271 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyroflow
3
+ Version: 0.1.0
4
+ Summary: Conversation-oriented Pyrogram extension with per-update listeners and multi-session coordination
5
+ Project-URL: Homepage, https://github.com/eeeob/pyroflow
6
+ Project-URL: Bug Tracker, https://github.com/eeeob/pyroflow/issues
7
+ Author-email: Abdullah <aldheeb01@gmail.com>
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Keywords: async,bot,conversation,coordinator,listener,pyrogram,telegram
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Framework :: AsyncIO
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: 3.14
22
+ Classifier: Topic :: Communications :: Chat
23
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
+ Classifier: Typing :: Typed
25
+ Requires-Python: >=3.9
26
+ Requires-Dist: cachetools
27
+ Requires-Dist: kurigram>=2.2.0
28
+ Provides-Extra: dev
29
+ Requires-Dist: hatch<=1.16.5; extra == 'dev'
30
+ Requires-Dist: twine<=6.2.0; extra == 'dev'
31
+ Provides-Extra: redis
32
+ Requires-Dist: redis>=4.2.0; extra == 'redis'
33
+ Description-Content-Type: text/markdown
34
+
35
+ # pyroflow
36
+
37
+ [![PyPI version](https://img.shields.io/pypi/v/pyroflow?dummy=1)](https://pypi.org/project/pyroflow/)
38
+ [![Python versions](https://img.shields.io/pypi/pyversions/pyroflow?dummy=1)](https://pypi.org/project/pyroflow/)
39
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
40
+
41
+ **Conversation-oriented Pyrogram extension with per-update listeners and multi-session coordination.**
42
+
43
+ pyroflow builds on top of [Pyrogram](https://github.com/pyrogram/pyrogram) / [Kurigram](https://github.com/KurimuzonAkuma/pyrogram) to replace the handler-based model with a conversation-first API — `await` a specific reply from a specific user instead of wiring up global handlers and managing state machines by hand.
44
+
45
+ ```
46
+ pip install pyroflow
47
+ ```
48
+
49
+ For Redis-backed coordination:
50
+
51
+ ```
52
+ pip install pyroflow[redis]
53
+ ```
54
+
55
+ ---
56
+
57
+ ## Why pyroflow?
58
+
59
+ Pyrogram fires your handler for **every** incoming update of a given type. The moment you need a back-and-forth conversation you end up writing state machines, storing `user_id → step` in a dict, and hoping two updates don't race each other.
60
+
61
+ pyroflow solves all three problems:
62
+
63
+ | Problem | pyroflow solution |
64
+ |---|---|
65
+ | Waiting for a specific reply | `UpdateListener` — `await` the next update from a user |
66
+ | Duplicate processing across multiple bot sessions | `UpdateCoordinated` — distributed lock per update |
67
+ | Replaying or inspecting previous handler steps | `UpdateHistory` — per-update-type handler record |
68
+
69
+ ---
70
+
71
+ ## Installation
72
+
73
+ **Minimum requirements:** Python 3.9+
74
+
75
+ ```
76
+ pip install pyroflow # core
77
+ pip install pyroflow[redis] # + Redis coordinator support
78
+ pip install pyroflow[dev] # + development tools (hatch, twine)
79
+ ```
80
+
81
+ ---
82
+
83
+ ## Quick start
84
+
85
+ ```python
86
+ from pyroflow import Client, MessageListener
87
+
88
+ client = Client("my_session")
89
+ client.register_listener(MessageListener())
90
+
91
+ @client.on_message()
92
+ async def on_start(client, message):
93
+ if message.text != "/start":
94
+ return
95
+
96
+ answer = await message.ask(
97
+ chat_id=message.chat.id,
98
+ text="What is your name?",
99
+ listen_user_id=message.from_user.id,
100
+ timeout=60,
101
+ )
102
+ await answer.reply(f"Hello, {answer.text}!")
103
+
104
+ client.run()
105
+ ```
106
+
107
+ ---
108
+
109
+ ## Core concepts
110
+
111
+ ### Listeners
112
+
113
+ A `UpdateListener` is a typed queue bound to a Pyrogram update type. Any coroutine can `await` the next matching update from a specific user or chat. An update claimed by a listener **never reaches the normal handler pipeline**.
114
+
115
+ ```python
116
+ from pyroflow import Client, MessageListener
117
+ from pyroflow.errors import ListenerTimeout
118
+
119
+ client = Client("my_session")
120
+ client.register_listener(MessageListener())
121
+
122
+ @client.on_message()
123
+ async def on_confirm(client, message):
124
+ if message.text != "/confirm":
125
+ return
126
+
127
+ await message.reply("Send your confirmation code:")
128
+
129
+ try:
130
+ code_msg = await client.message_listen(
131
+ chat_id=message.chat.id,
132
+ user_id=message.from_user.id,
133
+ timeout=120,
134
+ )
135
+ except ListenerTimeout:
136
+ await message.reply("Timed out. Please try again.")
137
+ return
138
+
139
+ await code_msg.reply(f"Code received: {code_msg.text}")
140
+
141
+ client.run()
142
+ ```
143
+
144
+ Shortcuts for the two most common listener types:
145
+
146
+ ```python
147
+ client.message_listen # UpdateListener[Message]
148
+ client.callback_listen # UpdateListener[CallbackQuery]
149
+ ```
150
+
151
+ ---
152
+
153
+ ### ask()
154
+
155
+ `ask()` is the high-level wrapper around listeners. It sends (or edits) a message and then suspends until a matching reply arrives — all in one `await`.
156
+
157
+ ```python
158
+ # Send a new message, then wait for reply
159
+ answer = await client.ask(
160
+ chat_id=chat_id,
161
+ text="Choose an option:",
162
+ reply_markup=keyboard,
163
+ listen_user_id=user_id,
164
+ timeout=30,
165
+ )
166
+
167
+ # Edit an existing message, then wait for a callback query
168
+ callback = await client.ask(
169
+ chat_id=chat_id,
170
+ text="Updated — choose again:",
171
+ message_id=sent_msg.id,
172
+ listen_user_id=user_id,
173
+ timeout=30,
174
+ update_type=CallbackQuery,
175
+ )
176
+ ```
177
+
178
+ **Parameters:**
179
+
180
+ | Parameter | Description |
181
+ |---|---|
182
+ | `chat_id` | Target chat |
183
+ | `text` | Message text |
184
+ | `message_id` | If provided, edits the message instead of sending a new one |
185
+ | `listen_user_id` | Filter the awaited update by user |
186
+ | `listen_message_id` | Filter the awaited update by message |
187
+ | `timeout` | Seconds to wait before raising `ListenerTimeout` |
188
+ | `update_type` | Update type to wait for — determines the return type (default: `Message`) |
189
+ | `meta` | Arbitrary metadata attached to the listener |
190
+
191
+ **Raises:**
192
+ - `ListenerTimeout` — no reply arrived within `timeout` seconds
193
+ - `ListenerCancelled` — the listener was cancelled while waiting
194
+
195
+ ---
196
+
197
+ ### Coordinators
198
+
199
+ A `UpdateCoordinated` acquires a **distributed lock** before processing an update. This ensures the same update is handled by exactly one session when the bot runs on multiple servers simultaneously.
200
+
201
+ ```python
202
+ from functools import partial
203
+ from redis.asyncio import Redis
204
+ from pyroflow import Client, MessageCoordinated, RedisUpdateCoordinator
205
+
206
+
207
+ client = Client("my_session")
208
+ redis = Redis(db=client.name)
209
+ coordinator_factory = partial(RedisUpdateCoordinator, redis)
210
+ coordinated = MessageCoordinated(coordinator_factory)
211
+ client.register_coordinated(coordinated)
212
+
213
+ client.run()
214
+ ```
215
+
216
+ **Supported backends:**
217
+
218
+ | Backend | Extra |
219
+ |---|---|
220
+ | Redis | `pip install pyroflow[redis]` |
221
+
222
+ **Lock states:**
223
+
224
+ - `HANDLED` — at least one handler completed without error; lock is released and other sessions skip the update.
225
+ - `None` — no handler ran successfully; lock is released so another session may retry.
226
+
227
+ ---
228
+
229
+ ### Histories
230
+
231
+ A `UpdateHistory` records which handlers ran successfully for each update. This enables features like `back` buttons that replay or inspect previous processing steps.
232
+
233
+ ```python
234
+ from pyroflow import Client, MessageHistory
235
+
236
+ client = Client("my_session")
237
+ client.register_history(MessageHistory())
238
+
239
+ client.run()
240
+ ```
241
+
242
+ ---
243
+
244
+ They can also be removed at runtime:
245
+
246
+ ```python
247
+ await client.unregister_listener(Message)
248
+ await client.unregister_coordinated(Message)
249
+ await client.unregister_history(Message)
250
+ ```
251
+
252
+ ---
253
+
254
+ ## Error handling
255
+
256
+ ```python
257
+ from pyroflow.errors import ListenerTimeout, ListenerCancelled
258
+
259
+ try:
260
+ reply = await client.ask(chat_id, "Your input?", listen_user_id=uid, timeout=30)
261
+ except ListenerTimeout:
262
+ await client.send_message(chat_id, "You took too long. Try again.")
263
+ except ListenerCancelled:
264
+ await client.send_message(chat_id, "Session was cancelled.")
265
+ ```
266
+
267
+ ---
268
+
269
+ ## License
270
+
271
+ MIT
@@ -0,0 +1,237 @@
1
+ # pyroflow
2
+
3
+ [![PyPI version](https://img.shields.io/pypi/v/pyroflow?dummy=1)](https://pypi.org/project/pyroflow/)
4
+ [![Python versions](https://img.shields.io/pypi/pyversions/pyroflow?dummy=1)](https://pypi.org/project/pyroflow/)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
6
+
7
+ **Conversation-oriented Pyrogram extension with per-update listeners and multi-session coordination.**
8
+
9
+ pyroflow builds on top of [Pyrogram](https://github.com/pyrogram/pyrogram) / [Kurigram](https://github.com/KurimuzonAkuma/pyrogram) to replace the handler-based model with a conversation-first API — `await` a specific reply from a specific user instead of wiring up global handlers and managing state machines by hand.
10
+
11
+ ```
12
+ pip install pyroflow
13
+ ```
14
+
15
+ For Redis-backed coordination:
16
+
17
+ ```
18
+ pip install pyroflow[redis]
19
+ ```
20
+
21
+ ---
22
+
23
+ ## Why pyroflow?
24
+
25
+ Pyrogram fires your handler for **every** incoming update of a given type. The moment you need a back-and-forth conversation you end up writing state machines, storing `user_id → step` in a dict, and hoping two updates don't race each other.
26
+
27
+ pyroflow solves all three problems:
28
+
29
+ | Problem | pyroflow solution |
30
+ |---|---|
31
+ | Waiting for a specific reply | `UpdateListener` — `await` the next update from a user |
32
+ | Duplicate processing across multiple bot sessions | `UpdateCoordinated` — distributed lock per update |
33
+ | Replaying or inspecting previous handler steps | `UpdateHistory` — per-update-type handler record |
34
+
35
+ ---
36
+
37
+ ## Installation
38
+
39
+ **Minimum requirements:** Python 3.9+
40
+
41
+ ```
42
+ pip install pyroflow # core
43
+ pip install pyroflow[redis] # + Redis coordinator support
44
+ pip install pyroflow[dev] # + development tools (hatch, twine)
45
+ ```
46
+
47
+ ---
48
+
49
+ ## Quick start
50
+
51
+ ```python
52
+ from pyroflow import Client, MessageListener
53
+
54
+ client = Client("my_session")
55
+ client.register_listener(MessageListener())
56
+
57
+ @client.on_message()
58
+ async def on_start(client, message):
59
+ if message.text != "/start":
60
+ return
61
+
62
+ answer = await message.ask(
63
+ chat_id=message.chat.id,
64
+ text="What is your name?",
65
+ listen_user_id=message.from_user.id,
66
+ timeout=60,
67
+ )
68
+ await answer.reply(f"Hello, {answer.text}!")
69
+
70
+ client.run()
71
+ ```
72
+
73
+ ---
74
+
75
+ ## Core concepts
76
+
77
+ ### Listeners
78
+
79
+ A `UpdateListener` is a typed queue bound to a Pyrogram update type. Any coroutine can `await` the next matching update from a specific user or chat. An update claimed by a listener **never reaches the normal handler pipeline**.
80
+
81
+ ```python
82
+ from pyroflow import Client, MessageListener
83
+ from pyroflow.errors import ListenerTimeout
84
+
85
+ client = Client("my_session")
86
+ client.register_listener(MessageListener())
87
+
88
+ @client.on_message()
89
+ async def on_confirm(client, message):
90
+ if message.text != "/confirm":
91
+ return
92
+
93
+ await message.reply("Send your confirmation code:")
94
+
95
+ try:
96
+ code_msg = await client.message_listen(
97
+ chat_id=message.chat.id,
98
+ user_id=message.from_user.id,
99
+ timeout=120,
100
+ )
101
+ except ListenerTimeout:
102
+ await message.reply("Timed out. Please try again.")
103
+ return
104
+
105
+ await code_msg.reply(f"Code received: {code_msg.text}")
106
+
107
+ client.run()
108
+ ```
109
+
110
+ Shortcuts for the two most common listener types:
111
+
112
+ ```python
113
+ client.message_listen # UpdateListener[Message]
114
+ client.callback_listen # UpdateListener[CallbackQuery]
115
+ ```
116
+
117
+ ---
118
+
119
+ ### ask()
120
+
121
+ `ask()` is the high-level wrapper around listeners. It sends (or edits) a message and then suspends until a matching reply arrives — all in one `await`.
122
+
123
+ ```python
124
+ # Send a new message, then wait for reply
125
+ answer = await client.ask(
126
+ chat_id=chat_id,
127
+ text="Choose an option:",
128
+ reply_markup=keyboard,
129
+ listen_user_id=user_id,
130
+ timeout=30,
131
+ )
132
+
133
+ # Edit an existing message, then wait for a callback query
134
+ callback = await client.ask(
135
+ chat_id=chat_id,
136
+ text="Updated — choose again:",
137
+ message_id=sent_msg.id,
138
+ listen_user_id=user_id,
139
+ timeout=30,
140
+ update_type=CallbackQuery,
141
+ )
142
+ ```
143
+
144
+ **Parameters:**
145
+
146
+ | Parameter | Description |
147
+ |---|---|
148
+ | `chat_id` | Target chat |
149
+ | `text` | Message text |
150
+ | `message_id` | If provided, edits the message instead of sending a new one |
151
+ | `listen_user_id` | Filter the awaited update by user |
152
+ | `listen_message_id` | Filter the awaited update by message |
153
+ | `timeout` | Seconds to wait before raising `ListenerTimeout` |
154
+ | `update_type` | Update type to wait for — determines the return type (default: `Message`) |
155
+ | `meta` | Arbitrary metadata attached to the listener |
156
+
157
+ **Raises:**
158
+ - `ListenerTimeout` — no reply arrived within `timeout` seconds
159
+ - `ListenerCancelled` — the listener was cancelled while waiting
160
+
161
+ ---
162
+
163
+ ### Coordinators
164
+
165
+ A `UpdateCoordinated` acquires a **distributed lock** before processing an update. This ensures the same update is handled by exactly one session when the bot runs on multiple servers simultaneously.
166
+
167
+ ```python
168
+ from functools import partial
169
+ from redis.asyncio import Redis
170
+ from pyroflow import Client, MessageCoordinated, RedisUpdateCoordinator
171
+
172
+
173
+ client = Client("my_session")
174
+ redis = Redis(db=client.name)
175
+ coordinator_factory = partial(RedisUpdateCoordinator, redis)
176
+ coordinated = MessageCoordinated(coordinator_factory)
177
+ client.register_coordinated(coordinated)
178
+
179
+ client.run()
180
+ ```
181
+
182
+ **Supported backends:**
183
+
184
+ | Backend | Extra |
185
+ |---|---|
186
+ | Redis | `pip install pyroflow[redis]` |
187
+
188
+ **Lock states:**
189
+
190
+ - `HANDLED` — at least one handler completed without error; lock is released and other sessions skip the update.
191
+ - `None` — no handler ran successfully; lock is released so another session may retry.
192
+
193
+ ---
194
+
195
+ ### Histories
196
+
197
+ A `UpdateHistory` records which handlers ran successfully for each update. This enables features like `back` buttons that replay or inspect previous processing steps.
198
+
199
+ ```python
200
+ from pyroflow import Client, MessageHistory
201
+
202
+ client = Client("my_session")
203
+ client.register_history(MessageHistory())
204
+
205
+ client.run()
206
+ ```
207
+
208
+ ---
209
+
210
+ They can also be removed at runtime:
211
+
212
+ ```python
213
+ await client.unregister_listener(Message)
214
+ await client.unregister_coordinated(Message)
215
+ await client.unregister_history(Message)
216
+ ```
217
+
218
+ ---
219
+
220
+ ## Error handling
221
+
222
+ ```python
223
+ from pyroflow.errors import ListenerTimeout, ListenerCancelled
224
+
225
+ try:
226
+ reply = await client.ask(chat_id, "Your input?", listen_user_id=uid, timeout=30)
227
+ except ListenerTimeout:
228
+ await client.send_message(chat_id, "You took too long. Try again.")
229
+ except ListenerCancelled:
230
+ await client.send_message(chat_id, "Session was cancelled.")
231
+ ```
232
+
233
+ ---
234
+
235
+ ## License
236
+
237
+ MIT
@@ -0,0 +1,78 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+
6
+ [project]
7
+ name = "pyroflow"
8
+ dynamic = ["version"]
9
+ description = "Conversation-oriented Pyrogram extension with per-update listeners and multi-session coordination"
10
+ requires-python = ">=3.9"
11
+ authors = [{ name = "Abdullah", email = "aldheeb01@gmail.com" }]
12
+ readme = "README.md"
13
+ license = { text = "MIT" }
14
+
15
+ keywords = [
16
+ "pyrogram",
17
+ "telegram",
18
+ "bot",
19
+ "async",
20
+ "conversation",
21
+ "listener",
22
+ "coordinator",
23
+ ]
24
+
25
+ classifiers = [
26
+ "Development Status :: 3 - Alpha",
27
+ "Intended Audience :: Developers",
28
+ "License :: OSI Approved :: MIT License",
29
+ "Programming Language :: Python :: 3",
30
+ "Programming Language :: Python :: 3.9",
31
+ "Programming Language :: Python :: 3.10",
32
+ "Programming Language :: Python :: 3.11",
33
+ "Programming Language :: Python :: 3.12",
34
+ "Programming Language :: Python :: 3.13",
35
+ "Programming Language :: Python :: 3.14",
36
+ "Topic :: Communications :: Chat",
37
+ "Topic :: Software Development :: Libraries :: Python Modules",
38
+ "Framework :: AsyncIO",
39
+ "Typing :: Typed",
40
+ ]
41
+
42
+ dependencies = [
43
+ "cachetools",
44
+ "kurigram>=2.2.0",
45
+ ]
46
+
47
+
48
+ [project.urls]
49
+ "Homepage" = "https://github.com/eeeob/pyroflow"
50
+ "Bug Tracker" = "https://github.com/eeeob/pyroflow/issues"
51
+
52
+
53
+ [project.optional-dependencies]
54
+ dev = [
55
+ "hatch<=1.16.5",
56
+ "twine<=6.2.0",
57
+ ]
58
+
59
+ redis = [
60
+ "redis>=4.2.0",
61
+ ]
62
+
63
+
64
+ [tool.hatch.build.targets.sdist]
65
+ exclude = [
66
+ ".github/",
67
+ ]
68
+
69
+ [tool.hatch.version]
70
+ path = "pyroflow/__meta__.py"
71
+
72
+
73
+ [tool.hatch.metadata]
74
+ allow-direct-references = true
75
+
76
+ [tool.hatch.build.targets.wheel]
77
+ ignore-vcs = true
78
+ packages = ["pyroflow"]
@@ -0,0 +1,14 @@
1
+ from .listener_coordinator import *
2
+ from .update_coordinated import *
3
+ from .update_coordinator import *
4
+ from .update_history import *
5
+ from .update_history_store import *
6
+ from .update_listener import *
7
+
8
+ from .client import Client
9
+ from .dispatcher import Dispatcher
10
+
11
+ from .types import Message, CallbackQuery
12
+
13
+
14
+
@@ -0,0 +1 @@
1
+ __version__ = "0.1.0"