Metadata-Version: 2.4
Name: falcon-notify
Version: 0.1.0
Summary: Python client SDK for the Falcon Notification Gateway
Project-URL: Homepage, https://github.com/your-org/falcon-notify
Project-URL: Documentation, https://github.com/your-org/falcon-notify#readme
Project-URL: Bug Tracker, https://github.com/your-org/falcon-notify/issues
Author: Falcon Gateway Team
License: MIT
License-File: LICENSE
Keywords: email,falcon,gateway,matrix,notification,push,sms,whatsapp
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Communications
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.8
Requires-Dist: requests>=2.28.0
Provides-Extra: async
Requires-Dist: httpx>=0.24.0; extra == 'async'
Provides-Extra: dev
Requires-Dist: mypy>=1.0; extra == 'dev'
Requires-Dist: pytest-mock>=3.10; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Requires-Dist: responses>=0.23.0; extra == 'dev'
Requires-Dist: ruff>=0.1.0; extra == 'dev'
Description-Content-Type: text/markdown

# falcon-notify

Python client SDK for the **Falcon Notification Gateway** - send SMS, Email, Push, WhatsApp, and Matrix notifications with minimal setup.

Compatible with **Python 3.8 – 3.13**.

---

## Installation

```bash
pip install falcon-notify
```

Requires only [`requests`](https://pypi.org/project/requests/) - no other dependencies.

---

## Quick start

```python
from falcon_notify import FalconClient, SMS, Email, Push, WhatsApp, Matrix, Schedule

client = FalconClient(
    base_url="https://your-gateway.example.com",
    token="your-platform-token",
)

# SMS
result = client.send(SMS("Your OTP is 1234"), recipients=["+255712345678"])
print(result.ok, result.message)

# Email
result = client.send(
    Email("Welcome!", "Thanks for signing up.", html="<h1>Thanks for signing up!</h1>"),
    recipients=[{"email": "alice@example.com"}],
)

# Push notification
result = client.send(
    Push("New order", "Your order #1234 has been confirmed.", data={"order_id": "1234"}),
    recipients=[{"device_token": "fcm-device-token-here"}],
)

# WhatsApp
result = client.send(
    WhatsApp("Hello from Falcon Gateway!"),
    recipients=[{"phone": "+255712345678"}],
)

# Matrix (Element / Synapse)
result = client.send(
    Matrix("Server alert: disk at 90%", html="<b>Server alert</b>: disk at 90%"),
    recipients=[{"matrix_user": "@alice:example.org"}],
)
```

---

## Multi-channel broadcast

Send across several channels in a single request:

```python
result = client.send(
    channels=[
        SMS("Your verification code is 5678"),
        Email("Verification code", "Your code is 5678"),
    ],
    recipients=[
        {"phone": "+255712345678", "email": "alice@example.com"}
    ],
)
```

---

## Phonebooks

Send to a saved phonebook instead of listing individual recipients:

```python
result = client.send(SMS("Monthly newsletter"), phonebook_codes=["PB-ABCD12"])

# Auto-save resolved recipients back to a phonebook
result = client.send(
    SMS("Flash sale!"),
    recipients=["+255712345678", "+255787654321"],
    save_to_phonebook="PB-ABCD12",
)
```

---

## Personalized messages

Different content per recipient:

```python
result = client.send_messages([
    {
        "recipient": "+255712345678",
        "channels": {"sms": True},
        "content": {"sms": {"text": "Hi Alice, your balance is 5000"}},
    },
    {
        "recipient": {"email": "bob@example.com"},
        "channels": {"email": True},
        "content": {"email": {"subject": "Hi Bob", "body": "Your balance is 3200"}},
    },
])
```

---

## Scheduling

### Run once

```python
result = client.send(
    SMS("Your appointment is tomorrow at 10 AM"),
    recipients=["+255712345678"],
    schedule=Schedule.once("2026-09-01T09:00:00Z", timezone="Africa/Dar_es_Salaam"),
)
```

### Cron - named preset

```python
result = client.send(
    SMS("Good morning!"),
    recipients=["+255712345678"],
    schedule=Schedule.daily_8am(timezone="Africa/Dar_es_Salaam"),
)
```

Available presets: `EVERY_15MIN`, `EVERY_30MIN`, `EVERY_HOUR`, `DAILY_8AM`, `DAILY_NOON`, `DAILY_6PM`, `WEEKDAYS_8AM`, `WEEKLY_MON`, `WEEKLY_FRI`, `MONTHLY_1ST`.

Shortcut methods: `Schedule.daily_8am()`, `Schedule.daily_noon()`, `Schedule.daily_6pm()`, `Schedule.weekdays_8am()`, `Schedule.weekly_monday()`, `Schedule.weekly_friday()`, `Schedule.monthly_first()`, `Schedule.every_hour()`, `Schedule.every_30min()`, `Schedule.every_15min()`.

### Cron - raw expression

```python
schedule = Schedule.cron(
    "0 9 * * 1-5",
    timezone="Africa/Nairobi",
    ends_at="2026-12-31T23:59:59Z",
    max_runs=50,
)
```

### Interval

```python
schedule = Schedule.interval(30, timezone="UTC")  # every 30 minutes
```

---

## Error handling

```python
from falcon_notify.exceptions import (
    FalconError,    # base - catch-all
    AuthError,      # HTTP 403 - bad token
    ValidationError,# HTTP 400 - bad payload
    NetworkError,   # connection error / timeout
    ServerError,    # HTTP 5xx
)

try:
    result = client.send(SMS("Hello"), recipients=["+255712345678"])
except AuthError:
    print("Check your platform token.")
except ValidationError as e:
    print("Payload error:", e)
except NetworkError as e:
    print("Network problem:", e)
except FalconError as e:
    print("Unexpected error:", e)
```

---

## Context manager

```python
with FalconClient(base_url="https://gateway.example.com", token="token") as client:
    client.send(SMS("Hello"), recipients=["+255712345678"])
```

---

## Channel reference

| Builder | Constructor | Key fields |
|---|---|---|
| `SMS(text)` | `SMS("Your code is 1234")` | `text` |
| `Email(subject, body)` | `Email("Hi", "Body", html=..., from_name=..., reply_to=..., attachment=bytes_or_b64, attachment_name=...)` | `subject`, `body`, `html`*, `from_name`*, `from_email`*, `reply_to`*, `attachment`*, `attachment_name`* |
| `Push(title, body)` | `Push("Alert", "Server down", data={...}, image_url=...)` | `title`, `body`, `data`*, `image_url`* |
| `WhatsApp(text)` | `WhatsApp("Hi", template_name=..., template_vars=[...])` | `text`, `media_url`*, `template_name`*, `template_vars`* |
| `Matrix(text)` | `Matrix("Alert", html=..., msg_type="m.text")` | `text`, `html`*, `msg_type` |

*optional*

---

## Email attachments

Pass raw `bytes` (auto-encoded) or an already base64-encoded `str`:

```python
with open("invoice.pdf", "rb") as f:
    result = client.send(
        Email(
            subject="Your invoice",
            body="Please find your invoice attached.",
            attachment=f.read(),          # bytes → auto base64-encoded
            attachment_name="invoice.pdf",
        ),
        recipients=[{"email": "alice@example.com"}],
    )
```

---

## Running tests

```bash
pip install falcon-notify[dev]
pytest
```
