169 lines
3.9 KiB
JavaScript
169 lines
3.9 KiB
JavaScript
require("dotenv").config()
|
|
const express = require("express")
|
|
const codes = require("./codes.json");
|
|
const app = express()
|
|
const port = process.env.SERVER_PORT || 3000;
|
|
app.use(express.json());
|
|
app.set("trust proxy", 1);
|
|
|
|
app.use((req, res, next) => {
|
|
console.log(`[${new Date().toLocaleString()}] ${req.ip} ${req.method} ${req.url}`);
|
|
next();
|
|
})
|
|
|
|
var rateLimits = {};
|
|
|
|
|
|
app.get("/", (req, res) => {
|
|
// load index
|
|
res.sendFile(__dirname + "/static/index.html")
|
|
});
|
|
|
|
|
|
app.post("/", async (req, res) => {
|
|
// Impliment a rate limit of 25 requests per minute
|
|
const ip = req.ip;
|
|
console.log(rateLimits[ip])
|
|
if (!rateLimits[ip]) {
|
|
rateLimits[ip] = {
|
|
count: 0,
|
|
lastRequest: Date.now()
|
|
};
|
|
}
|
|
const limit = rateLimits[ip];
|
|
const now = Date.now();
|
|
if (now - limit.lastRequest > 60000) {
|
|
limit.count = 0;
|
|
limit.lastRequest = now;
|
|
}
|
|
if (limit.count >= 25) {
|
|
return res.status(429).json({
|
|
code: 429,
|
|
error: "Rate limit exceeded",
|
|
status: "error"
|
|
});
|
|
}
|
|
limit.count++;
|
|
|
|
// Validate request body
|
|
if (!req.body.type || req.body.type !== "batching") {
|
|
return res.status(400).json({
|
|
error: "Invalid request type",
|
|
status: "error"
|
|
});
|
|
}
|
|
// Check that the body is valid json, if not throw an error
|
|
|
|
|
|
|
|
if (!req.body.data || !Array.isArray(req.body.data)) {
|
|
return res.status(400).json({
|
|
error: "Invalid data format",
|
|
status: "error"
|
|
});
|
|
}
|
|
|
|
// Build the batch request body
|
|
const batchRequests = req.body.data.map(item => {
|
|
const assetId = item;
|
|
|
|
if (!assetId) {
|
|
return {
|
|
assetId,
|
|
requestId: assetId,
|
|
status: "failure",
|
|
url: "",
|
|
additional: "Missing assetId."
|
|
};
|
|
}
|
|
|
|
return {
|
|
assetId,
|
|
requestId: assetId
|
|
};
|
|
});
|
|
|
|
// If body.cookie is provided, use it, else use process.env.COOKIE
|
|
const cookie = req.body.cookie || process.env.COOKIE;
|
|
|
|
const options = {
|
|
method: 'POST',
|
|
headers: {
|
|
authority: 'assetdelivery.roblox.com',
|
|
accept: '',
|
|
'accept-language': 'en-US,en;q=0.9',
|
|
'cache-control': 'no-cache',
|
|
'content-type': 'application/json',
|
|
origin: 'https://create.roblox.com',
|
|
pragma: 'no-cache',
|
|
referer: 'https://create.roblox.com/',
|
|
'roblox-browser-asset-request': 'true',
|
|
'roblox-place-id': '0', // Use a default or placeholder value if needed
|
|
'sec-ch-ua': '"Opera GX";v="105", "Chromium";v="119", "Not?A_Brand";v="24"',
|
|
'sec-ch-ua-mobile': '?0',
|
|
'sec-ch-ua-platform': '"Windows"',
|
|
'sec-fetch-dest': 'empty',
|
|
'sec-fetch-mode': 'cors',
|
|
'sec-fetch-site': 'same-site',
|
|
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:130.0) Gecko/20100101 Firefox/130.0',
|
|
Cookie: `.ROBLOSECURITY=${cookie};`
|
|
},
|
|
body: JSON.stringify(batchRequests)
|
|
};
|
|
|
|
try {
|
|
let response = await fetch("https://assetdelivery.roblox.com/v1/assets/batch", options);
|
|
let json = await response.json();
|
|
//console.log(JSON.stringify(json, null, 2));
|
|
// Build the response object
|
|
const responses = req.body.data.reduce((acc, item, index) => {
|
|
const assetId = item;
|
|
if (json[index].errors) {
|
|
const errorCode = json[index].errors[0].code;
|
|
acc[assetId] = {
|
|
status: "failure",
|
|
code: errorCode,
|
|
message: codes[errorCode].message,
|
|
additional: codes[errorCode].description
|
|
};
|
|
} else if (json[index].assetTypeId !== 3) {
|
|
// Return 415, the asset isnt audio
|
|
acc[assetId] = {
|
|
status: "failure",
|
|
code: 415,
|
|
message: codes[415].message,
|
|
additional: codes[415].description
|
|
};
|
|
} else {
|
|
acc[assetId] = {
|
|
status: "success",
|
|
url: json[index].location
|
|
};
|
|
}
|
|
return acc;
|
|
}, {});
|
|
|
|
res.json({
|
|
type: "batching-response",
|
|
data: responses
|
|
});
|
|
|
|
} catch (error) {
|
|
res.status(500).json({
|
|
type: "batching-response",
|
|
data: req.body.data.reduce((acc, item) => {
|
|
acc[item] = {
|
|
status: "failure",
|
|
url: "",
|
|
additional: "Request failed"
|
|
};
|
|
return acc;
|
|
}, {})
|
|
});
|
|
}
|
|
});
|
|
|
|
|
|
app.listen(port, () => {
|
|
console.log(`Listening on ${port}`);
|
|
}); |