Logs de webhook Discord depuis un serveur FiveM : guide PerformHttpRequest

Envoyez des logs à Discord depuis FiveM avec PerformHttpRequest et des embeds JSON. Côté serveur seulement, URL dans une convar, une queue pour les limites de débit, et quoi logger.

Vous voulez savoir qui a acheté quoi, qui a donné de l'argent, qui a exécuté une commande admin et qui a rejoint, sans regarder la console toute la journée. Un webhook Discord le fait : votre serveur poste un message à un canal. Ce guide montre le Lua côté serveur avec PerformHttpRequest, comment construire des embeds, comment garder l'URL sûre et comment éviter la limite de débit.

Créer le webhook

Dans Discord, ouvrez les paramètres du canal, puis Intégrations, puis Webhooks, créez un et copiez son URL. Elle ressemble à https://discord.com/api/webhooks/<id>/<token>. N'importe qui avec cette URL peut poster dans votre canal, donc traitez-la comme un mot de passe.

Garder l'URL dans une convar

Mettez-la dans server.cfg :

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

Utilisez set, pas setr ou sets. Une convar répliquée est envoyée à chaque client, ce qui fuirait l'URL. Lisez-la sur le serveur :

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

Si la convar est vide, sautez l'envoi au lieu de faire une erreur. Vous pouvez garder une convar par type de log (argent, admin, joins) pour les envoyer à différents canaux.

Envoyer un message

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

Points à savoir :

  • color est un nombre décimal, pas une chaîne hex. 3447003 est bleu et 15158332 est rouge. Convertissez hex avec tonumber('FF0000', 16).
  • timestamp doit être une date ISO 8601 en UTC. Le ! dans le format fait que os.date utilise UTC.
  • fields est une liste de { name = '...', value = '...', inline = true }. Une valeur de champ ne peut pas être vide.
  • Un appel webhook réussi retourne le statut 204, avec un corps vide.
  • Discord limite les tailles. Les titres sont courts (256 caractères), les descriptions sont longues mais plafonnées (4096), et un message contient un nombre limité de champs et d'embeds. Coupez le texte long avant de l'envoyer.

Un exemple d'appel, pour un joueur qui a envoyé de l'argent :

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

Côté serveur seulement, jamais depuis le client

Attention : n'appelez pas PerformHttpRequest pour un webhook dans un script client, et n'envoyez pas l'URL au client. Les fichiers clients peuvent être lus par chaque joueur, donc l'URL fuit et n'importe qui peut inonder votre canal.

Si un événement sur le client doit être loggé, envoyez un événement au serveur, et laissez le serveur décider quoi logger à partir de données en lesquelles il fait confiance. Un client qui peut déclencher « loggez ce texte » peut aussi déclencher de faux logs. Pour la sécurité des événements en général, lisez événements sécurisés dans FiveM. Loggez ce que le serveur a fait, pas ce que le client dit qu'il a fait.

Limites de débit : utilisez une queue

Discord rejette les appels webhook qui arrivent trop vite, avec une réponse 429. Une rafale de logs, comme 50 joueurs déclenchant le même événement, peut le faire. Envoyez les messages d'une queue à un rythme régulier :

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)

Attendre deux secondes entre les messages reste sous la limite. Si vous en avez besoin de plus, groupez plusieurs embeds en un message : une requête peut contenir jusqu'à 10 embeds dans sa liste embeds. Pour l'utilisation de Wait, voir Wait expliqué.

Quoi logger

Loggez les événements que vous voudriez quand quelque chose va mal :

  • Argent : grands transferts, achats, n'importe quoi qui crée de l'argent. C'est où l'abus se montre en premier.
  • Actions administrateur : qui a utilisé quelle commande admin sur qui, bans, avertissements, téléports. Voir bans et avertissements de joueurs.
  • Joins et leaves, avec les identifiants du joueur, ce qui aide aux différends et aux bans.
  • Génération d'objets et utilisation d'armes, si vous les autorisez.

Ne loggez pas tout. Un canal avec un message chaque seconde est un canal que personne ne lit. Choisissez quelques canaux, mettez les importants où le staff regarde, et gardez le reste court. Gardez les données personnelles à ce dont vous avez besoin, et ne loggez pas le chat privé.

Conseil : mettez votre fonction de logging dans une petite ressource et appelez-la avec un export depuis vos autres scripts, pour que l'URL du webhook et la queue de limite de débit vivent au même endroit. Voir exports dans FiveM.

Checklist

Problème Solution
Rien n'arrive Vérifiez le nom de la convar, que l'URL est complète et que l'appel s'exécute sur le serveur
Statut 400 Le JSON est invalide : valeur de champ vide, texte trop long, ou couleur chaîne hex
Statut 429 Envoyez par une queue avec une pause entre les messages
Timestamp ignoré Utilisez UTC ISO 8601 : os.date('!%Y-%m-%dT%H:%M:%SZ')
URL fuie Supprimez le webhook dans Discord, créez-en un nouveau, et gardez-le dans une convar set
Trop de messages Loggez seulement l'argent, les actions administrateur et les joins

Réponses rapides

Puis-je envoyer un webhook Discord depuis le client ?

Non. N'importe qui peut lire le code client et voler l'URL du webhook, puis spammer votre canal. Déclenchez le log sur le serveur et envoyez-le de là.

Combien de messages webhook puis-je envoyer ?

Discord limite les webhooks, et à peu près 30 messages par minute par webhook est un plafond sûr. Au-delà de cela, vous obtenez une réponse 429, donc mettez vos messages en queue et envoyez-les à un rythme régulier.

Où dois-je garder l'URL du webhook ?

Dans une convar de serveur définie avec set dans server.cfg, lue avec GetConvar. Ne la mettez jamais dans un fichier client ou dans une convar définie avec setr ou sets.

Des scripts sans ce problème

Advanced BoostingDu boosting de véhicules piloté par tablette : contrats de classe D à S+, crews et file d’attente en direct.Voir le script →Crypto MiningAchetez un entrepôt, montez vos rigs pièce par pièce et minez sur un marché qui bouge.Voir le script →Shop CreatorCréez un magasin en moins d’une minute — propriétaires, employés, coffres et braquages inclus.Voir le script →

À lire aussi