Lista blanca de Discord para FiveM: permite jugadores por rol de Discord
Construye una lista blanca de rol de Discord para tu servidor FiveM: playerConnecting con deferrals, el identificador de Discord, una comprobación de rol de bot a través de la API REST, y un token seguro.
Quieres que solo los jugadores con un cierto rol de Discord se unan: miembros, simpatizantes, autorizados. FiveM no tiene una comprobación de Discord incorporada, así que añades una en playerConnecting: lee el identificador de Discord del jugador, pregunta a Discord por sus roles a través de un bot, y acepta o rechaza la conexión.
Necesitas tres cosas: un bot de Discord en tu servidor, un script en el servidor FiveM, y un lugar seguro para el token.
Cómo funciona
- Un jugador se conecta. FiveM activa
playerConnectingy te da sus identificadores. - Pausas la conexión con deferrals (consulta playerConnecting y deferrals).
- Tu servidor pregunta a la API REST de Discord: ¿este usuario está en el gremio, y qué roles tiene?
- Llamas a
deferrals.done()para dejarlos entrar, odeferrals.done('reason')para rechazar.
Crea el bot
- Abre el portal de desarrolladores de Discord y crea una aplicación, luego añade un Bot a ella.
- Copia el token del bot. Trátalo como una contraseña.
- No se necesita ningún intent privilegiado para buscar un miembro por ID. (El Server Members Intent solo importa si un bot lista a cada miembro o escucha eventos de miembros.)
- Invita el bot a tu servidor de Discord. No necesita derechos de administrador, solo tiene que ser un miembro.
- En Discord, activa Modo de Desarrollador, luego copia tu id de servidor (gremio) y la id de rol del rol de lista blanca.
Mantén el token fuera del código
Establécelo como un convar en server.cfg, o en un archivo cfg que exec y nunca compartas:
set discord_bot_token "paste-token-here"
set discord_guild_id "123456789012345678"
set discord_whitelist_role "123456789012345679"Usa set, no setr. Un convar setr es replicado a cada cliente, que filtraría el token a los jugadores.
Léelos en el script:
local TOKEN = GetConvar('discord_bot_token', '')
local GUILD = GetConvar('discord_guild_id', '')
local ROLE = GetConvar('discord_whitelist_role', '')Cuidado: si el token alguna vez termina en una captura de pantalla, un mensaje de soporte o un repositorio público, reinícialo en el portal de desarrolladores inmediatamente.
Lee el identificador de Discord
Los identificadores de un jugador se parecen a discord:123456789012345678. Itera sobre ellos y quita el prefijo:
local function getDiscordId(src)
for _, id in ipairs(GetPlayerIdentifiers(src)) do
if id:sub(1, 8) == 'discord:' then
return id:sub(9)
end
end
endPregunta a Discord por los roles
El endpoint es GET https://discord.com/api/v10/guilds/{guild.id}/members/{user.id}, autorizado con Authorization: Bot <token>. La respuesta contiene un array roles de ids de roles.
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 }
)
endUn 404 significa que el usuario no es miembro de tu servidor de Discord. Cualquier otro estado, como 429 o 5xx, es un problema en el camino, no un «no».
El manejador de conexión
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)Guarda source en una variable local primero: después de Wait, el source global ya no apunta a este jugador.
Límites de velocidad y cacheo
Discord limita las solicitudes de API por ruta y globalmente. En un reinicio, muchos jugadores se reconectan a la vez y cada uno activa una solicitud. Dos protecciones simples:
- Cachea resultados durante unos minutos por ID de Discord, así que un jugador que reintenta no llama a la API nuevamente.
- Maneja
429. Discord responde con un headerRetry-After. Muestra un mensaje «intenta de nuevo» y no hagas un bucle en él.
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)
endUsa cachedCheck en el manejador en lugar de hasWhitelistRole. Un «sí» cacheado significa que un rol eliminado funciona hasta cinco minutos más, lo que está bien para una lista blanca.
Otros puntos
- No falles abierto. Si Discord está caído, rechaza con un mensaje claro en lugar de dejar a todos entrar, a menos que hayas decidido que es aceptable.
- Bypass de personal. Si quieres que los administradores se unan sin el rol, comprueba sus identificadores contra una lista antes de llamar a Discord.
- Jugadores sin Discord no obtienen ningún identificador. Tu mensaje anterior les dice qué hacer.
- Para bloquear a un tramposo para siempre, banéalo en lugar de eliminar un rol. Consulta moderación con txAdmin.
Lista de verificación
| Síntoma | Solución |
|---|---|
Sin identificador discord: |
La aplicación de Discord del jugador debe estar ejecutándose e iniciada sesión |
401 de Discord |
Token de bot incorrecto o reiniciado, o falta el prefijo Bot |
403 de Discord |
El bot no está en ese servidor, o el token pertenece a otro bot |
404 de Discord |
El usuario no está en tu servidor de Discord |
429 de Discord |
Límite de velocidad: cachea resultados y muestra un mensaje de reintento |
| Token visible para los jugadores | Usa set, no setr, y mantenlo fuera de archivos compartidos |
| Jugador atrapado en «Checking» | Cada ruta de código debe terminar con deferrals.done |
Respuestas rápidas
¿Necesita el jugador tener Discord abierto para unirse?
Sí. FiveM solo envía el identificador discord: cuando la aplicación de Discord está ejecutándose e iniciada sesión en la PC del jugador. Sin él, el identificador falta y tu script debe mostrar un mensaje claro.
¿Dónde pongo el token del bot?
En un convar establecido en server.cfg o un archivo cfg separado, leído con GetConvar. Nunca lo pongas en un archivo Lua que se comparta, se cargue o se almacene en un repositorio.
¿Llamo a la API de Discord en cada conexión?
Puedes, pero cachea el resultado durante unos minutos. Discord limita las solicitudes y un reinicio con muchos jugadores reconectándose puede golpear el límite.

