Metadata-Version: 2.5
Name: hookly
Version: 0.1.5
Summary: Batteries-included backend toolkit for Python — API, DB and CRM, ready to call.
Project-URL: Homepage, https://github.com/Xar1ee/hookly
Project-URL: Repository, https://github.com/Xar1ee/hookly
Project-URL: Issues, https://github.com/Xar1ee/hookly/issues
Author-email: Safaraliyev Xushnud <xushnudbackend@gmail.com>
License: MIT
Keywords: api,backend,crm,framework,sqlite,toolkit,webhook
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Requires-Dist: colorama>=0.4
Requires-Dist: python-dotenv>=1.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == 'dev'
Description-Content-Type: text/markdown

# ⚡ Hookly

**Batteries-included backend toolkit for Python.**
Stop wiring up an API, a database and a CRM by hand for every new project — import Hookly and call it.

![PyPI](https://img.shields.io/badge/pypi-hookly-blue)
![Python](https://img.shields.io/badge/python-3.10%2B-blue)
![License](https://img.shields.io/badge/license-MIT-green)
![Dependencies](https://img.shields.io/badge/dependencies-2-lightgrey)

Created by **Safaraliyev Xushnud**

---

## Contents

- [Why Hookly exists](#why-hookly-exists)
- [Install](#install)
- [60-second quickstart](#60-second-quickstart)
- [hookly.db — database without the ceremony](#hooklydb--database-without-the-ceremony)
- [hookly.crm — contacts and leads, ready to go](#hooklycrm--contacts-and-leads-ready-to-go)
- [hookly.api — a tiny JSON router](#hooklyapi--a-tiny-json-router)
- [Configuration via .env](#configuration-via-env)
- [Full example: a working mini-CRM API](#full-example-a-working-mini-crm-api)
- [FAQ](#faq)
- [Roadmap](#roadmap)
- [Source & links](#source--links)

---

## Why Hookly exists

Every backend project — a website, a Telegram bot's admin panel, an internal tool — ends up needing the same three things:

1. **Somewhere to store data** (usually a quick SQLite file, at least at first)
2. **Something to track contacts, leads or requests** (a mini-CRM)
3. **A way to expose it all as an API** (so a frontend, a bot, or Postman can talk to it)

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`.

> Hookly isn't trying to replace Django or FastAPI. It's for the moment *before* that — when you just need something working **right now**.

---

## Install

```bash
pip install hookly
```

That's it — `pip` also pulls in the two small libraries Hookly relies on:

| Package | What it's used for |
|---|---|
| [`python-dotenv`](https://pypi.org/project/python-dotenv/) | Lets you configure Hookly with a `.env` file instead of hardcoding values |
| [`colorama`](https://pypi.org/project/colorama/) | Makes Hookly's console output colored — including on Windows |

Nothing else. No Django, no ORM, no config files you have to write before your first line of code runs.

For local development (running the test suite):

```bash
pip install -e ".[dev]"
```

---

## 60-second quickstart

Create a file called `main.py`:

```python
from hookly import Database, CRM, API

db = Database("app.db")     # SQLite file — created automatically
crm = CRM(db)                # contacts table — created automatically
api = API("my-service")      # your API, named however you like

@api.get("/contacts")
def list_contacts():
    return crm.list_contacts()

@api.post("/contacts")
def new_contact():
    contact = crm.add_contact("Ali", phone="+998901234567")
    return {"id": contact.id, "name": contact.name}

api.run(port=8000)
```

Run it:

```bash
python main.py
```

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.**

---

## `hookly.db` — database without the ceremony

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.

```python
from hookly import Database

db = Database("app.db")

db.create_table("users", {
    "id": "INTEGER PRIMARY KEY AUTOINCREMENT",
    "name": "TEXT NOT NULL",
    "email": "TEXT",
})

user_id = db.insert("users", name="Ali", email="ali@example.com")

all_users = db.fetchall("SELECT * FROM users")
one_user = db.fetchone("SELECT * FROM users WHERE id = ?", (user_id,))

db.update("users", "id = ?", (user_id,), name="Ali Karimov")
db.delete("users", "id = ?", (user_id,))

db.close()
```

Or use it as a context manager so it closes itself:

```python
with Database("app.db") as db:
    db.create_table("logs", {"id": "INTEGER PRIMARY KEY", "message": "TEXT"})
    db.insert("logs", message="app started")
```

---

## `hookly.crm` — contacts and leads, ready to go

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.

```python
from hookly import CRM

crm = CRM()  # uses its own SQLite file if you don't pass a Database

contact = crm.add_contact("Ali", phone="+998901234567", email="ali@example.com")
# Contact(id=1, name='Ali', phone='+998901234567', email='ali@example.com', status='new', created_at='...')

crm.update_status(contact.id, "in_progress")

leads_in_progress = crm.list_contacts(status="in_progress")

crm.delete_contact(contact.id)
```

Statuses move through `new → in_progress → closed` — enough structure to build a real lead pipeline, simple enough to not fight you.

---

## `hookly.api` — a tiny JSON router

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.

```python
from hookly import API

api = API("my-service")

@api.get("/ping")
def ping():
    return {"status": "ok"}

@api.get("/users/count")
def user_count():
    return {"count": 42}

api.run(host="0.0.0.0", port=8000)
```

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.

> 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.

---

## Configuration via `.env`

Instead of hardcoding paths and ports, drop a `.env` file next to your script:

```
HOOKLY_DB_PATH=production.db
HOOKLY_HOST=0.0.0.0
HOOKLY_PORT=9000
```

```python
from hookly import Database, API

db = Database()      # reads HOOKLY_DB_PATH automatically
api = API("service")
api.run()             # reads HOOKLY_HOST / HOOKLY_PORT automatically
```

No `.env` file? Hookly falls back to sensible defaults (`hookly.db`, `127.0.0.1:8000`) — configuration is optional, not required.

---

## Full example: a working mini-CRM API

```python
from hookly import Database, CRM, API

db = Database("crm.db")
crm = CRM(db)
api = API("mini-crm")

@api.get("/contacts")
def all_contacts():
    return crm.list_contacts()

@api.get("/contacts/new")
def new_leads():
    return crm.list_contacts(status="new")

@api.post("/contacts")
def add_lead():
    contact = crm.add_contact("New Lead", phone="+998900000000")
    return {"id": contact.id, "status": contact.status}

if __name__ == "__main__":
    api.run(port=8000)
```

Five imports, three routes, one file — a running CRM backend with persistent storage.

---

## FAQ

**Does Hookly support PostgreSQL / MySQL?**
Not yet — `hookly.db` is SQLite-only for now. See [Roadmap](#roadmap).

**Can I use Hookly in production?**
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.

**Why does the API return a `dict`, not a `Response` object?**
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.

**Is `.env` required?**
No — it's entirely optional. Hookly works with zero configuration out of the box.

---

## Roadmap

- [ ] `hookly.auth` — token-based authentication, ready to drop into `API`
- [ ] `hookly.webhook` — send and receive webhooks natively (it's in the name!)
- [ ] Async support (`AsyncDatabase`, `AsyncAPI`)
- [ ] PostgreSQL backend for `hookly.db`
- [ ] Pagination helpers for large result sets
- [ ] CLI: `hookly new my-project` to scaffold a starter project
- [ ] `hookly.telegram` — a thin bridge for wiring a Telegram bot to the same CRM

Have an idea? Open an issue on GitHub — see below.

---

## Source & links

- **PyPI:** [pypi.org/project/hookly](https://pypi.org/project/hookly/)
- **GitHub:** [github.com/Xar1ee/hookly](https://github.com/Xar1ee/hookly)
- **Issues / feature requests:** [github.com/Xar1ee/hookly/issues](https://github.com/Xar1ee/hookly/issues)

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.

## License

MIT — do whatever you want with it, just keep the license notice.
