Webhooki
Zakładka Webhooki w /nay (scope nay.admin) wysyła zdarzenia skryptów na Twoje adresy URL. Wybierz zdarzenia (albo wszystkie), naciśnij Testuj, aby sprawdzić konfigurację, i podglądaj ostatnią odpowiedź każdego webhooka.
Są dwa typy:
- Discord: wklej adres URL webhooka kanału Discord (
https://discord.com/api/webhooks/...). Każde zdarzenie staje się embedem, z tytułem i etykietami w języku serwera oraz polami zdarzenia w kolejności. Formatowanie markdown Discorda w wartościach jest neutralizowane, nikt nie jest oznaczany, a zbyt długie wiadomości są przycinane do limitu 6000 znaków. - Podpisany JSON: dla Twojej własnej strony lub bota. Każdy webhook dostaje sekret, pokazywany przy utworzeniu lub ponownym wygenerowaniu.
Dostarczanie
Dostarczenie to POST z JSON:
json
{ "id": "3f9c2a1b7d4e8f60", "event": "nay_car_wash.wash", "resource": "nay_car_wash", "time": 1791670794, "data": { "player": "John", "price": 150 } }Nagłówki: X-Nay-Event, X-Nay-Delivery (to samo co id), X-Nay-Timestamp (sekundy Unix) i X-Nay-Signature: sha256=<hex>.
- Odpowiedz dowolnym statusem 2xx. Brak odpowiedzi w ciągu 15 sekund liczy się jako błąd sieci.
- Błędy sieci oraz odpowiedzi 5xx i 429 są ponawiane po 1, 10 i 60 sekundach z tym samym
X-Nay-Delivery. Pozostałe odpowiedzi 4xx nie są ponawiane. - Po 20 nieudanych dostarczeniach z rzędu webhook jest wyłączany. Włącz go ponownie w zakładce albo zapisz go z nowym adresem URL.
- Dostarczenia do Discorda nie są podpisywane i przy limitowaniu respektują
retry_afterDiscorda.
Zdarzenia
Zdarzenia nay_lib:
| Zdarzenie | Kiedy | Dane |
|---|---|---|
nay.setting | Ustawienie zostało zmienione lub przywrócone. | resource, key, action, actor |
nay.permission | Reguła uprawnień została nadana lub odebrana. | action, type, identifier, scope, actor |
nay.token | Token API został utworzony lub unieważniony. | action, id, label, actor |
nay.test | Przycisk Testuj. | message |
Każdy skrypt wymienia swoje zdarzenia na własnej stronie, na przykład Car Wash.
Sprawdzanie podpisu
Zanim zaufasz podpisanemu dostarczeniu, sprawdź jego podpis: HMAC-SHA256 z <X-Nay-Timestamp>.<raw body>, z kluczem w postaci sekretu dokładnie tak, jak został pokazany (sam tekst hex). Odrzucaj stare znaczniki czasu.
Node.js (Express):
js
import { createHmac, timingSafeEqual } from "node:crypto";
import express from "express";
const app = express();
app.post("/nayretis", express.raw({ type: "application/json" }), (req, res) => {
const timestamp = req.get("X-Nay-Timestamp") ?? "";
const expected = "sha256=" + createHmac("sha256", process.env.NAY_WEBHOOK_SECRET).update(`${timestamp}.${req.body}`).digest("hex");
const given = req.get("X-Nay-Signature") ?? "";
const valid = given.length === expected.length && timingSafeEqual(Buffer.from(given), Buffer.from(expected));
if (!valid || Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) {
return res.sendStatus(401);
}
const delivery = JSON.parse(req.body);
res.sendStatus(204);
});PHP:
php
<?php
$body = file_get_contents('php://input');
$timestamp = $_SERVER['HTTP_X_NAY_TIMESTAMP'] ?? '';
$expected = 'sha256=' . hash_hmac('sha256', $timestamp . '.' . $body, getenv('NAY_WEBHOOK_SECRET'));
if (!hash_equals($expected, $_SERVER['HTTP_X_NAY_SIGNATURE'] ?? '') || abs(time() - (int) $timestamp) > 300) {
http_response_code(401);
exit;
}
$delivery = json_decode($body, true);
http_response_code(204);