Discord whitelist dla FiveM: pozwól graczom po roli Discord

Zbuduj whitelist roli Discord dla serwera FiveM: playerConnecting z deferrals, identyfikator Discord, sprawdzenie roli bota przez REST API i bezpieczny token.

Chcesz, aby przyłączyć się tylko gracze z określoną rolą Discord: członkowie, zwolennicy, whitelisted. FiveM nie ma wbudowanego sprawdzenia Discord, więc dodajesz jedno w playerConnecting: przeczytaj identyfikator Discord gracza, poproś Discord o ich role przez bota, i zaakceptuj lub odrzuć połączenie.

Potrzebujesz trzech rzeczy: bot Discord na twoim serwerze, skrypt na serwerze FiveM i bezpieczne miejsce na token.

Jak to działa

  1. Gracz się przyłącza. FiveM wyzwala playerConnecting i daje ci ich identyfikatory.
  2. Pausujesz połączenie z deferrals (zobacz playerConnecting and deferrals).
  3. Twój serwer pyta Discord REST API: czy ten użytkownik jest w gildii, i jakie role ma?
  4. Wołasz deferrals.done(), aby ich wpuścić, lub deferrals.done('reason'), aby odrzucić.

Utwórz bota

  1. Otwórz portal deweloperów Discord i utwórz aplikację, potem dodaj Bota do niej.
  2. Skopiuj token bota. Traktuj to jak hasło.
  3. Brak uprzywilejowanego zamysłu jest potrzebny, aby wyszukać jednego członka po ID. (Server Members Intent liczy się tylko jeśli bot wymieniła każdego członka lub słucha zdarzeń członków.)
  4. Zaproś bota na serwer Discord. Nie potrzebuje uprawnień administratora, musi tylko być członkiem.
  5. W Discord, włącz Tryb Dewelopera, potem skopiuj serwer (guild) ID i role ID roli whitelist.

Trzymaj token poza kodem

Ustaw go jako convar w server.cfg, lub w pliku cfg, który exec i nigdy nie dzielisz:

cfg
set discord_bot_token "paste-token-here"
set discord_guild_id "123456789012345678"
set discord_whitelist_role "123456789012345679"

Użyj set, nie setr. Convar setr jest replikowany do każdego klienta, co przecieknęłoby token graczom.

Przeczytaj je w skrypcie:

lua
local TOKEN = GetConvar('discord_bot_token', '')
local GUILD = GetConvar('discord_guild_id', '')
local ROLE  = GetConvar('discord_whitelist_role', '')

Ostrzeżenie: jeśli token kiedykolwiek skończy się na zrzucie ekranu, wiadomości pomocy lub publicznym repozytorium, zresetuj go w portalu deweloperów zaraz.

Przeczytaj identyfikator Discord

Identyfikatory gracza wyglądają jak discord:123456789012345678. Pętla nad nimi i usuń przedrostek:

lua
local function getDiscordId(src)
    for _, id in ipairs(GetPlayerIdentifiers(src)) do
        if id:sub(1, 8) == 'discord:' then
            return id:sub(9)
        end
    end
end

Poproś Discord o role

Punkt końcowy to GET https://discord.com/api/v10/guilds/{guild.id}/members/{user.id}, autoryzowany z Authorization: Bot <token>. Odpowiedź zawiera tablicę roles ID roli.

lua
local function hasWhitelistRole(discordId, cb)
    PerformHttpRequest(
        ('https://discord.com/api/v10/guilds/%s/members/%s'):format(GUILD, discordId),
        function(status, body)
            if status == 200 then
                local member = json.decode(body)
                for _, role in ipairs(member.roles or {}) do
                    if role == ROLE then return cb(true) end
                end
                return cb(false)
            elseif status == 404 then
                return cb(false, 'not_in_guild')
            end
            cb(nil, status) -- rate limit or Discord error
        end,
        'GET', '',
        { ['Authorization'] = 'Bot ' .. TOKEN }
    )
end

404 oznacza, że użytkownik nie jest członkiem twojego serwera Discord. Każdy inny status, takie jak 429 lub 5xx, to problem w drodze, nie "nie".

Handler połączenia

lua
AddEventHandler('playerConnecting', function(name, setKickReason, deferrals)
    local src = source
    deferrals.defer()
    Wait(0)

    deferrals.update('Checking your Discord role...')

    local discordId = getDiscordId(src)
    if not discordId then
        return deferrals.done('Open the Discord app on your PC, then reconnect.')
    end

    hasWhitelistRole(discordId, function(ok, info)
        if ok then
            deferrals.done()
        elseif ok == false and info == 'not_in_guild' then
            deferrals.done('Join our Discord server first.')
        elseif ok == false then
            deferrals.done('You do not have the whitelist role.')
        else
            deferrals.done('Could not reach Discord (' .. tostring(info) .. '). Try again in a moment.')
        end
    end)
end)

Zapisz source w lokalnym najpierw: po Wait, globalny source już nie wskazuje tego gracza.

Limity tempa i cachowanie

Discord limituje żądania API na trasę i globalnie. Przy restarcie, wielu graczy przyłącza się naraz i każdy wyzwala żądanie. Dwie proste ochrony:

  • Cachuj wyniki przez kilka minut na Discord ID, więc gracz, który ponawia, nie wołuje API ponownie.
  • Obsłużyć 429. Discord odpowiada nagłówkiem Retry-After. Pokaż wiadomość "spróbuj ponownie" i nie pętli na nim.
lua
local cache = {}
local TTL = 5 * 60 * 1000

local function cachedCheck(discordId, cb)
    local hit = cache[discordId]
    if hit and GetGameTimer() - hit.at < TTL then
        return cb(hit.ok)
    end
    hasWhitelistRole(discordId, function(ok, info)
        if ok ~= nil then cache[discordId] = { ok = ok, at = GetGameTimer() } end
        cb(ok, info)
    end)
end

Użyj cachedCheck w handleru zamiast hasWhitelistRole. Cachowany "tak" oznacza, że usunięta rola działa do pięciu minut dłużej, co jest ok dla whitelist.

Inne punkty

  • Nie nie otwieraj. Jeśli Discord jest wyłączony, odrzuć z jasną wiadomością zamiast wpuszczać wszystkich, chyba że zdecydowałeś, że to jest ok.
  • Obejście personelu. Jeśli chcesz, aby administratorzy przyłączyli się bez roli, sprawdzić ich identyfikatory wobec listy przed wołaniem Discord.
  • Gracze bez Discord nie otrzymają identyfikatora. Twoja wiadomość powyżej mówi im, co robić.
  • Aby zablokować oszusta na zawsze, zamiast usuwania roli ich zabanuj. Zobacz moderating with txAdmin.

Lista kontrolna

Symptom Naprawa
Brak identyfikatora discord: Discord app gracza musi być uruchomiona i zalogowana
401 z Discord Złyły lub zresetowany token bota, lub brakujący przedrostek Bot
403 z Discord Bot nie jest na tym serwerze, lub token należy do innego bota
404 z Discord Użytkownik nie jest na twoim serwerze Discord
429 z Discord Limit tempa: cachuj wyniki i pokaż wiadomość ponowienia
Token widoczny dla graczy Użyj set, nie setr, i trzymaj go poza plikami wspólnymi
Gracz utknął na "Checking" Każda ścieżka kodu musi kończyć się deferrals.done

Szybkie odpowiedzi

Czy gracz potrzebuje Discord otwartego, aby przyłączyć się?

Tak. FiveM wysyła identyfikator discord: tylko, gdy aplikacja Discord jest uruchomiona i zalogowana na PC gracza. Bez tego, identyfikator brakuje i twój skrypt powinien pokazać jasną wiadomość.

Gdzie włóż token bota?

W convar ustawiony w server.cfg lub osobnym pliku cfg, przeczytany z GetConvar. Nigdy nie włóż go w plik Lua, który jest wspólny, załadowany lub przechowywany w repozytorium.

Czy wołam Discord API przy każdym połączeniu?

Możesz, ale cachuj wynik przez kilka minut. Discord limituje żądania i restart z wieloma graczami przyłączającymi się może trafić limit.

Skrypty bez tego problemu

Tebex TemplateMotyw premium dla twojego sklepu Tebex, bez kodu — edytowany w całości z panelu Tebex.Zobacz skrypt →Mic PhoneSkładany telefon, który rozkłada się w tablet i działa też na prawdziwym telefonie gracza.Zobacz skrypt →

Czytaj dalej