hookly 0.1.0__tar.gz → 0.1.5__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.
hookly-0.1.5/PKG-INFO ADDED
@@ -0,0 +1,305 @@
1
+ Metadata-Version: 2.5
2
+ Name: hookly
3
+ Version: 0.1.5
4
+ Summary: Batteries-included backend toolkit for Python — API, DB and CRM, ready to call.
5
+ Project-URL: Homepage, https://github.com/Xar1ee/hookly
6
+ Project-URL: Repository, https://github.com/Xar1ee/hookly
7
+ Project-URL: Issues, https://github.com/Xar1ee/hookly/issues
8
+ Author-email: Safaraliyev Xushnud <xushnudbackend@gmail.com>
9
+ License: MIT
10
+ Keywords: api,backend,crm,framework,sqlite,toolkit,webhook
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Requires-Python: >=3.10
22
+ Requires-Dist: colorama>=0.4
23
+ Requires-Dist: python-dotenv>=1.0
24
+ Provides-Extra: dev
25
+ Requires-Dist: pytest>=7.0; extra == 'dev'
26
+ Description-Content-Type: text/markdown
27
+
28
+ # ⚡ Hookly
29
+
30
+ **Batteries-included backend toolkit for Python.**
31
+ Stop wiring up an API, a database and a CRM by hand for every new project — import Hookly and call it.
32
+
33
+ ![PyPI](https://img.shields.io/badge/pypi-hookly-blue)
34
+ ![Python](https://img.shields.io/badge/python-3.10%2B-blue)
35
+ ![License](https://img.shields.io/badge/license-MIT-green)
36
+ ![Dependencies](https://img.shields.io/badge/dependencies-2-lightgrey)
37
+
38
+ Created by **Safaraliyev Xushnud**
39
+
40
+ ---
41
+
42
+ ## Contents
43
+
44
+ - [Why Hookly exists](#why-hookly-exists)
45
+ - [Install](#install)
46
+ - [60-second quickstart](#60-second-quickstart)
47
+ - [hookly.db — database without the ceremony](#hooklydb--database-without-the-ceremony)
48
+ - [hookly.crm — contacts and leads, ready to go](#hooklycrm--contacts-and-leads-ready-to-go)
49
+ - [hookly.api — a tiny JSON router](#hooklyapi--a-tiny-json-router)
50
+ - [Configuration via .env](#configuration-via-env)
51
+ - [Full example: a working mini-CRM API](#full-example-a-working-mini-crm-api)
52
+ - [FAQ](#faq)
53
+ - [Roadmap](#roadmap)
54
+ - [Source & links](#source--links)
55
+
56
+ ---
57
+
58
+ ## Why Hookly exists
59
+
60
+ Every backend project — a website, a Telegram bot's admin panel, an internal tool — ends up needing the same three things:
61
+
62
+ 1. **Somewhere to store data** (usually a quick SQLite file, at least at first)
63
+ 2. **Something to track contacts, leads or requests** (a mini-CRM)
64
+ 3. **A way to expose it all as an API** (so a frontend, a bot, or Postman can talk to it)
65
+
66
+ Normally you rebuild all three from scratch, every single time. **Hookly ships all three as ready-made modules that already work together.** You don't write a database layer — you call `Database`. You don't write a contacts table — you call `CRM`. You don't wire up routes by hand — you decorate a function and call `API`.
67
+
68
+ > Hookly isn't trying to replace Django or FastAPI. It's for the moment *before* that — when you just need something working **right now**.
69
+
70
+ ---
71
+
72
+ ## Install
73
+
74
+ ```bash
75
+ pip install hookly
76
+ ```
77
+
78
+ That's it — `pip` also pulls in the two small libraries Hookly relies on:
79
+
80
+ | Package | What it's used for |
81
+ |---|---|
82
+ | [`python-dotenv`](https://pypi.org/project/python-dotenv/) | Lets you configure Hookly with a `.env` file instead of hardcoding values |
83
+ | [`colorama`](https://pypi.org/project/colorama/) | Makes Hookly's console output colored — including on Windows |
84
+
85
+ Nothing else. No Django, no ORM, no config files you have to write before your first line of code runs.
86
+
87
+ For local development (running the test suite):
88
+
89
+ ```bash
90
+ pip install -e ".[dev]"
91
+ ```
92
+
93
+ ---
94
+
95
+ ## 60-second quickstart
96
+
97
+ Create a file called `main.py`:
98
+
99
+ ```python
100
+ from hookly import Database, CRM, API
101
+
102
+ db = Database("app.db") # SQLite file — created automatically
103
+ crm = CRM(db) # contacts table — created automatically
104
+ api = API("my-service") # your API, named however you like
105
+
106
+ @api.get("/contacts")
107
+ def list_contacts():
108
+ return crm.list_contacts()
109
+
110
+ @api.post("/contacts")
111
+ def new_contact():
112
+ contact = crm.add_contact("Ali", phone="+998901234567")
113
+ return {"id": contact.id, "name": contact.name}
114
+
115
+ api.run(port=8000)
116
+ ```
117
+
118
+ Run it:
119
+
120
+ ```bash
121
+ python main.py
122
+ ```
123
+
124
+ Open `http://127.0.0.1:8000/contacts` in your browser — you'll see an empty list `[]`. Send a `POST` request to the same address (Postman, curl, or your browser's console) and refresh — your contact is there, saved in SQLite, served over HTTP. **That's the whole stack, in fifteen lines.**
125
+
126
+ ---
127
+
128
+ ## `hookly.db` — database without the ceremony
129
+
130
+ No models to declare, no migration files, no `Base.metadata.create_all()` — describe a table once as a plain dictionary, then insert and fetch with normal Python calls.
131
+
132
+ ```python
133
+ from hookly import Database
134
+
135
+ db = Database("app.db")
136
+
137
+ db.create_table("users", {
138
+ "id": "INTEGER PRIMARY KEY AUTOINCREMENT",
139
+ "name": "TEXT NOT NULL",
140
+ "email": "TEXT",
141
+ })
142
+
143
+ user_id = db.insert("users", name="Ali", email="ali@example.com")
144
+
145
+ all_users = db.fetchall("SELECT * FROM users")
146
+ one_user = db.fetchone("SELECT * FROM users WHERE id = ?", (user_id,))
147
+
148
+ db.update("users", "id = ?", (user_id,), name="Ali Karimov")
149
+ db.delete("users", "id = ?", (user_id,))
150
+
151
+ db.close()
152
+ ```
153
+
154
+ Or use it as a context manager so it closes itself:
155
+
156
+ ```python
157
+ with Database("app.db") as db:
158
+ db.create_table("logs", {"id": "INTEGER PRIMARY KEY", "message": "TEXT"})
159
+ db.insert("logs", message="app started")
160
+ ```
161
+
162
+ ---
163
+
164
+ ## `hookly.crm` — contacts and leads, ready to go
165
+
166
+ Every project eventually needs *some* place to track "people who reached out." Instead of building that table again, use `CRM` — it's a `Database` with a contacts schema and status tracking built in.
167
+
168
+ ```python
169
+ from hookly import CRM
170
+
171
+ crm = CRM() # uses its own SQLite file if you don't pass a Database
172
+
173
+ contact = crm.add_contact("Ali", phone="+998901234567", email="ali@example.com")
174
+ # Contact(id=1, name='Ali', phone='+998901234567', email='ali@example.com', status='new', created_at='...')
175
+
176
+ crm.update_status(contact.id, "in_progress")
177
+
178
+ leads_in_progress = crm.list_contacts(status="in_progress")
179
+
180
+ crm.delete_contact(contact.id)
181
+ ```
182
+
183
+ Statuses move through `new → in_progress → closed` — enough structure to build a real lead pipeline, simple enough to not fight you.
184
+
185
+ ---
186
+
187
+ ## `hookly.api` — a tiny JSON router
188
+
189
+ A decorator-based HTTP router, built entirely on Python's standard library — **zero extra dependencies for the API layer itself.** Perfect for prototypes, internal tools, and services that don't need the full weight of a framework yet.
190
+
191
+ ```python
192
+ from hookly import API
193
+
194
+ api = API("my-service")
195
+
196
+ @api.get("/ping")
197
+ def ping():
198
+ return {"status": "ok"}
199
+
200
+ @api.get("/users/count")
201
+ def user_count():
202
+ return {"count": 42}
203
+
204
+ api.run(host="0.0.0.0", port=8000)
205
+ ```
206
+
207
+ Every handler just returns a Python `dict` or `list` — Hookly serializes it to JSON automatically. If a handler raises an exception, Hookly catches it and returns a clean `500` JSON error instead of crashing your whole server.
208
+
209
+ > Outgrowing it? That's expected — swap `hookly.api` for FastAPI or Django REST Framework later. Hookly's job was to get you moving on day one, not to be the framework you ship to millions of users.
210
+
211
+ ---
212
+
213
+ ## Configuration via `.env`
214
+
215
+ Instead of hardcoding paths and ports, drop a `.env` file next to your script:
216
+
217
+ ```
218
+ HOOKLY_DB_PATH=production.db
219
+ HOOKLY_HOST=0.0.0.0
220
+ HOOKLY_PORT=9000
221
+ ```
222
+
223
+ ```python
224
+ from hookly import Database, API
225
+
226
+ db = Database() # reads HOOKLY_DB_PATH automatically
227
+ api = API("service")
228
+ api.run() # reads HOOKLY_HOST / HOOKLY_PORT automatically
229
+ ```
230
+
231
+ No `.env` file? Hookly falls back to sensible defaults (`hookly.db`, `127.0.0.1:8000`) — configuration is optional, not required.
232
+
233
+ ---
234
+
235
+ ## Full example: a working mini-CRM API
236
+
237
+ ```python
238
+ from hookly import Database, CRM, API
239
+
240
+ db = Database("crm.db")
241
+ crm = CRM(db)
242
+ api = API("mini-crm")
243
+
244
+ @api.get("/contacts")
245
+ def all_contacts():
246
+ return crm.list_contacts()
247
+
248
+ @api.get("/contacts/new")
249
+ def new_leads():
250
+ return crm.list_contacts(status="new")
251
+
252
+ @api.post("/contacts")
253
+ def add_lead():
254
+ contact = crm.add_contact("New Lead", phone="+998900000000")
255
+ return {"id": contact.id, "status": contact.status}
256
+
257
+ if __name__ == "__main__":
258
+ api.run(port=8000)
259
+ ```
260
+
261
+ Five imports, three routes, one file — a running CRM backend with persistent storage.
262
+
263
+ ---
264
+
265
+ ## FAQ
266
+
267
+ **Does Hookly support PostgreSQL / MySQL?**
268
+ Not yet — `hookly.db` is SQLite-only for now. See [Roadmap](#roadmap).
269
+
270
+ **Can I use Hookly in production?**
271
+ For internal tools and small services, sure. For anything public-facing at scale, use Hookly to prototype fast, then migrate the API layer to FastAPI/Django once you know what you're building — the CRM and DB modules can usually stay as-is.
272
+
273
+ **Why does the API return a `dict`, not a `Response` object?**
274
+ Because 90% of the time that's all you need. Simplicity is the point — if you need response headers, status code control, or streaming, it's time to graduate to a full framework.
275
+
276
+ **Is `.env` required?**
277
+ No — it's entirely optional. Hookly works with zero configuration out of the box.
278
+
279
+ ---
280
+
281
+ ## Roadmap
282
+
283
+ - [ ] `hookly.auth` — token-based authentication, ready to drop into `API`
284
+ - [ ] `hookly.webhook` — send and receive webhooks natively (it's in the name!)
285
+ - [ ] Async support (`AsyncDatabase`, `AsyncAPI`)
286
+ - [ ] PostgreSQL backend for `hookly.db`
287
+ - [ ] Pagination helpers for large result sets
288
+ - [ ] CLI: `hookly new my-project` to scaffold a starter project
289
+ - [ ] `hookly.telegram` — a thin bridge for wiring a Telegram bot to the same CRM
290
+
291
+ Have an idea? Open an issue on GitHub — see below.
292
+
293
+ ---
294
+
295
+ ## Source & links
296
+
297
+ - **PyPI:** [pypi.org/project/hookly](https://pypi.org/project/hookly/)
298
+ - **GitHub:** [github.com/Xar1ee/hookly](https://github.com/Xar1ee/hookly)
299
+ - **Issues / feature requests:** [github.com/Xar1ee/hookly/issues](https://github.com/Xar1ee/hookly/issues)
300
+
301
+ Hookly is free and open for everyone. Fork it, break it, improve it — and if something's confusing, open an issue rather than suffering in silence.
302
+
303
+ ## License
304
+
305
+ MIT — do whatever you want with it, just keep the license notice.
hookly-0.1.5/README.md ADDED
@@ -0,0 +1,278 @@
1
+ # ⚡ Hookly
2
+
3
+ **Batteries-included backend toolkit for Python.**
4
+ Stop wiring up an API, a database and a CRM by hand for every new project — import Hookly and call it.
5
+
6
+ ![PyPI](https://img.shields.io/badge/pypi-hookly-blue)
7
+ ![Python](https://img.shields.io/badge/python-3.10%2B-blue)
8
+ ![License](https://img.shields.io/badge/license-MIT-green)
9
+ ![Dependencies](https://img.shields.io/badge/dependencies-2-lightgrey)
10
+
11
+ Created by **Safaraliyev Xushnud**
12
+
13
+ ---
14
+
15
+ ## Contents
16
+
17
+ - [Why Hookly exists](#why-hookly-exists)
18
+ - [Install](#install)
19
+ - [60-second quickstart](#60-second-quickstart)
20
+ - [hookly.db — database without the ceremony](#hooklydb--database-without-the-ceremony)
21
+ - [hookly.crm — contacts and leads, ready to go](#hooklycrm--contacts-and-leads-ready-to-go)
22
+ - [hookly.api — a tiny JSON router](#hooklyapi--a-tiny-json-router)
23
+ - [Configuration via .env](#configuration-via-env)
24
+ - [Full example: a working mini-CRM API](#full-example-a-working-mini-crm-api)
25
+ - [FAQ](#faq)
26
+ - [Roadmap](#roadmap)
27
+ - [Source & links](#source--links)
28
+
29
+ ---
30
+
31
+ ## Why Hookly exists
32
+
33
+ Every backend project — a website, a Telegram bot's admin panel, an internal tool — ends up needing the same three things:
34
+
35
+ 1. **Somewhere to store data** (usually a quick SQLite file, at least at first)
36
+ 2. **Something to track contacts, leads or requests** (a mini-CRM)
37
+ 3. **A way to expose it all as an API** (so a frontend, a bot, or Postman can talk to it)
38
+
39
+ Normally you rebuild all three from scratch, every single time. **Hookly ships all three as ready-made modules that already work together.** You don't write a database layer — you call `Database`. You don't write a contacts table — you call `CRM`. You don't wire up routes by hand — you decorate a function and call `API`.
40
+
41
+ > Hookly isn't trying to replace Django or FastAPI. It's for the moment *before* that — when you just need something working **right now**.
42
+
43
+ ---
44
+
45
+ ## Install
46
+
47
+ ```bash
48
+ pip install hookly
49
+ ```
50
+
51
+ That's it — `pip` also pulls in the two small libraries Hookly relies on:
52
+
53
+ | Package | What it's used for |
54
+ |---|---|
55
+ | [`python-dotenv`](https://pypi.org/project/python-dotenv/) | Lets you configure Hookly with a `.env` file instead of hardcoding values |
56
+ | [`colorama`](https://pypi.org/project/colorama/) | Makes Hookly's console output colored — including on Windows |
57
+
58
+ Nothing else. No Django, no ORM, no config files you have to write before your first line of code runs.
59
+
60
+ For local development (running the test suite):
61
+
62
+ ```bash
63
+ pip install -e ".[dev]"
64
+ ```
65
+
66
+ ---
67
+
68
+ ## 60-second quickstart
69
+
70
+ Create a file called `main.py`:
71
+
72
+ ```python
73
+ from hookly import Database, CRM, API
74
+
75
+ db = Database("app.db") # SQLite file — created automatically
76
+ crm = CRM(db) # contacts table — created automatically
77
+ api = API("my-service") # your API, named however you like
78
+
79
+ @api.get("/contacts")
80
+ def list_contacts():
81
+ return crm.list_contacts()
82
+
83
+ @api.post("/contacts")
84
+ def new_contact():
85
+ contact = crm.add_contact("Ali", phone="+998901234567")
86
+ return {"id": contact.id, "name": contact.name}
87
+
88
+ api.run(port=8000)
89
+ ```
90
+
91
+ Run it:
92
+
93
+ ```bash
94
+ python main.py
95
+ ```
96
+
97
+ Open `http://127.0.0.1:8000/contacts` in your browser — you'll see an empty list `[]`. Send a `POST` request to the same address (Postman, curl, or your browser's console) and refresh — your contact is there, saved in SQLite, served over HTTP. **That's the whole stack, in fifteen lines.**
98
+
99
+ ---
100
+
101
+ ## `hookly.db` — database without the ceremony
102
+
103
+ No models to declare, no migration files, no `Base.metadata.create_all()` — describe a table once as a plain dictionary, then insert and fetch with normal Python calls.
104
+
105
+ ```python
106
+ from hookly import Database
107
+
108
+ db = Database("app.db")
109
+
110
+ db.create_table("users", {
111
+ "id": "INTEGER PRIMARY KEY AUTOINCREMENT",
112
+ "name": "TEXT NOT NULL",
113
+ "email": "TEXT",
114
+ })
115
+
116
+ user_id = db.insert("users", name="Ali", email="ali@example.com")
117
+
118
+ all_users = db.fetchall("SELECT * FROM users")
119
+ one_user = db.fetchone("SELECT * FROM users WHERE id = ?", (user_id,))
120
+
121
+ db.update("users", "id = ?", (user_id,), name="Ali Karimov")
122
+ db.delete("users", "id = ?", (user_id,))
123
+
124
+ db.close()
125
+ ```
126
+
127
+ Or use it as a context manager so it closes itself:
128
+
129
+ ```python
130
+ with Database("app.db") as db:
131
+ db.create_table("logs", {"id": "INTEGER PRIMARY KEY", "message": "TEXT"})
132
+ db.insert("logs", message="app started")
133
+ ```
134
+
135
+ ---
136
+
137
+ ## `hookly.crm` — contacts and leads, ready to go
138
+
139
+ Every project eventually needs *some* place to track "people who reached out." Instead of building that table again, use `CRM` — it's a `Database` with a contacts schema and status tracking built in.
140
+
141
+ ```python
142
+ from hookly import CRM
143
+
144
+ crm = CRM() # uses its own SQLite file if you don't pass a Database
145
+
146
+ contact = crm.add_contact("Ali", phone="+998901234567", email="ali@example.com")
147
+ # Contact(id=1, name='Ali', phone='+998901234567', email='ali@example.com', status='new', created_at='...')
148
+
149
+ crm.update_status(contact.id, "in_progress")
150
+
151
+ leads_in_progress = crm.list_contacts(status="in_progress")
152
+
153
+ crm.delete_contact(contact.id)
154
+ ```
155
+
156
+ Statuses move through `new → in_progress → closed` — enough structure to build a real lead pipeline, simple enough to not fight you.
157
+
158
+ ---
159
+
160
+ ## `hookly.api` — a tiny JSON router
161
+
162
+ A decorator-based HTTP router, built entirely on Python's standard library — **zero extra dependencies for the API layer itself.** Perfect for prototypes, internal tools, and services that don't need the full weight of a framework yet.
163
+
164
+ ```python
165
+ from hookly import API
166
+
167
+ api = API("my-service")
168
+
169
+ @api.get("/ping")
170
+ def ping():
171
+ return {"status": "ok"}
172
+
173
+ @api.get("/users/count")
174
+ def user_count():
175
+ return {"count": 42}
176
+
177
+ api.run(host="0.0.0.0", port=8000)
178
+ ```
179
+
180
+ Every handler just returns a Python `dict` or `list` — Hookly serializes it to JSON automatically. If a handler raises an exception, Hookly catches it and returns a clean `500` JSON error instead of crashing your whole server.
181
+
182
+ > Outgrowing it? That's expected — swap `hookly.api` for FastAPI or Django REST Framework later. Hookly's job was to get you moving on day one, not to be the framework you ship to millions of users.
183
+
184
+ ---
185
+
186
+ ## Configuration via `.env`
187
+
188
+ Instead of hardcoding paths and ports, drop a `.env` file next to your script:
189
+
190
+ ```
191
+ HOOKLY_DB_PATH=production.db
192
+ HOOKLY_HOST=0.0.0.0
193
+ HOOKLY_PORT=9000
194
+ ```
195
+
196
+ ```python
197
+ from hookly import Database, API
198
+
199
+ db = Database() # reads HOOKLY_DB_PATH automatically
200
+ api = API("service")
201
+ api.run() # reads HOOKLY_HOST / HOOKLY_PORT automatically
202
+ ```
203
+
204
+ No `.env` file? Hookly falls back to sensible defaults (`hookly.db`, `127.0.0.1:8000`) — configuration is optional, not required.
205
+
206
+ ---
207
+
208
+ ## Full example: a working mini-CRM API
209
+
210
+ ```python
211
+ from hookly import Database, CRM, API
212
+
213
+ db = Database("crm.db")
214
+ crm = CRM(db)
215
+ api = API("mini-crm")
216
+
217
+ @api.get("/contacts")
218
+ def all_contacts():
219
+ return crm.list_contacts()
220
+
221
+ @api.get("/contacts/new")
222
+ def new_leads():
223
+ return crm.list_contacts(status="new")
224
+
225
+ @api.post("/contacts")
226
+ def add_lead():
227
+ contact = crm.add_contact("New Lead", phone="+998900000000")
228
+ return {"id": contact.id, "status": contact.status}
229
+
230
+ if __name__ == "__main__":
231
+ api.run(port=8000)
232
+ ```
233
+
234
+ Five imports, three routes, one file — a running CRM backend with persistent storage.
235
+
236
+ ---
237
+
238
+ ## FAQ
239
+
240
+ **Does Hookly support PostgreSQL / MySQL?**
241
+ Not yet — `hookly.db` is SQLite-only for now. See [Roadmap](#roadmap).
242
+
243
+ **Can I use Hookly in production?**
244
+ For internal tools and small services, sure. For anything public-facing at scale, use Hookly to prototype fast, then migrate the API layer to FastAPI/Django once you know what you're building — the CRM and DB modules can usually stay as-is.
245
+
246
+ **Why does the API return a `dict`, not a `Response` object?**
247
+ Because 90% of the time that's all you need. Simplicity is the point — if you need response headers, status code control, or streaming, it's time to graduate to a full framework.
248
+
249
+ **Is `.env` required?**
250
+ No — it's entirely optional. Hookly works with zero configuration out of the box.
251
+
252
+ ---
253
+
254
+ ## Roadmap
255
+
256
+ - [ ] `hookly.auth` — token-based authentication, ready to drop into `API`
257
+ - [ ] `hookly.webhook` — send and receive webhooks natively (it's in the name!)
258
+ - [ ] Async support (`AsyncDatabase`, `AsyncAPI`)
259
+ - [ ] PostgreSQL backend for `hookly.db`
260
+ - [ ] Pagination helpers for large result sets
261
+ - [ ] CLI: `hookly new my-project` to scaffold a starter project
262
+ - [ ] `hookly.telegram` — a thin bridge for wiring a Telegram bot to the same CRM
263
+
264
+ Have an idea? Open an issue on GitHub — see below.
265
+
266
+ ---
267
+
268
+ ## Source & links
269
+
270
+ - **PyPI:** [pypi.org/project/hookly](https://pypi.org/project/hookly/)
271
+ - **GitHub:** [github.com/Xar1ee/hookly](https://github.com/Xar1ee/hookly)
272
+ - **Issues / feature requests:** [github.com/Xar1ee/hookly/issues](https://github.com/Xar1ee/hookly/issues)
273
+
274
+ Hookly is free and open for everyone. Fork it, break it, improve it — and if something's confusing, open an issue rather than suffering in silence.
275
+
276
+ ## License
277
+
278
+ MIT — do whatever you want with it, just keep the license notice.
@@ -0,0 +1,12 @@
1
+ """
2
+ Hookly — batteries-included backend toolkit for Python.
3
+
4
+ from hookly import Database, CRM, API
5
+ """
6
+
7
+ from .db import Database
8
+ from .crm import CRM
9
+ from .api import API
10
+
11
+ __version__ = "0.1.1"
12
+ __all__ = ["Database", "CRM", "API", "__version__"]
@@ -1,21 +1,24 @@
1
1
  """
2
- hookly.api — a tiny decorator-based JSON API router.
3
-
4
- No dependencies, no config files: decorate a function, run the server.
5
- Good enough for internal tools and prototypes; swap in ASGI later for
6
- production if you outgrow it.
2
+ hookly.api — a tiny decorator-based JSON API router, pure stdlib for the
3
+ server itself, with a colorama-powered console banner (works on Windows
4
+ too, unlike raw ANSI codes) and .env-based defaults via python-dotenv.
7
5
  """
8
6
 
9
7
  from __future__ import annotations
10
8
 
11
9
  import json
10
+ import os
12
11
  from http.server import BaseHTTPRequestHandler, HTTPServer
13
12
  from typing import Callable
14
13
 
14
+ from colorama import Fore, Style, init as colorama_init
15
+ from dotenv import load_dotenv
16
+
17
+ load_dotenv()
18
+ colorama_init(autoreset=True)
15
19
 
16
- class API:
17
- """Call it, don't build it: a minimal HTTP router."""
18
20
 
21
+ class API:
19
22
  def __init__(self, name: str = "hookly-api") -> None:
20
23
  self.name = name
21
24
  self._routes: dict[tuple[str, str], Callable] = {}
@@ -24,7 +27,6 @@ class API:
24
27
  def decorator(func: Callable) -> Callable:
25
28
  self._routes[(method.upper(), path)] = func
26
29
  return func
27
-
28
30
  return decorator
29
31
 
30
32
  def get(self, path: str) -> Callable:
@@ -33,7 +35,9 @@ class API:
33
35
  def post(self, path: str) -> Callable:
34
36
  return self.route(path, "POST")
35
37
 
36
- def run(self, host: str = "127.0.0.1", port: int = 8000) -> None:
38
+ def run(self, host: str | None = None, port: int | None = None) -> None:
39
+ host = host or os.getenv("HOOKLY_HOST", "127.0.0.1")
40
+ port = port or int(os.getenv("HOOKLY_PORT", "8000"))
37
41
  routes = self._routes
38
42
 
39
43
  class Handler(BaseHTTPRequestHandler):
@@ -49,7 +53,7 @@ class API:
49
53
  result = handler()
50
54
  body = json.dumps(result).encode()
51
55
  status = 200
52
- except Exception as exc: # noqa: BLE001 - surface errors as JSON
56
+ except Exception as exc:
53
57
  body = json.dumps({"error": str(exc)}).encode()
54
58
  status = 500
55
59
  self.send_response(status)
@@ -57,14 +61,15 @@ class API:
57
61
  self.end_headers()
58
62
  self.wfile.write(body)
59
63
 
60
- def do_GET(self) -> None: # noqa: N802 - stdlib naming
64
+ def do_GET(self) -> None:
61
65
  self._handle("GET")
62
66
 
63
- def do_POST(self) -> None: # noqa: N802 - stdlib naming
67
+ def do_POST(self) -> None:
64
68
  self._handle("POST")
65
69
 
66
- def log_message(self, format: str, *args) -> None: # noqa: A002
67
- pass # keep the console quiet
70
+ def log_message(self, format: str, *args) -> None:
71
+ pass
68
72
 
69
- print(f"⚡ {self.name} running at http://{host}:{port}")
73
+ print(f"{Fore.CYAN}⚡ {Style.BRIGHT}{self.name}{Style.RESET_ALL}{Fore.CYAN} "
74
+ f"running at {Fore.YELLOW}http://{host}:{port}{Style.RESET_ALL}")
70
75
  HTTPServer((host, port), Handler).serve_forever()
@@ -1,8 +1,5 @@
1
1
  """
2
2
  hookly.crm — a ready-made contacts/leads module.
3
-
4
- Drop it on top of any Database instance and you get a contacts table,
5
- lead statuses and simple queries for free.
6
3
  """
7
4
 
8
5
  from __future__ import annotations
@@ -20,13 +17,11 @@ class Contact:
20
17
  name: str
21
18
  phone: str = ""
22
19
  email: str = ""
23
- status: str = "new" # new -> in_progress -> closed
20
+ status: str = "new"
24
21
  created_at: str = field(default_factory=lambda: datetime.now(timezone.utc).isoformat())
25
22
 
26
23
 
27
24
  class CRM:
28
- """A small CRM: contacts in, statuses tracked, no setup required."""
29
-
30
25
  STATUSES = ("new", "in_progress", "closed")
31
26
 
32
27
  def __init__(self, db: Optional[Database] = None) -> None:
@@ -3,24 +3,30 @@ hookly.db — a SQLite layer with no ceremony.
3
3
 
4
4
  No models to declare, no migrations to write for simple cases: describe a
5
5
  table once, then insert/fetch with plain Python calls.
6
+
7
+ The default database path can be set via a HOOKLY_DB_PATH variable in a
8
+ .env file, thanks to python-dotenv — handy for keeping paths out of code.
6
9
  """
7
10
 
8
11
  from __future__ import annotations
9
12
 
13
+ import os
10
14
  import sqlite3
11
15
  from typing import Any, Iterable, Optional
12
16
 
17
+ from dotenv import load_dotenv
18
+
19
+ load_dotenv()
20
+
13
21
 
14
22
  class Database:
15
23
  """Thin, friendly wrapper around sqlite3."""
16
24
 
17
- def __init__(self, path: str = "hookly.db") -> None:
18
- self.path = path
25
+ def __init__(self, path: str | None = None) -> None:
26
+ self.path = path or os.getenv("HOOKLY_DB_PATH", "hookly.db")
19
27
  self._conn = sqlite3.connect(self.path)
20
28
  self._conn.row_factory = sqlite3.Row
21
29
 
22
- # -- low level -----------------------------------------------------
23
-
24
30
  def execute(self, query: str, params: Iterable[Any] = ()) -> sqlite3.Cursor:
25
31
  cur = self._conn.execute(query, tuple(params))
26
32
  self._conn.commit()
@@ -35,15 +41,11 @@ class Database:
35
41
  row = cur.fetchone()
36
42
  return dict(row) if row else None
37
43
 
38
- # -- convenience -----------------------------------------------------
39
-
40
44
  def create_table(self, name: str, columns: dict[str, str]) -> None:
41
- """create_table('users', {'id': 'INTEGER PRIMARY KEY', 'name': 'TEXT'})"""
42
45
  cols_sql = ", ".join(f"{col} {ctype}" for col, ctype in columns.items())
43
46
  self.execute(f"CREATE TABLE IF NOT EXISTS {name} ({cols_sql})")
44
47
 
45
48
  def insert(self, table: str, **fields: Any) -> int:
46
- """insert('users', name='Ali', email='ali@example.com') -> new row id"""
47
49
  keys = ", ".join(fields.keys())
48
50
  placeholders = ", ".join("?" for _ in fields)
49
51
  cur = self.execute(
@@ -4,13 +4,13 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "hookly"
7
- version = "0.1.0"
7
+ version = "0.1.5"
8
8
  description = "Batteries-included backend toolkit for Python — API, DB and CRM, ready to call."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
11
11
  license = { text = "MIT" }
12
- authors = [{ name = "Your Name", email = "you@example.com" }]
13
- keywords = ["backend", "api", "crm", "sqlite", "toolkit", "framework"]
12
+ authors = [{ name = "Safaraliyev Xushnud", email = "xushnudbackend@gmail.com" }]
13
+ keywords = ["backend", "api", "crm", "sqlite", "toolkit", "framework", "webhook"]
14
14
  classifiers = [
15
15
  "Development Status :: 3 - Alpha",
16
16
  "Intended Audience :: Developers",
@@ -23,15 +23,18 @@ classifiers = [
23
23
  "Topic :: Software Development :: Libraries :: Python Modules",
24
24
  "Topic :: Internet :: WWW/HTTP :: WSGI :: Application",
25
25
  ]
26
- dependencies = []
26
+ dependencies = [
27
+ "python-dotenv>=1.0",
28
+ "colorama>=0.4",
29
+ ]
27
30
 
28
31
  [project.optional-dependencies]
29
32
  dev = ["pytest>=7.0"]
30
33
 
31
34
  [project.urls]
32
- Homepage = "https://github.com/yourusername/hookly"
33
- Repository = "https://github.com/yourusername/hookly"
34
- Issues = "https://github.com/yourusername/hookly/issues"
35
+ Homepage = "https://github.com/Xar1ee/hookly"
36
+ Repository = "https://github.com/Xar1ee/hookly"
37
+ Issues = "https://github.com/Xar1ee/hookly/issues"
35
38
 
36
39
  [tool.hatch.build.targets.wheel]
37
40
  packages = ["hookly"]
hookly-0.1.0/PKG-INFO DELETED
@@ -1,135 +0,0 @@
1
- Metadata-Version: 2.5
2
- Name: hookly
3
- Version: 0.1.0
4
- Summary: Batteries-included backend toolkit for Python — API, DB and CRM, ready to call.
5
- Project-URL: Homepage, https://github.com/yourusername/hookly
6
- Project-URL: Repository, https://github.com/yourusername/hookly
7
- Project-URL: Issues, https://github.com/yourusername/hookly/issues
8
- Author-email: Your Name <you@example.com>
9
- License: MIT
10
- Keywords: api,backend,crm,framework,sqlite,toolkit
11
- Classifier: Development Status :: 3 - Alpha
12
- Classifier: Intended Audience :: Developers
13
- Classifier: License :: OSI Approved :: MIT License
14
- Classifier: Operating System :: OS Independent
15
- Classifier: Programming Language :: Python :: 3
16
- Classifier: Programming Language :: Python :: 3.10
17
- Classifier: Programming Language :: Python :: 3.11
18
- Classifier: Programming Language :: Python :: 3.12
19
- Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application
20
- Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
- Requires-Python: >=3.10
22
- Provides-Extra: dev
23
- Requires-Dist: pytest>=7.0; extra == 'dev'
24
- Description-Content-Type: text/markdown
25
-
26
- # ⚡ Hookly
27
-
28
- **Batteries-included backend toolkit for Python.**
29
- Stop wiring up an API, a database layer and a CRM by hand — import Hookly and call it.
30
-
31
- ![PyPI](https://img.shields.io/badge/pypi-hookly-blue)
32
- ![Python](https://img.shields.io/badge/python-3.10%2B-blue)
33
- ![License](https://img.shields.io/badge/license-MIT-green)
34
-
35
- ---
36
-
37
- ## Why Hookly
38
-
39
- Every backend needs the same handful of things: an API layer, a place to store
40
- data, and something to track contacts, leads or tickets. Hookly ships all
41
- three as ready-made modules that work together out of the box — you call
42
- them, you don't rebuild them.
43
-
44
- ```python
45
- from hookly import Database, CRM, API
46
-
47
- db = Database("app.db")
48
- crm = CRM(db)
49
- api = API("my-service")
50
-
51
- @api.get("/contacts")
52
- def list_contacts():
53
- return crm.list_contacts()
54
-
55
- @api.post("/contacts")
56
- def new_contact():
57
- return crm.add_contact("Ali", phone="+998901234567").__dict__
58
-
59
- api.run(port=8000)
60
- ```
61
-
62
- That's a working API + database + CRM. No boilerplate.
63
-
64
- ---
65
-
66
- ## Install
67
-
68
- ```bash
69
- pip install hookly
70
- ```
71
-
72
- (local dev: `pip install -e ".[dev]"` from the project root)
73
-
74
- ---
75
-
76
- ## Modules
77
-
78
- ### `hookly.db` — SQLite without the ceremony
79
-
80
- ```python
81
- from hookly import Database
82
-
83
- db = Database("app.db")
84
- db.create_table("users", {"id": "INTEGER PRIMARY KEY", "name": "TEXT"})
85
- db.insert("users", name="Ali")
86
- db.fetchall("SELECT * FROM users")
87
- ```
88
-
89
- ### `hookly.crm` — contacts and leads, ready to go
90
-
91
- ```python
92
- from hookly import CRM
93
-
94
- crm = CRM()
95
- contact = crm.add_contact("Ali", email="ali@example.com")
96
- crm.update_status(contact.id, "in_progress")
97
- crm.list_contacts(status="in_progress")
98
- ```
99
-
100
- ### `hookly.api` — a tiny decorator-based router
101
-
102
- ```python
103
- from hookly import API
104
-
105
- api = API("my-service")
106
-
107
- @api.get("/ping")
108
- def ping():
109
- return {"status": "ok"}
110
-
111
- api.run(port=8000)
112
- ```
113
-
114
- ---
115
-
116
- ## Roadmap
117
-
118
- - [ ] Async support (`AsyncDatabase`, `AsyncAPI`)
119
- - [ ] Auth module (`hookly.auth`)
120
- - [ ] PostgreSQL backend for `hookly.db`
121
- - [ ] CLI: `hookly new my-project`
122
-
123
- ---
124
-
125
- ## Philosophy
126
-
127
- Hookly isn't trying to replace FastAPI or Django — it's for the moment
128
- before that, when you just need something working *now*: a table, an
129
- endpoint, a contact list. Grow out of it, swap in the big tools later.
130
-
131
- ---
132
-
133
- ## License
134
-
135
- MIT
hookly-0.1.0/README.md DELETED
@@ -1,110 +0,0 @@
1
- # ⚡ Hookly
2
-
3
- **Batteries-included backend toolkit for Python.**
4
- Stop wiring up an API, a database layer and a CRM by hand — import Hookly and call it.
5
-
6
- ![PyPI](https://img.shields.io/badge/pypi-hookly-blue)
7
- ![Python](https://img.shields.io/badge/python-3.10%2B-blue)
8
- ![License](https://img.shields.io/badge/license-MIT-green)
9
-
10
- ---
11
-
12
- ## Why Hookly
13
-
14
- Every backend needs the same handful of things: an API layer, a place to store
15
- data, and something to track contacts, leads or tickets. Hookly ships all
16
- three as ready-made modules that work together out of the box — you call
17
- them, you don't rebuild them.
18
-
19
- ```python
20
- from hookly import Database, CRM, API
21
-
22
- db = Database("app.db")
23
- crm = CRM(db)
24
- api = API("my-service")
25
-
26
- @api.get("/contacts")
27
- def list_contacts():
28
- return crm.list_contacts()
29
-
30
- @api.post("/contacts")
31
- def new_contact():
32
- return crm.add_contact("Ali", phone="+998901234567").__dict__
33
-
34
- api.run(port=8000)
35
- ```
36
-
37
- That's a working API + database + CRM. No boilerplate.
38
-
39
- ---
40
-
41
- ## Install
42
-
43
- ```bash
44
- pip install hookly
45
- ```
46
-
47
- (local dev: `pip install -e ".[dev]"` from the project root)
48
-
49
- ---
50
-
51
- ## Modules
52
-
53
- ### `hookly.db` — SQLite without the ceremony
54
-
55
- ```python
56
- from hookly import Database
57
-
58
- db = Database("app.db")
59
- db.create_table("users", {"id": "INTEGER PRIMARY KEY", "name": "TEXT"})
60
- db.insert("users", name="Ali")
61
- db.fetchall("SELECT * FROM users")
62
- ```
63
-
64
- ### `hookly.crm` — contacts and leads, ready to go
65
-
66
- ```python
67
- from hookly import CRM
68
-
69
- crm = CRM()
70
- contact = crm.add_contact("Ali", email="ali@example.com")
71
- crm.update_status(contact.id, "in_progress")
72
- crm.list_contacts(status="in_progress")
73
- ```
74
-
75
- ### `hookly.api` — a tiny decorator-based router
76
-
77
- ```python
78
- from hookly import API
79
-
80
- api = API("my-service")
81
-
82
- @api.get("/ping")
83
- def ping():
84
- return {"status": "ok"}
85
-
86
- api.run(port=8000)
87
- ```
88
-
89
- ---
90
-
91
- ## Roadmap
92
-
93
- - [ ] Async support (`AsyncDatabase`, `AsyncAPI`)
94
- - [ ] Auth module (`hookly.auth`)
95
- - [ ] PostgreSQL backend for `hookly.db`
96
- - [ ] CLI: `hookly new my-project`
97
-
98
- ---
99
-
100
- ## Philosophy
101
-
102
- Hookly isn't trying to replace FastAPI or Django — it's for the moment
103
- before that, when you just need something working *now*: a table, an
104
- endpoint, a contact list. Grow out of it, swap in the big tools later.
105
-
106
- ---
107
-
108
- ## License
109
-
110
- MIT
@@ -1,25 +0,0 @@
1
- """
2
- Hookly — batteries-included backend toolkit for Python.
3
-
4
- Instead of wiring up a DB layer, a CRM module and an API router by hand,
5
- you import Hookly and call it.
6
-
7
- from hookly import Database, CRM, API
8
-
9
- db = Database("app.db")
10
- crm = CRM(db)
11
- api = API("my-service")
12
-
13
- @api.get("/contacts")
14
- def list_contacts():
15
- return crm.list_contacts()
16
-
17
- api.run(port=8000)
18
- """
19
-
20
- from .db import Database
21
- from .crm import CRM
22
- from .api import API
23
-
24
- __version__ = "0.1.0"
25
- __all__ = ["Database", "CRM", "API", "__version__"]
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes