Webhooks
O separador Webhooks do /nay (âmbito nay.admin) envia os eventos dos scripts para os seus URL. Escolha os eventos (ou todos), clique em Testar para verificar a configuração e veja a última resposta de cada webhook.
Há dois tipos:
- Discord: cole o URL de webhook de um canal do Discord (
https://discord.com/api/webhooks/...). Cada evento torna-se um embed, com título e etiquetas no idioma do servidor e os campos do evento por ordem. O markdown do Discord nos valores é escapado, ninguém é mencionado, e as mensagens longas são cortadas no limite de 6000 caracteres. - JSON assinado: para o seu próprio site ou bot. Cada webhook recebe um segredo, mostrado quando é criado ou gerado de novo.
Entregas
Uma entrega é um POST de JSON:
json
{ "id": "3f9c2a1b7d4e8f60", "event": "nay_car_wash.wash", "resource": "nay_car_wash", "time": 1791670794, "data": { "player": "John", "price": 150 } }Cabeçalhos: X-Nay-Event, X-Nay-Delivery (igual ao id), X-Nay-Timestamp (segundos Unix) e X-Nay-Signature: sha256=<hex>.
- Responda com qualquer status 2xx. Sem resposta em 15 segundos, a entrega conta como erro de rede.
- Os erros de rede e as respostas 5xx e 429 são reenviados após 1, 10 e 60 segundos com o mesmo
X-Nay-Delivery. As outras respostas 4xx não são reenviadas. - Depois de 20 entregas falhadas seguidas, o webhook é desativado. Reative-o no separador com Reativar, ou guarde-o com um novo URL.
- As entregas para o Discord não são assinadas e respeitam o
retry_afterdo Discord quando há limite de pedidos.
Eventos
Eventos do nay_lib:
| Evento | Quando | Dados |
|---|---|---|
nay.setting | Uma definição é alterada ou reposta. | resource, key, action, actor |
nay.permission | Uma regra de permissão é concedida ou revogada. | action, type, identifier, scope, actor |
nay.token | Um token de API é criado ou revogado. | action, id, label, actor |
nay.test | O botão Testar. | message |
Cada script lista os seus próprios eventos na respetiva página, por exemplo Car Wash.
Verificar a assinatura
Antes de confiar numa entrega assinada, verifique a respetiva assinatura: HMAC-SHA256 de <X-Nay-Timestamp>.<raw body>, com o segredo como chave, exatamente como é mostrado (o próprio texto hexadecimal). Recuse timestamps antigos.
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);