Metadata-Version: 2.4
Name: etoneya-api
Version: 0.2.0
Summary: Python client library for the Etoneya Subscription Management API.
Author-email: nloverx <owner@nloverx.best>
License: MIT
Project-URL: Homepage, https://github.com/keys-nloverx/etoneya-lib
Project-URL: Documentation, https://github.com/keys-nloverx/etoneya-lib#readme
Project-URL: Repository, https://github.com/keys-nloverx/etoneya-lib
Project-URL: Issues, https://github.com/keys-nloverx/etoneya-lib/issues
Project-URL: Changelog, https://github.com/keys-nloverx/etoneya-lib/blob/master/CHANGELOG.md
Keywords: etoneya,subscription,api,client,httpx
Classifier: Development Status :: 4 - Beta
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: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx<1.0,>=0.25
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
Requires-Dist: pytest-cov>=5.0; extra == "dev"
Requires-Dist: black>=24.0; extra == "dev"
Requires-Dist: mypy>=1.10; extra == "dev"
Requires-Dist: ruff>=0.5; extra == "dev"
Dynamic: license-file

# Etoneya API Client

[![CI](https://github.com/keys-nloverx/etoneya-lib/actions/workflows/test.yml/badge.svg)](https://github.com/keys-nloverx/etoneya-lib/actions/workflows/test.yml)
[![Python](https://img.shields.io/badge/python-3.10%2B-blue)](#installation)
[![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)

Fully-typed Python client for the Etoneya Subscription Management API. Ships
both a synchronous (`EtoneyaClient`) and asynchronous (`AsyncEtoneyaClient`)
flavour built on top of [`httpx`](https://www.python-httpx.org/).

## Highlights

- 🔁 **Sync + async** clients with identical APIs
- 🧱 **Typed dataclass models** — including `BanCheckResult` and `HealthInfo`
- 🚦 **Granular exceptions** mapped from HTTP status codes (401/403, 404, 4xx, 409, 429, 5xx, transport)
- 🧰 **Context-manager support** (`with` / `async with`)
- 🧪 **First-class testability** — built around `httpx.MockTransport`
- 📦 **PEP 561** typed (`py.typed` shipped)

## Installation

Requires **Python 3.10+**.

```bash
pip install etoneya-api
```

Or from source:

```bash
git clone https://github.com/keys-nloverx/etoneya-lib.git
cd etoneya-lib
pip install -e ".[dev]"
```

## Quick start

### Synchronous

```python
from etoneya import EtoneyaClient

with EtoneyaClient(
    base_url="https://api.example.com",
    auth_token="your_auth_token_here",
) as client:
    user = client.create_user(
        tg_id=123456789,
        type_sub="premium",
        udid="udid-1",
        limit_devices=3,
    )
    print(user.id)

    info = client.health_check()
    print(info.status, info.build)
```

### Asynchronous

```python
import asyncio
from etoneya import AsyncEtoneyaClient

async def main() -> None:
    async with AsyncEtoneyaClient(
        base_url="https://api.example.com",
        auth_token="your_auth_token_here",
    ) as client:
        info = await client.health_check()
        print(info.status)

asyncio.run(main())
```

## API surface

### User management

```python
client.create_user(tg_id=..., type_sub="premium", udid="...", limit_devices=3)
client.get_user(1)
client.get_user_by_telegram(123456789)
client.list_users()                        # all users
client.list_users(filter="active")         # active only
client.list_users(filter="banned")         # banned only
client.update_user(1, limit_devices=5)
client.ban_user(1, reason="Spam")
client.unban_user(1)
client.delete_user(1)
```

The legacy aliases `get_all_users`, `get_active_users`, and
`get_banned_users` are kept for backwards compatibility.

### Devices

```python
client.get_device(1)
client.get_device_by_hwid("hwid-1")
client.get_user_devices("udid-1")
client.delete_device(1)
```

### IP bans

```python
client.create_ip_ban("192.168.1.1", reason="abuse")

result = client.check_ip_ban("192.168.1.1")
if result.is_banned:
    print(result.ban_info.reason)

client.get_all_bans()
client.delete_ip_ban(1)
client.delete_ip_ban_by_address("192.168.1.1")
```

### System

```python
info = client.health_check()           # HealthInfo
client.update_subscriptions("hook-token")
```

## Error handling

```python
from etoneya import (
    AuthenticationError,
    ConflictError,
    EtoneyaAPIError,
    NotFoundError,
    RateLimitError,
    ServerError,
    TransportError,
    ValidationError,
)

try:
    client.get_user(999)
except NotFoundError as exc:
    print(exc.status_code, exc.message, exc.response)
except AuthenticationError:
    ...
except RateLimitError:
    ...
except TransportError:
    # connection refused / timeouts / DNS — preserves the underlying httpx error
    ...
except EtoneyaAPIError:
    ...
```

| HTTP status | Exception |
|-------------|-----------|
| 400, 422    | `ValidationError` |
| 401, 403    | `AuthenticationError` |
| 404         | `NotFoundError` |
| 409         | `ConflictError` |
| 429         | `RateLimitError` |
| 5xx         | `ServerError` |
| transport   | `TransportError` |
| anything else | `EtoneyaAPIError` |

## Models

All models are slotted, keyword-only `dataclass`es; convert to/from JSON-style
dicts via `from_dict` / `to_dict`.

```python
@dataclass(slots=True, kw_only=True)
class User:
    id: int
    tg_id: int
    type_sub: str
    udid: str
    limit_devices: int
    reason: str = ""
    is_banned: bool = False
    is_active: bool = True
    allowed_useragents: list[str] = []
    status: str = "user"  # "user" | "volunteer"
```

`Device`, `BannedIP`, `BanCheckResult`, and `HealthInfo` follow the same
pattern.

## Configuration

```python
import httpx

EtoneyaClient(
    base_url="https://api.example.com",
    auth_token="...",
    timeout=httpx.Timeout(10.0, read=30.0),
    user_agent="my-bot/1.0",
)
```

You can also inject a custom `httpx.BaseTransport` (or
`httpx.AsyncBaseTransport`) — handy for tests, retries via `httpx-retries`, or
custom proxying.

## Development

```bash
git clone https://github.com/keys-nloverx/etoneya-lib.git
cd etoneya-lib
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
pre-commit install

ruff check .
black --check .
mypy etoneya/
pytest
```

The CI matrix runs lint + type-check + tests on Python 3.10 – 3.13 across
Linux/macOS/Windows.

## License

MIT — see [LICENSE](LICENSE).

## Contact

- Author: nloverx (`owner@nloverx.best`)
- Telegram: [@nloverx](https://t.me/nloverx)
