Registros de Discord webhook desde un servidor FiveM: guía de PerformHttpRequest
Envía registros a Discord desde FiveM con PerformHttpRequest e inserciones JSON. Solo lado del servidor, URL en un convar, una cola para límites de velocidad, y qué registrar.
Quieres saber quién compró qué, quién dio dinero, quién ejecutó un comando de administrador y quién se unió, sin mirar la consola todo el día. Un webhook de Discord lo hace: tu servidor publica un mensaje en un canal. Esta guía muestra el lado del servidor Lua con PerformHttpRequest, cómo construir inserciones, cómo mantener la URL segura y cómo evitar el límite de velocidad.
Crea el webhook
En Discord, abre la configuración del canal, luego Integraciones, luego Webhooks, crea uno y copia su URL. Se parece a https://discord.com/api/webhooks/<id>/<token>. Cualquiera con esa URL puede publicar en tu canal, así que trátala como una contraseña.
Mantén la URL en un convar
Ponlo en server.cfg:
set discord_webhook_logs "https://discord.com/api/webhooks/ID/TOKEN"Usa set, no setr o sets. Un convar replicado se envía a cada cliente, que filtraría la URL. Léelo en el servidor:
local WEBHOOK = GetConvar('discord_webhook_logs', '')Si el convar está vacío, salta el envío en lugar de dar error. Puedes mantener un convar por tipo de registro (dinero, admin, uniones) para enviarlos a diferentes canales.
Envía un mensaje
local function sendLog(title, description, color, fields)
if WEBHOOK == '' then return end
local embed = {
title = title,
description = description,
color = color or 3447003,
fields = fields,
timestamp = os.date('!%Y-%m-%dT%H:%M:%SZ'),
}
PerformHttpRequest(WEBHOOK, function(status, body, headers)
if status ~= 204 and status ~= 200 then
print(('webhook failed: %s'):format(status))
end
end, 'POST', json.encode({ username = 'Server logs', embeds = { embed } }), {
['Content-Type'] = 'application/json',
})
endPuntos a saber:
colores un número decimal, no una cadena hex.3447003es azul y15158332es rojo. Convierte hex contonumber('FF0000', 16).timestampdebe ser una fecha ISO 8601 en UTC. El!en el formato hace queos.dateuse UTC.fieldses una lista de{ name = '...', value = '...', inline = true }. Un valor de campo no puede estar vacío.- Una llamada webhook exitosa devuelve el estado 204, con un cuerpo vacío.
- Discord limita los tamaños. Los títulos son cortos (256 caracteres), las descripciones son largas pero limitadas (4096), y un mensaje contiene un número limitado de campos e inserciones. Corta texto largo antes de enviarlo.
Un ejemplo de llamada, para un jugador que envió dinero:
sendLog('Money transfer', 'A player sent money', 3066993, {
{ name = 'From', value = ('%s (id %d)'):format(GetPlayerName(src), src), inline = true },
{ name = 'To', value = ('%s (id %d)'):format(GetPlayerName(target), target), inline = true },
{ name = 'Amount', value = ('$%d'):format(amount), inline = false },
})Solo del lado del servidor, nunca desde el cliente
Cuidado: no llames a
PerformHttpRequestpara un webhook en un script de cliente, y no envíes la URL al cliente. Los archivos del cliente pueden ser leídos por cada jugador, así que la URL se filtra y cualquiera puede inundar tu canal.
Si un evento en el cliente debe ser registrado, envía un evento al servidor, y deja que el servidor decida qué registrar desde datos en los que confía. Un cliente que puede activar «registra este texto» también puede activar registros falsos. Para la seguridad de eventos en general, lee eventos seguros en FiveM. Registra lo que hizo el servidor, no lo que el cliente dice que hizo.
Límites de velocidad: usa una cola
Discord rechaza llamadas de webhook que llegan demasiado rápido, con una respuesta 429. Una ráfaga de registros, como 50 jugadores activando el mismo evento, puede golpearla. Envía los mensajes desde una cola a un ritmo constante:
local queue = {}
local function enqueue(payload)
queue[#queue + 1] = payload
end
CreateThread(function()
while true do
local item = table.remove(queue, 1)
if item then
PerformHttpRequest(WEBHOOK, function(status, _, headers)
if status == 429 then
-- put it back and wait a bit longer
table.insert(queue, 1, item)
Wait(5000)
end
end, 'POST', json.encode(item), { ['Content-Type'] = 'application/json' })
end
Wait(2000)
end
end)Esperar dos segundos entre mensajes se mantiene bajo el límite. Si necesitas más, agrupa varias inserciones en un mensaje: una solicitud puede llevar hasta 10 inserciones en su lista embeds. Para el uso de Wait, consulta Wait explicado.
Qué registrar
Registra los eventos que querrías cuando algo vaya mal:
- Dinero: transferencias grandes, compras, cualquier cosa que cree dinero. Aquí es donde el abuso se muestra primero.
- Acciones de administrador: quién usó qué comando de administrador en quién, bans, advertencias, teleports. Consulta banear y advertir jugadores.
- Uniones y salidas, con los identificadores del jugador, lo que ayuda con disputas y bans.
- Generación de elementos y uso de armas, si los permites.
No registres todo. Un canal con un mensaje cada segundo es un canal que nadie lee. Elige unos pocos canales, pon los importantes donde el personal mira, y mantén el resto corto. Mantén los datos personales a lo que necesitas, y no registres chat privado.
Consejo: pon tu función de registro en un pequeño recurso y llámala con una exportación desde tus otros scripts, así la URL del webhook y la cola de límite de velocidad viven en un lugar. Consulta exportaciones en FiveM.
Lista de verificación
| Síntoma | Solución |
|---|---|
| Nada llega | Comprueba el nombre del convar, que la URL esté completa y que la llamada se ejecute en el servidor |
| Estado 400 | El JSON es inválido: valor de campo vacío, texto demasiado largo, o una cadena de color hex |
| Estado 429 | Envía a través de una cola con una pausa entre mensajes |
| La marca de tiempo es ignorada | Usa UTC ISO 8601: os.date('!%Y-%m-%dT%H:%M:%SZ') |
| URL filtrada | Elimina el webhook en Discord, crea uno nuevo, y mantenlo en un convar set |
| Demasiados mensajes | Registra solo dinero, acciones de administrador y uniones |
Respuestas rápidas
¿Puedo enviar un webhook de Discord desde el cliente?
No. Cualquiera puede leer el código del cliente y robar la URL del webhook, luego enviar spam a tu canal. Activa el registro en el servidor y envíalo desde allí.
¿Cuántos mensajes de webhook puedo enviar?
Discord limita los webhooks, y aproximadamente 30 mensajes por minuto por webhook es un techo seguro. Más allá de eso obtienes una respuesta 429, así que encola tus mensajes y envíalos a un ritmo constante.
¿Dónde debo guardar la URL del webhook?
En un convar del servidor establecido con set en server.cfg, leído con GetConvar. Nunca lo pongas en un archivo del cliente o en un convar establecido con setr o sets.
Scripts que evitan este problema
Advanced BoostingBoosting de vehículos desde una tablet: contratos de clase D a S+, crews y cola en vivo.Ver script →
Crypto MiningCompra una nave, monta rigs pieza a pieza y mina monedas en un mercado vivo.Ver script →
Shop CreatorCrea una tienda en menos de un minuto — dueños, empleados, caja fuerte y atracos.Ver script →