-- ## CONFIG ## -- local SERVER_BASE_URL:string = "http://localhost:7000/" local REQUESTS_PER_MINUTE:number = 500 -- roblox 500 requests per minute limit local ALLOW_CLIENT:boolean = true; -- ## MODULE ## -- local exposed = {} local isServer = game:GetService("RunService"):IsServer() shared.BatchHttpModule = shared.BatchHttpModule or {} shared.BatchHttpModule.queue = shared.BatchHttpModule.queue or {} shared.BatchHttpModule.signal = shared.BatchHttpModule.signal or Instance.new("BindableEvent") shared.BatchHttpModule.lastSent = shared.BatchHttpModule.lastSent or 0; shared.BatchHttpModule.waitingForResponse = shared.BatchHttpModule.waitingForResponse or {} shared.BatchHttpModule.clientRemote = shared.BatchHttpModule.clientRemote or (function() local funcName = "_BatchHTTP" local funcLocation = workspace if (isServer) then local remoteFunction = Instance.new("RemoteFunction") remoteFunction.Name = funcName remoteFunction.Parent = funcLocation remoteFunction.OnServerInvoke = function(plr,options) if not ALLOW_CLIENT then return end return exposed:RequestAsync(options) end return remoteFunction else return funcLocation:WaitForChild(funcName) end end)() local signal:BindableEvent = shared.BatchHttpModule.signal local THREAD_INTERVAL:number = (60/REQUESTS_PER_MINUTE) local RealHttpService = game:GetService("HttpService") local InstanceSecret = (RealHttpService:GenerateGUID(false).."--"..game.JobId) -- not only using jobid so that random people cant just spam the api to get a response function ProcessQueue() --print("processing queue") local requests = {} local waiting = shared.BatchHttpModule.waitingForResponse for _,queueObj in shared.BatchHttpModule.queue do table.insert(requests, queueObj) table.insert(shared.BatchHttpModule.waitingForResponse,queueObj.id) end local body = {} if (#requests ~= 0) then body.requests = requests end if (#waiting ~= 0) then body.waiting = waiting end if not (body.waiting or body.requests) then return end shared.BatchHttpModule.lastSent = tick() local jsonBody = RealHttpService:JSONEncode(body) shared.BatchHttpModule.queue = {} local response = RealHttpService:PostAsync(SERVER_BASE_URL..InstanceSecret, jsonBody, Enum.HttpContentType.ApplicationJson, true, { ["X-BatchHttp-Api-Key"] = "silly!" -- i know this exposes the key to client but its just temporary (permanent) }) local data = RealHttpService:JSONDecode(response) signal:Fire(data.responses or {}) end function AddToQueue(queueObj) table.insert(shared.BatchHttpModule.queue, queueObj) end function DoRequest(request) local id = RealHttpService:GenerateGUID(false); print("[BatchHTTP] sending request",id) AddToQueue({ id = id, request = request, }) local response = nil; while not response do local receivedData = signal.Event:Wait() for _,obj in receivedData do if obj.id == id then response = obj local tableIndex = table.find(shared.BatchHttpModule.waitingForResponse,id) if tableIndex then table.remove(shared.BatchHttpModule.waitingForResponse,tableIndex) end end end end if response.error then warn('[BatchHTTP] request '..id..' errored "'..response.error.message..'"') else print("[BatchHTTP] got response "+response.status,id) end return response.data end shared.BatchHttpModule.thread = shared.BatchHttpModule.thread or coroutine.wrap(function() while true do if (tick() - shared.BatchHttpModule.lastSent) > THREAD_INTERVAL then task.spawn(ProcessQueue) end task.wait() end end)() exposed.HttpEnabled = RealHttpService.HttpEnabled exposed.GenerateGUID = RealHttpService.GenerateGUID exposed.GetSecret = RealHttpService.GetSecret exposed.JSONDecode = RealHttpService.JSONDecode exposed.JSONEncode = RealHttpService.JSONEncode exposed.UrlEncode = RealHttpService.UrlEncode function exposed:RequestAsync(options) if (isServer) then return DoRequest(options) else return shared.BatchHttpModule.clientRemote:InvokeServer(options) end end function exposed:PostAsync(url, body, contentType, compress, headers) return exposed:RequestAsync({ Method = "post"; Url = url; Body = body; Headers = headers; }) end function exposed:GetAsync(url) return exposed:RequestAsync({ Method = "get"; Url = url; }) end return exposed;