Whitelist Discord per FiveM: consenti i giocatori per ruolo Discord

Costruisci una whitelist di ruoli Discord per il tuo server FiveM: playerConnecting con deferrals, l'identificatore Discord, un controllo dei ruoli bot tramite REST API, e un token sicuro.

Vuoi che solo i giocatori con un certo ruolo Discord si possano unire: membri, supporter, whitelisted. FiveM non ha un controllo Discord integrato, quindi ne aggiungi uno in playerConnecting: leggi l'identificatore Discord del giocatore, chiedi a Discord i loro ruoli attraverso un bot, e accetta o rifiuta la connessione.

Hai bisogno di tre cose: un bot Discord nel tuo server, uno script nel server FiveM, e un luogo sicuro per il token.

Come funziona

  1. Un giocatore si connette. FiveM attiva playerConnecting e ti dà i loro identificatori.
  2. Metti in pausa la connessione con deferrals (vedi playerConnecting and deferrals).
  3. Il tuo server chiede all'API REST Discord: questo utente è nella guild, e quali ruoli ha?
  4. Chiama deferrals.done() per farlo entrare, o deferrals.done('reason') per rifiutare.

Crea il bot

  1. Apri il portale degli sviluppatori Discord e crea un'applicazione, quindi aggiungi un Bot ad essa.
  2. Copia il token del bot. Trattalo come una password.
  3. Nessun intent privilegiato è necessario per cercare un membro per ID. (Il Server Members Intent conta solo se un bot elenca ogni membro o ascolta gli eventi dei membri.)
  4. Invita il bot al tuo server Discord. Non ha bisogno di diritti admin, deve solo essere un membro.
  5. Su Discord, attiva la Modalità sviluppatore, quindi copia l'ID del server (guild) e l'ID del ruolo del ruolo della whitelist.

Mantieni il token fuori dal codice

Impostalo come convar in server.cfg, o in un file cfg che exec e mai condividi:

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

Usa set, non setr. Una convar setr viene replicata ad ogni client, il che farebbe perdere il token ai giocatori.

Leggili nello script:

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

Attenzione: se il token finisce mai in uno screenshot, un messaggio di supporto o un repository pubblico, reimpostalo nel portale degli sviluppatori subito.

Leggi l'identificatore Discord

Gli identificatori di un giocatore somigliano a discord:123456789012345678. Scorri su di essi e rimuovi il prefisso:

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

Chiedi a Discord i ruoli

L'endpoint è GET https://discord.com/api/v10/guilds/{guild.id}/members/{user.id}, autorizzato con Authorization: Bot <token>. La risposta contiene un array roles di ID ruolo.

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

Un 404 significa che l'utente non è un membro del tuo server Discord. Qualsiasi altro stato, come 429 o 5xx, è un problema sulla strada, non un "no".

L'handler di connessione

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)

Salva source in una variabile locale per primo: dopo Wait, il source globale non punta più a questo giocatore.

Limiti di frequenza e caching

Discord limita le richieste API per rotta e globalmente. Su un riavvio, molti giocatori si riconnettono contemporaneamente e ognuno attiva una richiesta. Due protezioni semplici:

  • Cache i risultati per pochi minuti per ogni ID Discord, così un giocatore che riprova non chiama l'API di nuovo.
  • Gestisci 429. Discord risponde con un header Retry-After. Mostra un messaggio "riprova" e non fare un loop su di esso.
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

Usa cachedCheck nell'handler al posto di hasWhitelistRole. Un "sì" cache significa che un ruolo rimosso funziona fino a cinque minuti più a lungo, il che va bene per una whitelist.

Altri punti

  • Non fallire aperto. Se Discord è giù, rifiuta con un messaggio chiaro invece di lasciare entrare tutti, a meno che tu non abbia deciso che è accettabile.
  • Bypass dello staff. Se vuoi che gli admin entrino senza il ruolo, controlla i loro identificatori rispetto a una lista prima di chiamare Discord.
  • Giocatori senza Discord non ottengono identificatore. Il tuo messaggio sopra dice loro cosa fare.
  • Per bloccare un cheater una volta per tutte, bannalo invece di rimuovere un ruolo. Vedi moderating with txAdmin.

Checklist

Sintomo Soluzione
Nessun identificatore discord: L'app Discord del giocatore deve essere in esecuzione e loggata
401 da Discord Token del bot errato o ripristinato, o mancante prefisso Bot
403 da Discord Il bot non è in quel server, o il token appartiene a un altro bot
404 da Discord L'utente non è nel tuo server Discord
429 da Discord Limite di frequenza: cache i risultati e mostra un messaggio di retry
Token visibile ai giocatori Usa set, non setr, e mantienilo fuori dai file condivisi
Giocatore bloccato su "Checking" Ogni percorso di codice deve terminare con deferrals.done

Risposte rapide

Il giocatore ha bisogno che Discord sia aperto per entrare?

Sì. FiveM invia solo l'identificatore discord: quando l'app Discord desktop è in esecuzione e loggata sul PC del giocatore. Senza di essa, l'identificatore è mancante e il tuo script dovrebbe mostrare un messaggio chiaro.

Dove metto il token del bot?

In una convar impostata in server.cfg o in un file cfg separato, letta con GetConvar. Non metterla mai in un file Lua che è condiviso, caricato o memorizzato in un repository.

Chiamo l'API Discord ad ogni connessione?

Puoi, ma cache il risultato per pochi minuti. Discord applica limiti di frequenza alle richieste e un riavvio con molti giocatori che si riconnettono può colpire il limite.

Script senza questo problema

Tebex TemplateUn tema premium senza codice per il tuo negozio Tebex, modificabile interamente dal pannello Tebex.Vedi script →Mic PhoneUn telefono pieghevole che si apre in un tablet e arriva sul telefono vero del giocatore.Vedi script →

Continua a leggere