Вики сервера клана 27

API и кастомизация

Для тех, кто делает свой сайт, бота или магазин для сервера клана 27, и для тех, кто хочет перекрасить клиентский мод под себя. Свой мод вместо rozb-ui писать не нужно: всё, что сервер отдаёт наружу, — здесь.

API сервера#

Адрес — https://27.rozb.pw/api/v1/. Ответы — JSON в UTF-8, время — секунды Unix, суммы — целые АР.

Без ключа доступно всё публичное: игроки, топы, кланы, кто в сети. С ключом — ваш счёт в банке и счета на оплату игрокам: так магазин на сайте принимает АР. Деньги со счёта игрока списываются только тогда, когда он сам нажмёт «Оплатить» в игре, — ключ не может перевести ни одного АР без его согласия.

Ключ передаётся заголовком:

Authorization: Bearer rozb_…

Ключ — как пароль

Держите ключ только на своём сервере (бэкенде). В браузерный JavaScript, в репозиторий и в чат его не вставляйте. Утёк — сразу отзовите в игре и создайте новый.

Ключи#

В игре: Банк → Ключи API (в моде) или команда /bank api.

Команды: /bank api, /bank api create, /bank api create clan, /bank api revoke <номер>.

Ошибки и лимиты#

Ошибка — HTTP-код и тело {"error": "<код>", "message": "<текст для людей>"}.

Код HTTP Когда
400 неверные поля запроса (bad_amount, missing_comment, bad_callback…)
401 нет ключа (no_key) или он неверный / отозван (bad_key)
403 игрок заблокировал счета от вашего ключа (blocked)
404 нет такого игрока, клана, счёта (unknown_player, unknown_clan, unknown_invoice)
409 счёт уже не ждёт оплаты (not_pending)
429 больше 120 запросов в минуту с одного адреса (rate_limit, заголовок Retry-After) или лимит неоплаченных счетов (too_many_pending)
502, 503 сервер Minecraft выключен или запускается (server_down, pc_offline, unavailable) — повторите позже

Сервер работает не круглосуточно: когда он выключен, API отвечает 502. Счета при этом не теряются.

Публичные запросы#

GET /players/{ник} — карточка игрока (как подсказка у ника в чате):

{
  "name": "Alice",
  "uuid": "f0a8f4ba-…",
  "online": true,
  "clan": {"tag": "SEV", "name": "Северяне", "rank": "leader"},
  "stats": {
    "playtime": {"value": 50825, "place": 1},
    "player_kills": {"value": 0},
    "mob_kills": {"value": 116, "place": 3},
    "advancements": {"value": 42, "place": 1},
    "deaths": {"value": 5, "place": 4}
  },
  "first_join": 1792411482,
  "first_join_approx": false
}

playtime — в тиках (20 в секунду). place нет, если значение нулевое. clan нет, если игрок не в клане или клан скрывает состав; rank — leader, deputy, elder, member (нет — игрок скрыл ранг). first_join_approx: true — игрок заходил раньше, чем сервер начал это записывать: дата приблизительная.

GET /tops/{показатель}?period=all|week&limit=10 — топ (до 50 строк). Показатели: playtime, player_kills, mob_kills, advancements, deaths. Неделя — разница с суточным снимком семидневной давности; пока его нет, "pending": true и пустой список.

{"metric": "advancements", "period": "all", "pending": false,
 "entries": [{"place": 1, "name": "Alice", "value": 126}, {"place": 2, "name": "Bob", "value": 47}]}

GET /clans — кланы: tag, name, а если клан не скрывает — member_count, leader, created. GET /clans/{тег} — то же и members (name, rank), если состав открыт.

GET /online — {"count": 3, "max": 20, "players": ["Alice", "Bob", "Carol"]}.

GET /currency — что считается АР: {"name": "АР", "items": ["minecraft:deepslate_diamond_ore", …], "withdraw_item": "…"}.

Счёт ключа#

GET /me — {"account": {"type": "player" | "clan", "name": "Alice"}, "balance": 1250, "key": "7"}.

GET /me/history?limit=20&offset=0 — операции счёта (до 100 за раз), новые первыми:

[{"id": 812, "time": 1792411600, "type": "transfer", "amount": 25, "balance_after": 1250,
  "other": "Bob", "note": "Счёт inv_…: Алмазный меч"}]

type: deposit, withdraw, transfer, admin, api; amount со знаком.

Счета на оплату#

POST /invoices — выставить счёт игроку:

{"payer": "Bob", "amount": 25, "comment": "Алмазный меч",
 "external_id": "order-1042", "callback_url": "https://shop.example/rozb-hook", "ttl_hours": 24}
Поле
payer ник игрока (заходил на сервер)
amount целое, от 1 АР
comment за что, до 100 символов — игрок видит это в игре
external_id необязательно: ваш номер заказа, до 64 символов, вернётся в ответах и вебхуках
callback_url необязательно: куда слать вебхуки (http/https; адреса локальных сетей не принимаются)
ttl_hours необязательно: сколько ждать оплаты, 1–168 часов (по умолчанию 24)

Ответ 201 — счёт:

{"id": "inv_T2DGDYcQ6fWaNyYs", "status": "pending", "amount": 25, "comment": "Алмазный меч",
 "payer": "Bob", "payee": "Alice", "external_id": "order-1042",
 "created": 1792411600, "expires": 1792498000, "updated": 1792411600}

status: pending (ждёт), paid, declined (игрок отказался), expired, cancelled (отменили вы).

Игрок в сети сразу видит в чате «Счёт на оплату от Alice: 25 АР — Алмазный меч [Оплатить] [Отказать]», не в сети — напоминание при входе; все неоплаченные счета — в банке, «Счета на оплату». «Оплатить» открывает окно с суммой и получателем, деньги переводятся только после нажатия в нём. Игрок может отказаться и заблокировать отправителя — тогда новые счета от ключей этого счёта получат 403 blocked.

Ограничения: не больше 3 неоплаченных счетов от одного ключа одному игроку и 50 на ключ.

GET /invoices/{id} — счёт (только свой: чужой — 404). DELETE /invoices/{id} — отменить, пока он ждёт.

Вебхуки#

Если у счёта есть callback_url, при оплате, отказе и истечении срока на него приходит POST:

X-Rozb-Event: invoice.paid
X-Rozb-Signature: sha256=<hex HMAC-SHA256 тела секретом ключа>
Content-Type: application/json

{"event": "invoice.paid", "invoice": { …как в GET /invoices/{id}… }}

События: invoice.paid, invoice.declined, invoice.expired. Ответьте 2xx; иначе — ещё две попытки (через 30 секунд и через 5 минут). Перенаправления не выполняются. Вебхук — подсказка: перед выдачей товара сверьте статус запросом GET /invoices/{id}.

Проверка подписи (Python):

import hmac, hashlib

def valid(body: bytes, header: str, secret: str) -> bool:
    good = "sha256=" + hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(good, header or "")

Примеры#

curl:

curl https://27.rozb.pw/api/v1/tops/playtime?limit=5
curl -H "Authorization: Bearer $ROZB_KEY" https://27.rozb.pw/api/v1/me
curl -X POST -H "Authorization: Bearer $ROZB_KEY" -H "Content-Type: application/json" \
  -d '{"payer":"Bob","amount":25,"comment":"Алмазный меч","external_id":"order-1042"}' \
  https://27.rozb.pw/api/v1/invoices

Python (магазин: выставить счёт и дождаться оплаты):

import os, time, requests

API = "https://27.rozb.pw/api/v1"
H = {"Authorization": "Bearer " + os.environ["ROZB_KEY"]}

inv = requests.post(f"{API}/invoices", headers=H, json={
    "payer": "Bob", "amount": 25, "comment": "Алмазный меч", "external_id": "order-1042"}).json()
while True:
    st = requests.get(f"{API}/invoices/{inv['id']}", headers=H).json()["status"]
    if st != "pending":
        break
    time.sleep(10)
print("итог:", st)   # paid — выдать товар

JavaScript (Node 18+, на сервере):

const API = "https://27.rozb.pw/api/v1";
const r = await fetch(`${API}/players/Alice`);
const p = await r.json();
console.log(p.name, p.stats.playtime.value / 20 / 3600, "ч");

Публичные запросы можно делать и прямо из браузера (CORS открыт), но с ключом — только со своего сервера.

Кастомизация мода#

Темы#

Настройки мода → «Тема»: встроенные «Ночь» (по умолчанию), «Графит» и «Лес». Свою тему положите файлом в config/rozb_ui/themes/<имя>.json или в ресурспак: assets/<пространство>/rozb_ui_themes/<имя>.json. Тема — только тёмная: цвета текста в окнах задаёт сервер под тёмный фон.

{
  "name": "Моя",
  "frame": "#0B1120", "chrome": "#0F172A", "toolbar": "#1E293B",
  "address": "#0F172A", "address_border": "#334155",
  "page": "#111827", "page_edge": "#374151",
  "text": "#E2E8F0", "muted": "#94A3B8", "label": "#F8FAFC",
  "icon": "#94A3B8", "icon_off": "#475569", "link": "#7DD3FC",
  "lock": "#28C840", "check": "#22C55E", "close_hover": "#DC2626",
  "nav_hover": "#334155", "nav_down": "#0F172A",
  "dots": ["#FF5F57", "#FEBC2E", "#28C840"],
  "buttons": {
    "normal":  {"fill": "#334155", "light": "#475569", "dark": "#1E293B"},
    "primary": {"fill": "#166534", "light": "#228B46", "dark": "#14532D"},
    "danger":  {"fill": "#7F1D1D", "light": "#A62D2D", "dark": "#631616"},
    "ghost":   {"fill": "#111827", "border": "#475569"},
    "field":   {"fill": "#0B0F1A", "border": "#475569"},
    "off":     {"fill": "#1E293B", "border": "#334155"}
  }
}

Цвет — #RRGGBB или #AARRGGBB. Всё, что не указано, берётся из «Ночи», поэтому в своей теме достаточно поменять пару цветов.

Ресурспак#

Свои плитки «Главной»#

Настройки мода → «Свои плитки «Главной»…»: название, команда (как в чате, например spawn или /warp шахта) и значок — id предмета (minecraft:compass). До 12 плиток, отмечены голубой точкой. Порядок всех плиток, и своих, и серверных, меняется перетаскиванием прямо на «Главной». Всё хранится в config/rozb-ui.json только у вас.