Discord whitelist para FiveM: permitir jogadores por role Discord
Construa uma whitelist de role Discord para seu servidor FiveM: playerConnecting com deferrals, o identificador Discord, uma verificação de role de bot através da API REST, e um token seguro.
Você quer que apenas jogadores com um certo role Discord entrem: membros, apoiadores, whitelisted. FiveM não tem verificação Discord integrada, então você adiciona uma em playerConnecting: leia o identificador Discord do jogador, pergunte ao Discord por seus roles através de um bot, e aceite ou rejeite a conexão.
Você precisa de três coisas: um bot Discord no seu servidor, um script no servidor FiveM, e um lugar seguro para o token.
Como funciona
- Um jogador se conecta. FiveM dispara
playerConnectinge lhe dá seus identificadores. - Você pausa a conexão com deferrals (veja playerConnecting and deferrals).
- Seu servidor pergunta à API REST do Discord: este usuário está na guild, e quais roles ele tem?
- Você chama
deferrals.done()para deixá-lo entrar, oudeferrals.done('reason')para rejeitar.
Crie o bot
- Abra o portal de desenvolvedor do Discord e crie uma aplicação, depois adicione um Bot a ela.
- Copie o token do bot. Trate-o como uma senha.
- Nenhum intent privilegiado é necessário para procurar um membro por ID. (O Server Members Intent apenas importa se um bot lista cada membro ou ouve eventos de membro.)
- Convide o bot para seu servidor Discord. Não precisa direitos de admin, apenas tem que ser um membro.
- No Discord, ative o Developer Mode, depois copie seu server (guild) ID e o role ID do role de whitelist.
Mantenha o token fora do código
Defina como uma convar em server.cfg, ou em um arquivo cfg que você exec e nunca compartilha:
set discord_bot_token "paste-token-here"
set discord_guild_id "123456789012345678"
set discord_whitelist_role "123456789012345679"Use set, não setr. Uma convar setr é replicada para cada cliente, o que vazaria o token para os jogadores.
Leia-os no script:
local TOKEN = GetConvar('discord_bot_token', '')
local GUILD = GetConvar('discord_guild_id', '')
local ROLE = GetConvar('discord_whitelist_role', '')Atenção: se o token nunca acabar em uma screenshot, uma mensagem de suporte ou um repositório público, resete-o no portal de desenvolvedor imediatamente.
Leia o identificador Discord
Os identificadores de um jogador se parecem com discord:123456789012345678. Faça loop sobre eles e remova o prefixo:
local function getDiscordId(src)
for _, id in ipairs(GetPlayerIdentifiers(src)) do
if id:sub(1, 8) == 'discord:' then
return id:sub(9)
end
end
endPergunte ao Discord pelos roles
O endpoint é GET https://discord.com/api/v10/guilds/{guild.id}/members/{user.id}, autorizado com Authorization: Bot <token>. A resposta contém um array roles de IDs de role.
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 }
)
endUm 404 significa que o usuário não é membro do seu servidor Discord. Qualquer outro status, como 429 ou 5xx, é um problema no caminho, não um "não".
O handler de conexão
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)Salve source em um local primeiro: após Wait, o global source não aponta mais para este jogador.
Rate limits e caching
Discord limita requests de API por rota e globalmente. Em um restart, muitos jogadores se reconectam de uma vez e cada um dispara um request. Duas proteções simples:
- Cache resultados por alguns minutos por ID Discord, então um jogador que tenta novamente não chama a API novamente.
- Lide com
429. Discord responde com um headerRetry-After. Mostre uma mensagem "try again" e não faça loop nela.
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)
endUse cachedCheck no handler no lugar de hasWhitelistRole. Um "sim" em cache significa um role removido funciona até cinco minutos mais, o que é bem para uma whitelist.
Outros pontos
- Não falhe aberto. Se Discord estiver inativo, rejeite com uma mensagem clara em vez de deixar todos entrar, a menos que você tenha decidido que é aceitável.
- Bypass de staff. Se você quer que admins entrem sem o role, verifique seus identificadores contra uma lista antes de chamar Discord.
- Jogadores sem Discord obtêm nenhum identificador. Sua mensagem acima os diz o que fazer.
- Para bloquear um trapaceiro para sempre, bani-o em vez de remover um role. Veja moderating with txAdmin.
Checklist
| Sintoma | Solução |
|---|---|
Sem identificador discord: |
O app Discord do jogador deve estar rodando e conectado |
401 do Discord |
Token de bot errado ou resetado, ou faltando prefixo Bot |
403 do Discord |
O bot não está naquele servidor, ou o token pertence a outro bot |
404 do Discord |
O usuário não está no seu servidor Discord |
429 do Discord |
Rate limit: cache resultados e mostre uma mensagem de retry |
| Token visível para jogadores | Use set, não setr, e mantenha fora de arquivos compartilhados |
| Jogador preso em "Checking" | Cada caminho de código deve terminar com deferrals.done |
Respostas rápidas
O jogador precisa ter Discord aberto para entrar?
Sim. FiveM apenas envia o identificador discord: quando o aplicativo desktop Discord está rodando e conectado no PC do jogador. Sem ele, o identificador está faltando e seu script deve mostrar uma mensagem clara.
Onde coloco o token do bot?
Em uma convar definida em server.cfg ou um arquivo cfg separado, leia com GetConvar. Nunca coloque em um arquivo Lua que é compartilhado, uploadado ou armazenado em um repositório.
Chamo a API Discord a cada conexão?
Você pode, mas cache o resultado por alguns minutos. Discord limita requests e um restart com muitos jogadores se reconectando pode atingir o limite.

