Skip to content

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_after do Discord quando há limite de pedidos.

Eventos ​

Eventos do nay_lib:

EventoQuandoDados
nay.settingUma definição é alterada ou reposta.resource, key, action, actor
nay.permissionUma regra de permissão é concedida ou revogada.action, type, identifier, scope, actor
nay.tokenUm token de API é criado ou revogado.action, id, label, actor
nay.testO 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);