Metadata-Version: 2.4
Name: cristalix
Version: 3.1
Summary: Unofficial Python client for Cristalix OpenAPI
Author: kkp_
License: MIT
Project-URL: Homepage, https://cristalix.gg
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: License :: OSI Approved :: MIT License
Classifier: Intended Audience :: Developers
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx>=0.27.0
Requires-Dist: pydantic>=2.5.0
Requires-Dist: typing-extensions>=4.0.0; python_version < "3.11"
Dynamic: license-file

Cristalix Top Python Client
===========================

Неофициальный Python-клиент для Cristalix Top API (`/v1/api`).

> ВНИМАНИЕ: библиотека не является официальным продуктом Cristalix.

## Установка

```bash
pip install cristalix
```

## Быстрый старт (sync)

```python
from cristalix import CristalixClient

client = CristalixClient(
    project_key="YOUR_PROJECT_KEY",
    token="YOUR_PROJECT_TOKEN",
)

games = client.get_games()
leaderboard = client.get_game_leaderboard(
    game_id=games[0].gameId,
    field="wins",
    limit=5,
)

print(games[0].title)
print(leaderboard[0].username, leaderboard[0].value)
```

## Быстрый старт (async)

```python
import asyncio
from cristalix import AsyncCristalixClient


async def main() -> None:
    async with AsyncCristalixClient(
        project_key="YOUR_PROJECT_KEY",
        token="YOUR_PROJECT_TOKEN",
    ) as client:
        profile = await client.get_player_profile_by_name("KoTuK_PvP")
        print(profile.playerId, profile.name)


asyncio.run(main())
```

## Авторизация

Клиент автоматически отправляет:
- `X-Project-Key: <project_key>`
- `Authorization: Bearer <token>`

Базовый URL по умолчанию: `https://api.cristalix.gg`.

## HTTP/2

По умолчанию клиент создаёт `httpx.Client/AsyncClient` с `http2=True`.
Если в вашей среде HTTP/2 нужно отключить, передайте `http2=False`:

```python
from cristalix import CristalixClient, AsyncCristalixClient

client = CristalixClient(project_key="YOUR_PROJECT_KEY", token="YOUR_PROJECT_TOKEN", http2=False)

# async
# async with AsyncCristalixClient(project_key="YOUR_PROJECT_KEY", token="YOUR_PROJECT_TOKEN", http2=False) as client:
#     ...
```

## Параметры, которые часто используются

- `fields=...` - вернуть только указанные поля.
- `exclude=...` - исключить поля из ответа.
- `lite=true|false` - облегченный профиль (для методов профиля).
- `offset` / `limit` - пагинация.
- `mode`, `subMode`, `season`, `period` - фильтрация игровых статистик.

Периоды: `HOUR`, `DAY`, `WEEK`, `MONTH`, `QUARTER`, `YEAR`, `ALL`.

## API: кратко по всем функциям (запрос -> ответ)

### Игры

**1) Список игр (`get_games`)**  
`GET /v1/api/games?fields=gameId,title,modes`

```json
[
  {
    "gameId": "uhc",
    "title": "UHC",
    "season": "S1",
    "modes": [
      {"key": "solo", "name": "Solo", "fields": [{"key": "wins", "type": "number"}]}
    ]
  }
]
```

**2) Таблица лидеров (`get_game_leaderboard`)**  
`GET /v1/api/games/{gameId}/leaderboard?field=wins&mode=solo&period=WEEK&offset=0&limit=10&trends=true`

```json
[
  {
    "position": 1,
    "playerId": "uuid-1",
    "username": "PlayerOne",
    "value": 245,
    "trends": {"wins": {"trend": "STABLE", "hourlyRate": 0.8}}
  }
]
```

**3) Расширенная таблица (`get_game_leaderboard_rich`)**  
`GET /v1/api/games/{gameId}/leaderboard/rich?field=wins&mode=solo&period=ALL&limit=10`

```json
[
  {
    "position": 1,
    "playerId": "uuid-1",
    "username": "PlayerOne",
    "value": 1024,
    "fieldsByPeriod": {"wins": {"DAY": 5, "WEEK": 40, "ALL": 1024}},
    "periodEnding": 1714406400
  }
]
```

### Статистика игрока в игре

**4) Статистика игрока (`get_player_game_statistics`)**  
`GET /v1/api/games/{gameId}/player/{playerId}?mode=solo&season=S1&period=ALL`

```json
{
  "playerId": "uuid-1",
  "gameId": "uhc",
  "mode": "solo",
  "subMode": "default",
  "season": "S1",
  "period": "ALL",
  "fields": {"wins": 245, "kills": 1900},
  "privacyHidden": false
}
```

**5) Статистика по периодам (`get_player_game_periods_statistics`)**  
`GET /v1/api/games/{gameId}/player/{playerId}/periods?mode=solo&season=S1`

```json
{
  "playerId": "uuid-1",
  "gameId": "uhc",
  "periods": {
    "DAY": {"wins": 2},
    "WEEK": {"wins": 15},
    "ALL": {"wins": 245}
  },
  "privacyHidden": false
}
```

**6) Позиция игрока в лидерборде (`get_player_game_leaderboard_position`)**  
`GET /v1/api/games/{gameId}/player/{playerId}/position?field=wins&period=WEEK`

```json
{
  "playerId": "uuid-1",
  "gameId": "uhc",
  "field": "wins",
  "period": "WEEK",
  "position": 7,
  "privacyHidden": false
}
```

### Профили игроков

**7) Профиль по ID (`get_player_profile`)**  
`GET /v1/api/players/{playerId}?lite=true`

```json
{
  "playerId": "uuid-1",
  "name": "PlayerOne",
  "groups": {"staff": "moder"},
  "status": {"online": true, "realm": "hub-1"},
  "stats": {"views": 5000, "likes": 450}
}
```

**8) Профиль по нику (`get_player_profile_by_name`)**  
`GET /v1/api/players/by-name/{name}?lite=true`

```json
{
  "playerId": "uuid-1",
  "name": "PlayerOne"
}
```

**9) Batch по ID (`get_players_batch`)**  
`POST /v1/api/players/batch`  
Body:

```json
{"ids": ["uuid-1", "uuid-2"]}
```

Response:

```json
{
  "requested": 2,
  "found": 1,
  "notFound": ["uuid-2"],
  "items": [{"playerId": "uuid-1", "name": "PlayerOne"}]
}
```

**10) Batch по никам (`get_players_batch_by_names`)**  
`POST /v1/api/players/batch/names`  
Body:

```json
{"names": ["PlayerOne", "PlayerTwo"]}
```

Response:

```json
{
  "requested": 2,
  "found": 2,
  "notFound": [],
  "items": [
    {"playerId": "uuid-1", "name": "PlayerOne"},
    {"playerId": "uuid-2", "name": "PlayerTwo"}
  ]
}
```

**11) Поиск игроков (`search_players`)**  
`GET /v1/api/players/search?q=kotik&limit=5`

```json
[
  {"playerId": "uuid-1", "name": "KoTuK_PvP"},
  {"playerId": "uuid-2", "name": "Kotik123"}
]
```

### Социальные данные

**12) Друзья (`get_player_friends`)**  
`GET /v1/api/players/{playerId}/friends?offset=0&limit=20`

```json
{
  "total": 2,
  "items": [
    {"playerId": "uuid-a", "username": "FriendA"},
    {"playerId": "uuid-b", "username": "FriendB"}
  ],
  "privacyHidden": false
}
```

**13) Подписчики/подписки (`get_player_subscribers`)**  
`GET /v1/api/players/{playerId}/subscribers?type=INCOMING&offset=0&limit=20`

```json
{
  "total": 1,
  "items": [{"playerId": "uuid-z", "username": "FollowerZ"}],
  "privacyHidden": false
}
```

`type`: `INCOMING` или `OUTGOING`.

**14) История ников (`get_player_name_history`)**  
`GET /v1/api/players/{playerId}/history`

```json
{
  "items": [
    {"oldName": "OldNick", "newName": "PlayerOne", "changedAt": "2024-01-05T12:00:00Z"}
  ],
  "privacyHidden": false
}
```

**15) Локация игрока (`get_player_location`)**  
`GET /v1/api/players/{playerId}/location`

```json
{
  "playerId": "uuid-1",
  "online": true,
  "realm": "hub-1",
  "realmType": "LOBBY",
  "privacyHidden": false
}
```

**16) Реакции (`get_player_reactions`)**  
`GET /v1/api/players/{playerId}/reactions?type=LIKE&offset=0&limit=20`

```json
{
  "total": 2,
  "items": [
    {
      "reactorId": "uuid-x",
      "username": "UserX",
      "type": "LIKE",
      "reactedAt": "2024-02-01T10:30:00Z"
    }
  ]
}
```

`type`: `LIKE` или `DISLIKE`.

### Рейтинги и роли

**17) Рейтинг кармы (`get_karma_ratings`)**  
`GET /v1/api/ratings/karma?offset=0&limit=10`

```json
[
  {"position": 1, "playerId": "uuid-1", "username": "TopKarma", "value": 9999}
]
```

**18) Рейтинг лайков (`get_likes_ratings`)**  
`GET /v1/api/ratings/likes?offset=0&limit=10`

```json
[
  {"position": 1, "playerId": "uuid-2", "username": "TopLikes", "value": 12345}
]
```

**19) Рейтинг просмотров (`get_views_ratings`)**  
`GET /v1/api/ratings/views?offset=0&limit=10`

```json
[
  {"position": 1, "playerId": "uuid-3", "username": "TopViews", "value": 987654}
]
```

**20) Роли (`get_roles`)**  
`GET /v1/api/roles`

```json
[
  {
    "name": "moder",
    "fullName": "Moderator",
    "prefix": "[MOD]",
    "priority": 80,
    "staffGroup": true
  }
]
```

## Обработка ошибок

Клиент может бросать:
- `HTTPStatusError` - API вернуло неуспешный HTTP-код.
- `NetworkError` - проблема сети/таймаута.
- `ValidationError` - ответ API не совпал с ожидаемой структурой.

