Log webhook Discord da un server FiveM: guida PerformHttpRequest

Invia log a Discord da FiveM con PerformHttpRequest e JSON embed. Solo lato server, URL in una convar, una coda per i limiti di frequenza, e cosa registrare.

Vuoi sapere chi ha comprato cosa, chi ha dato soldi, chi ha eseguito un comando admin e chi è entrato, senza guardare la console tutto il giorno. Un webhook Discord lo fa: il tuo server pubblica un messaggio in un canale. Questa guida mostra il Lua lato server con PerformHttpRequest, come costruire embed, come mantenere l'URL al sicuro e come evitare il limite di frequenza.

Crea il webhook

Su Discord, apri le impostazioni del canale, poi Integrazioni, poi Webhook, creane uno e copia il suo URL. Somiglia a https://discord.com/api/webhooks/<id>/<token>. Chiunque abbia quell'URL può postare nel tuo canale, quindi trattalo come una password.

Mantieni l'URL in una convar

Mettilo in server.cfg:

cfg
set discord_webhook_logs "https://discord.com/api/webhooks/ID/TOKEN"

Usa set, non setr o sets. Una convar replicata viene inviata ad ogni client, il che farebbe perdere l'URL. Leggila sul server:

lua
local WEBHOOK = GetConvar('discord_webhook_logs', '')

Se la convar è vuota, salta l'invio invece di errare. Puoi mantenere una convar per tipo di log (soldi, admin, ingressi) per inviarli a canali diversi.

Invia un messaggio

lua
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',
    })
end

Punti da conoscere:

  • color è un numero decimale, non una stringa hex. 3447003 è blu e 15158332 è rosso. Converti hex con tonumber('FF0000', 16).
  • timestamp deve essere una data ISO 8601 in UTC. Il ! nel formato fa usare a os.date l'UTC.
  • fields è una lista di { name = '...', value = '...', inline = true }. Un valore del campo non può essere vuoto.
  • Una chiamata webhook di successo restituisce lo stato 204, con un corpo vuoto.
  • Discord limita le dimensioni. I titoli sono brevi (256 caratteri), le descrizioni sono lunghe ma limitate (4096), e un messaggio contiene un numero limitato di campi ed embed. Taglia il testo lungo prima di inviarlo.

Un'esempio di chiamata, per un giocatore che ha inviato soldi:

lua
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 lato server, mai dal client

Attenzione: non chiamare PerformHttpRequest per un webhook in uno script client, e non inviare l'URL al client. I file client possono essere letti da ogni giocatore, quindi l'URL si perde e chiunque può inondare il tuo canale.

Se un evento nel client dovrebbe essere registrato, invia un evento al server, e lascia che il server decida cosa registrare dai dati di cui si fida. Un client che può attivare "registra questo testo" può anche attivare log falsi. Per la sicurezza generale degli eventi, leggi secure events in FiveM. Registra quello che il server ha fatto, non quello che il client dice di aver fatto.

Limiti di frequenza: usa una coda

Discord rifiuta le chiamate webhook che arrivano troppo velocemente, con una risposta 429. Un'ondata di log, come 50 giocatori che attivano lo stesso evento, può colpirla. Invia i messaggi da una coda a un ritmo costante:

lua
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)

Aspettando due secondi tra i messaggi rimani sotto il limite. Se ne hai bisogno di più, raggruppa diversi embed in un messaggio: una richiesta può portare fino a 10 embed nella sua lista embeds. Per l'uso di Wait, vedi Wait explained.

Cosa registrare

Registra gli eventi che vorresti quando qualcosa va storto:

  • Soldi: trasferimenti grandi, acquisti, qualsiasi cosa che crei soldi. Questo è dove l'abuso si mostra prima.
  • Azioni admin: chi ha usato quale comando admin su chi, ban, avvertimenti, teleport. Vedi ban and warn players.
  • Ingressi e uscite, con gli identificatori del giocatore, che aiuta con le dispute e i ban.
  • Spawning di articoli e uso di armi, se lo consenti.

Non registrare tutto. Un canale con un messaggio ogni secondo è un canale che nessuno legge. Scegli pochi canali, metti i più importanti dove guarda lo staff, e mantieni il resto breve. Mantieni i dati personali a quelli di cui hai bisogno, e non registrare la chat privata.

Consiglio: metti la tua funzione di logging in una piccola risorsa e chiamala con un export dai tuoi altri script, così l'URL del webhook e la coda del limite di frequenza vivono in un unico posto. Vedi exports in FiveM.

Checklist

Sintomo Soluzione
Nulla arriva Controlla il nome della convar, che l'URL sia completo e che la chiamata venga eseguita sul server
Status 400 Il JSON non è valido: valore di campo vuoto, testo troppo lungo, o una stringa colore hex
Status 429 Invia attraverso una coda con una pausa tra i messaggi
Timestamp ignorato Usa UTC ISO 8601: os.date('!%Y-%m-%dT%H:%M:%SZ')
URL perso Elimina il webhook in Discord, creane uno nuovo, e mantienilo in una convar set
Troppi messaggi Registra solo soldi, azioni admin e ingressi

Risposte rapide

Posso inviare un webhook Discord dal client?

No. Chiunque può leggere il codice client e rubare l'URL del webhook, quindi spammando il tuo canale. Attiva il log sul server e invialo da lì.

Quanti messaggi webhook posso inviare?

Discord applica limiti di frequenza ai webhook, e approssimativamente 30 messaggi al minuto per webhook è un limite di sicurezza. Oltre questo ottieni una risposta 429, quindi metti in coda i tuoi messaggi e invialli a un ritmo costante.

Dove dovrei tenere l'URL del webhook?

In una convar del server impostata con set in server.cfg, letta con GetConvar. Non metterla mai in un file client o in una convar impostata con setr o sets.

Script senza questo problema

Advanced BoostingBoosting di veicoli dal tablet: contratti dalla classe D alla S+, crew e coda in tempo reale.Vedi script →Crypto MiningCompra un magazzino, costruisci rig pezzo per pezzo e mina monete su un mercato che si muove.Vedi script →Shop CreatorCrea un negozio in meno di un minuto — proprietari, dipendenti, casseforti e rapine inclusi.Vedi script →

Continua a leggere