Discord-Webhook-Protokolle von einem FiveM-Server: PerformHttpRequest-Leitfaden

Sende Protokolle zu Discord von FiveM mit PerformHttpRequest und JSON-Embeds. Nur Server-Seite, URL in einer Convar, eine Warteschlange für Raten-Limits und was zu protokollieren ist.

Du möchtest wissen, wer was gekauft hat, wer Geld gegeben hat, wer einen Admin-Befehl ausgeführt hat und wer beigetreten ist, ohne den ganzen Tag die Konsole zu beobachten. Ein Discord-Webhook macht das: Dein Server postet eine Nachricht in einen Kanal. Dieser Leitfaden zeigt die Server-Seite Lua mit PerformHttpRequest, wie man Embeds erstellt, wie man die URL sicher hält und wie man das Raten-Limit vermeidet.

Erstelle den Webhook

In Discord öffne die Kanal-Einstellungen, dann Integrationen, dann Webhooks, erstelle einen und kopiere seine URL. Sie sieht aus wie https://discord.com/api/webhooks/<id>/<token>. Jeder mit dieser URL kann in deinem Kanal posten, behandle sie daher wie ein Passwort.

Halte die URL in einer Convar

Lege sie in server.cfg:

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

Verwende set, nicht setr oder sets. Eine replizierende Convar wird an jeden Client gesendet, was die URL lecken würde. Lese sie auf dem Server:

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

Falls die Convar leer ist, überspringe das Senden anstatt zu fehlern. Du kannst eine Convar pro Art von Protokoll halten (Geld, Admin, Beitritte) um sie zu verschiedenen Kanälen zu senden.

Sende eine Nachricht

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

Punkte zum Wissen:

  • color ist eine Dezimalzahl, keine Hex-String. 3447003 ist blau und 15158332 ist rot. Konvertiere Hex mit tonumber('FF0000', 16).
  • timestamp muss ein ISO 8601-Datum in UTC sein. Das ! in dem Format lässt os.date UTC verwenden.
  • fields ist eine Liste von { name = '...', value = '...', inline = true }. Ein Feldwert kann nicht leer sein.
  • Ein erfolgreicher Webhook-Aufruf gibt den Status 204 mit einem leeren Body zurück.
  • Discord begrenzt Größen. Titel sind kurz (256 Zeichen), Beschreibungen sind lang aber begrenzt (4096), und eine Nachricht hat eine begrenzte Anzahl von Feldern und Embeds. Schneide lange Texte ab, bevor du sie sendest.

Ein Beispielaufruf für einen Spieler, der Geld gesendet hat:

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 },
})

Nur Server-Seite, nie vom Client

Achtung: rufe PerformHttpRequest nicht für einen Webhook in einem Client-Skript auf und sende die URL nicht an den Client. Client-Dateien können von jedem Spieler gelesen werden, daher leckt die URL und jeder kann deinen Kanal mit Spam füllen.

Falls ein Event auf dem Client protokolliert werden sollte, sende ein Event zum Server und lass den Server entscheiden, was aus Daten zu protokollieren ist, denen es traut. Ein Client, der „protokolliere diesen Text" triggern kann, kann auch gefälschte Protokolle triggern. Für Event-Sicherheit allgemein lese sichere Events in FiveM. Protokolliere, was der Server tat, nicht was der Client sagt, es tat.

Raten-Limits: Verwende eine Warteschlange

Discord lehnt Webhook-Aufrufe ab, die zu schnell ankommen, mit einer 429-Antwort. Ein Ansturm von Protokollen, wie 50 Spieler, die das gleiche Event triggern, kann es treffen. Sende die Nachrichten aus einer Warteschlange in einem konstanten Tempo:

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)

Warten zwei Sekunden zwischen Nachrichten bleibt unter dem Limit. Falls du mehr brauchst, gruppe mehrere Embeds in eine Nachricht: eine Anforderung kann bis zu 10 Embeds in ihrer embeds-Liste tragen. Für die Verwendung von Wait, siehe Wait erklärt.

Was zu protokollieren ist

Protokolliere die Events, die du mögen würdest, wenn etwas schiefgeht:

  • Geld: große Transfers, Käufe, alles, das Geld schafft. Hier zeigt sich Missbrauch zuerst.
  • Admin-Aktionen: wer welchen Admin-Befehl auf wen verwendete, Verbote, Verwarnungen, Teleports. Siehe Spieler verbieten und verwarnen.
  • Beitritte und Abgänge, mit den Kennungen des Spielers, was bei Streitigkeiten und Verboten hilft.
  • Gegenstands-Spawning und Waffengebrauch, falls du sie erlaubst.

Protokolliere nicht alles. Ein Kanal mit einer Nachricht pro Sekunde ist ein Kanal, den niemand liest. Wähle ein paar Kanäle, lege die wichtigen dahin, wo Personal schaut, und halte den Rest kurz. Halte persönliche Daten zu dem, was du brauchst, und protokolliere keinen privaten Chat.

Tipp: lege deine Protokollierungsfunktion in eine kleine Ressource und rufe sie mit einem Export von deinen anderen Skripten auf, so dass die Webhook-URL und die Raten-Limit-Warteschlange an einem Ort leben. Siehe Exports in FiveM.

Checkliste

Symptom Behebung
Nichts kommt an Überprüfe den Convar-Namen, dass die URL vollständig ist und dass der Aufruf auf dem Server läuft
Status 400 Der JSON ist ungültig: leerer Feldwert, zu langer Text oder eine Hex-Farb-String
Status 429 Sende über eine Warteschlange mit einer Pause zwischen Nachrichten
Zeitstempel ignoriert Verwende UTC ISO 8601: os.date('!%Y-%m-%dT%H:%M:%SZ')
URL geleckt Lösche den Webhook in Discord, erstelle einen neuen und halte ihn in einer set-Convar
Zu viele Nachrichten Protokolliere nur Geld, Admin-Aktionen und Beitritte

Kurze Antworten

Kann ich einen Discord-Webhook vom Client senden?

Nein. Jeder kann Client-Code lesen und die Webhook-URL stehlen, dann deinen Kanal mit Spam füllen. Triggere das Protokoll auf dem Server und sende es von dort.

Wie viele Webhook-Nachrichten kann ich senden?

Discord begrenzt Webhooks und ungefähr 30 Nachrichten pro Minute pro Webhook ist eine sichere Obergrenze. Darüber hinaus bekommst du eine 429-Antwort, daher stelle deine Nachrichten in die Warteschlange und sende sie in einem konstanten Tempo.

Wo sollte ich die Webhook-URL behalten?

In einer Server-Convar, die mit set in server.cfg gesetzt wird, gelesen mit GetConvar. Lege sie nie in eine Client-Datei oder eine Convar, die mit setr oder sets gesetzt wird.

Scripts ohne dieses Problem

Advanced BoostingFahrzeug-Boosting per Tablet: Aufträge von Klasse D bis S+, Crews und eine Live-Warteschlange.Script ansehen →Crypto MiningKauf eine Lagerhalle, bau Rigs Teil für Teil und mine Coins auf einem Markt, der sich bewegt.Script ansehen →Shop CreatorBau einen Shop in unter einer Minute — Besitzer, Angestellte, Tresore und Überfälle inklusive.Script ansehen →

Weiterlesen