Логи Discord webhook с FiveM сервера: руководство PerformHttpRequest

Отправляйте логи в Discord с FiveM с помощью PerformHttpRequest и JSON embeds. Только серверная часть, URL в convar, очередь для лимитов и что логировать.

Вы хотите знать, кто что купил, кто дал деньги, кто запустил команду администратора и кто присоединился, без просмотра консоли весь день. Discord webhook делает это: ваш сервер публикует сообщение на канале. Это руководство показывает серверную часть Lua с PerformHttpRequest, как строить embeds, как держать URL безопасным и как избежать лимита.

Создайте webhook

В Discord откройте параметры канала, затем Integrations, затем Webhooks, создайте один и скопируйте его URL. Это выглядит как https://discord.com/api/webhooks/<id>/<token>. Любой с этим URL может опубликовать в вашем канале, поэтому обращайтесь с ним как с паролем.

Держите URL в convar

Поместите это в server.cfg:

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

Используйте set, не setr или sets. Replicated convar отправляется каждому клиенту, что вызовет утечку URL. Читайте это на сервере:

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

Если convar пуст, пропустите отправку вместо ошибки. Вы можете держать один convar на тип лога (деньги, админ, присоединения), чтобы отправлять их в разные каналы.

Отправьте сообщение

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

Моменты для знания:

  • color — это десятичное число, не шестнадцатеричная строка. 3447003 — это синий и 15158332 — это красный. Конвертируйте hex с tonumber('FF0000', 16).
  • timestamp должен быть датой ISO 8601 в UTC. ! в формате заставляет os.date использовать UTC.
  • fields — это список { name = '...', value = '...', inline = true }. Значение поля не может быть пустым.
  • Успешный вызов webhook возвращает статус 204, с пустым телом.
  • Discord ограничивает размеры. Названия короткие (256 символов), описания длинные, но ограничены (4096), и сообщение содержит ограниченное количество полей и embeds. Обрежьте длинный текст перед отправкой.

Пример вызова, для игрока, который отправил деньги:

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

Только на сервере, никогда из клиента

Внимание: не вызывайте PerformHttpRequest для webhook в клиентском скрипте и не отправляйте URL клиенту. Файлы клиента могут быть прочитаны каждым игроком, поэтому URL утекает и любой может затопить вашей канал.

Если событие на клиенте должно быть залогировано, отправьте событие на сервер, и пусть сервер решает, что логировать из доверенных данных. Клиент, который может вызвать «залогировать этот текст», может также вызвать поддельные логи. Для безопасности событий в целом читайте secure events in FiveM. Логируйте то, что сделал сервер, не то, что говорит клиент.

Лимиты: используйте очередь

Discord отклоняет вызовы webhook, которые приходят слишком быстро, с ответом 429. Всплеск логи, такие как 50 игроков, запускающих то же событие, может его поразить. Отправляйте сообщения из очереди в устойчивом темпе:

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)

Ожидание две секунды между сообщениями держит под лимитом. Если вам нужно больше, объедините несколько embeds в одно сообщение: один запрос может нести до 10 embeds в его списке embeds. Для использования Wait см. Wait explained.

Что логировать

Логируйте события, которые вы хотели бы, когда что-то идёт не так:

  • Деньги: большие передачи, покупки, всё, что создаёт деньги. Это где проявляется злоупотребление в первую очередь.
  • Действия администратора: кто использовал какую команду администратора на ком, запреты, предупреждения, телепорты. См. ban and warn players.
  • Присоединения и отключения, с идентификаторами игрока, что помогает с диспутами и запретами.
  • Спавнинг предметов и использование оружия, если вы это позволяете.

Не логируйте всё. Канал с сообщением каждую секунду — это канал, который никто не читает. Выберите несколько каналов, положите важные там, где смотрит сотрудник, и держите остальное коротким. Держите личные данные на то, что вам нужно, и не логируйте приватный чат.

Совет: положите функцию логирования в маленький ресурс и вызовите её с экспортом из других скриптов, поэтому URL webhook и очередь лимита живут в одном месте. См. exports in FiveM.

Контрольный список

Проблема Решение
Ничего не приходит Проверьте имя convar, что URL полный и что вызов работает на сервере
Статус 400 JSON неправильный: пустое значение поля, слишком длинный текст, или цвет hex строка
Статус 429 Отправляйте через очередь с паузой между сообщениями
Временная метка игнорируется Используйте UTC ISO 8601: os.date('!%Y-%m-%dT%H:%M:%SZ')
URL утёкший Удалите webhook в Discord, создайте новый и держите его в convar set
Слишком много сообщений Логируйте только деньги, действия администратора и присоединения

Короткие ответы

Могу ли я отправить Discord webhook из клиента?

Нет. Любой может прочитать клиентский код и украсть URL webhook, затем спамить вашей канал. Вызовите лог на сервере и отправьте его оттуда.

Сколько сообщений webhook я могу отправить?

Discord ограничивает webhook, и примерно 30 сообщений в минуту на webhook — это безопасный потолок. Дальше вы получаете ответ 429, поэтому поставьте ваши сообщения в очередь и отправляйте их в устойчивом темпе.

Где мне держать URL webhook?

В convar сервера, установленном с set в server.cfg, читаемый с GetConvar. Никогда не кладите его в файл клиента или в convar, установленный с setr или sets.

Скрипты без этой проблемы

Advanced BoostingУгон машин на заказ через планшет: контракты от класса D до S+, команды и живая очередь.Смотреть скрипт →Crypto MiningКупите склад, соберите фермы по деталям и майните монеты на живом рынке.Смотреть скрипт →Shop CreatorМагазин меньше чем за минуту — владельцы, сотрудники, сейфы и ограбления в комплекте.Смотреть скрипт →

Читайте также