Metadata-Version: 2.4
Name: l2calendar
Version: 1.0.0
Summary: Minimal Python client for the public L2 Calendar API - Lineage 2 private server openings by chronicle.
Author: Dreiev
License: MIT
Project-URL: Homepage, https://l2calendar.com
Project-URL: Repository, https://github.com/Dreiev/l2calendar-client
Project-URL: Source (Python), https://github.com/Dreiev/l2calendar-client/tree/main/python
Project-URL: Source (JavaScript), https://github.com/Dreiev/l2calendar-client/tree/main/js
Keywords: l2calendar,lineage2,lineage-2,l2,mmorpg,game-server,server-tracker,calendar,api,api-client
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Intended Audience :: Developers
Classifier: Topic :: Games/Entertainment
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# l2calendar (Python)

Minimal, dependency-free Python client for the public **[L2 Calendar](https://l2calendar.com)** API — a free, read-only JSON feed of **Lineage 2 private server openings by chronicle**.

- Standard library only (`urllib`) — no `requests`, no dependencies.
- No API key, no auth. The API sends `Access-Control-Allow-Origin: *`.
- Python 3.8+, ships with `py.typed`.

## Install

```bash
pip install l2calendar
```

## Quick start

```python
from l2calendar import get_servers, get_chronicles

servers = get_servers(chronicle="interlude", only_upcoming=True, limit=10)
for server in servers:
    print(server["name"], server["rate"], server["opening_date"], server["website_url"])

chronicles = get_chronicles()  # [{"id": 1, "name": "Interlude", "slug": "interlude", ...}, ...]
```

## API

| Function | Returns | Notes |
|----------|---------|-------|
| `get_servers(chronicle=None, *, only_upcoming, new_only, min_rate, max_rate, limit, base_url, timeout)` | `list[dict]` | VIP first, then soonest opening. |
| `get_chronicles(*, base_url, timeout)` | `list[dict]` | The chronicle catalog (14 entries). |
| `get_server(name, **kwargs)` | `dict \| None` | Exact name, case-insensitive. |
| `search_servers(query, **kwargs)` | `list[dict]` | Matches name, website, rate, chronicle, description, labels. |
| `parse_rate(rate)` | `float \| None` | `"x5"` → `5.0`, `"x0.5"` → `0.5`, `"GvE"` → `None`. |
| `select_servers(servers, *, ...)` | `list[dict]` | Apply the filters to your own page. |

Errors raise `L2CalendarError` with an optional `.status`.

## Server shape

```python
{
    "name": str,
    "website_url": str,
    "rate": str,                  # "x5", "x1000", "GvE"
    "opening_date": str,          # "2026-02-28"
    "opening_time": str | None,
    "opening_datetime_utc": str | None,
    "description": str | None,
    "is_vip": bool,
    "vip_level": int,
    "multiproff": bool,
    "multicraft": bool,
    "is_new": bool,
    "chronicle": {"name": str, "slug": str} | None,
    "labels": [{"name": str, "slug": str, "color": str}],
}
```

> **Note:** the public API returns a single page of up to 50 servers (VIP first, then soonest opening). `limit`, rate and upcoming filters run client-side over that page — narrow with `chronicle` for precision.

## Data source

- `GET https://l2calendar.com/api/servers` — optional `?chronicle=<slug>`.
- `GET https://l2calendar.com/api/chronicles`.

## About L2 Calendar

A multilingual (EN/ES/PT/RU) calendar and tracker of Lineage 2 private server openings by chronicle — [l2calendar.com](https://l2calendar.com).

## License

MIT © Dreiev
