UNPKG

cfx-resolve

Version:

Resolve CFX token to host informations for FiveM

161 lines (153 loc) 7.12 kB
"use strict"; let STATIC = { CFX: "https://cfx.re/join/", IP_API: "http://ip-api.com/json/", CORS_API: "http://cors-anywhere.herokuapp.com/", STREAM_API: "https://servers-frontend.fivem.net/api/servers/single/" } const isBrowser = () => typeof window !== "undefined"; if(isBrowser()) { STATIC.CFX = STATIC.CORS_API+STATIC.CFX; STATIC.STREAM_API = STATIC.CORS_API+STATIC.STREAM_API; } function escapeRegExp(string) { return string.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&'); } function replaceAll(str, find, replace) { return str.replace(new RegExp(escapeRegExp(find), 'g'), replace); } async function req(url, post = false) { let options = {}; if(post) { options = { mode: "cors", headers: { "Access-Control-Allow-Origin": "*", "origin": url, "Content-Type": "application/x-www-form-urlencoded" }, method: "POST", body: "method=getEndpoints" }; } else { options = { mode: "cors", headers: { "Access-Control-Allow-Origin": "*", "origin": url } }; } let response; if(typeof fetch != "function") { const fetch = require("node-fetch"); response = await fetch(url, options) } else { response = await fetch(url, options) } return response; } async function resolve(token, options) { const data = {}; if(!token) throw new Error("Specify an CFX token"); if(token.includes("join/")) { token = token.split("join/")[1]; } if(options && typeof options != "object") throw new Error("Options type should be object"); if(typeof token == "string") { const request = await req(STATIC.CFX+token); if(request.status != 200) throw new Error("Connection timed out"); if(!request) throw new Error("An error occurred while handle request"); let host = request.headers.get("x-citizenfx-url"); if(!host) throw new Error("Host not found"); if(host.includes("users.cfx.re")) { let fetchHost = await req(host+"client", true); if(fetchHost.status != 200) throw new Error("Connection timed out"); fetchHost = await fetchHost.json(); if(fetchHost.error) throw new Error("An error occurred while resolve host"); if(fetchHost.length <= 0) throw new Error("An error occurred while resolve host"); host = "http://"+fetchHost[0]+"/"; } data.host = host.match(/(\d{1,3}\.){3}\d{1,3}:\d{1,5}/g)[0]; data.ip = host.match(/(\d{1,3}\.){3}\d{1,3}:\d{1,5}/g)[0].split(":")[0]; data.port = host.match(/(\d{1,3}\.){3}\d{1,3}:\d{1,5}/g)[0].split(":")[1]; if(!data.ip || !data.port) throw new Error("An error occurred while parse data"); data.port = Number(data.port); data.links = { "info": host+"info.json", "players": host+"players.json", "dynamic": host+"dynamic.json" } if(options && options.geo) { let geo = await req(STATIC.IP_API+data.ip); if(geo.status != 200) throw new Error("Connection timed out"); geo = await geo.json(); if(geo.status == "success") { data.geo = { "country": geo.country, "countryCode": geo.countryCode, "region": geo.regionName, "city": geo.city, "timezone": geo.timezone, "isp": geo.isp, "org": geo.org, "as": geo.as, } } else { data.geo = "An error occurred while fetch geo"; } } if(options && options.info) { let info = await req("http://"+data.ip+":"+data.port+"/info.json"); if(info.status != 200) throw new Error("Connection timed out"); info = await info.json(); let dynamic = await req("http://"+data.ip+":"+data.port+"/dynamic.json"); if(dynamic.status != 200) throw new Error("Connection timed out"); dynamic = await dynamic.json(); let stream = await req(STATIC.STREAM_API+token); if(stream.status != 200) throw new Error("Connection timed out"); stream = await stream.json(); for (let i = 0; i <= 9; i++) { dynamic.hostname = replaceAll(dynamic.hostname, "^"+i, "") } data.info = { "hostname": (dynamic.hostname ? dynamic.hostname : "Not Provided"), "players": (dynamic.clients ? Number(dynamic.clients) : 0), "slot": (dynamic.sv_maxclients ? Number(dynamic.sv_maxclients) : 0), "online": (dynamic.clients ? Number(dynamic.clients) : 0) + "/" + (dynamic.sv_maxclients ? Number(dynamic.sv_maxclients) : 0), "boost": (stream.Data ? (stream.Data.upvotePower ? Number(stream.Data.upvotePower) : 0) : 0), "private": (stream.Data ? (stream.Data.private ? stream.Data.private : false) : false), "owner": (stream.Data ? (stream.Data.ownerName ? { "username": (stream.Data ? (stream.Data.ownerName ? stream.Data.ownerName : "Not Provided") : "Not Provided"), "profile": (stream.Data ? (stream.Data.ownerProfile ? stream.Data.ownerProfile : "Not Provided") : "Not Provided"), "avatar": (stream.Data ? (stream.Data.ownerAvatar ? stream.Data.ownerAvatar : "Not Provided") : "Not Provided"), "lastSeen": (stream.Data ? (stream.Data.lastSeen ? stream.Data.lastSeen : "Not Provided") : "Not Provided"), } : "Not Provided") : "Not Provided"), "map": (dynamic.mapname && dynamic.mapname.length >= 2 ? dynamic.mapname : "Not Provided"), "type": (dynamic.gametype ? dynamic.gametype : "Not Provided"), "server": (info.server ? info.server : "Not Provided"), "version": info.version, "icon": info.icon, "enhancedHostSupport": info.enhancedHostSupport, "vars": info.vars, "resources": info.resources, } } if(options && options.players) { let players = await req("http://"+data.ip+":"+data.port+"/players.json"); if(players.status != 200) throw new Error("Connection timed out"); players = await players.json(); if(typeof players == "object") { data.players = players; } else { data.players = "An error occurred while fetch players"; } } return data; } else { throw new Error("Invalid CFX token"); } } if(!isBrowser()) { module.exports.resolve = resolve; }