Files
OpenParty/core/route/ubiservices.js
ibratabian17 f6540675c9 btoa -> atob
2024-12-21 22:29:14 +07:00

232 lines
8.4 KiB
JavaScript

const axios = require('axios');
const { v4: uuidv4 } = require('uuid');
const core = {
main: require('../var').main,
CloneObject: require('../helper').CloneObject,
generateCarousel: require('../carousel/carousel').generateCarousel,
generateSweatCarousel: require('../carousel/carousel').generateSweatCarousel,
generateCoopCarousel: require('../carousel/carousel').generateCoopCarousel,
updateMostPlayed: require('../carousel/carousel').updateMostPlayed,
signer: require('../lib/signUrl'),
ipResolver: require('../lib/ipResolver'),
};
const { addUserId, updateUserTicket, getUserData, updateUser } = require('./account');
const settings = require('../../settings.json');
const cachedTicket = {};
const ipCache = {}; // Cache untuk menyimpan ticket berdasarkan IP
const prodwsurl = "https://public-ubiservices.ubi.com/";
// Replace placeholders in the object
const replaceDomainPlaceholder = (obj, domain) => {
if (typeof obj === 'string') {
return obj.replace('{SettingServerDomainVarOJDP}', domain);
} else if (Array.isArray(obj)) {
return obj.map(item => replaceDomainPlaceholder(item, domain));
} else if (obj && typeof obj === 'object') {
return Object.keys(obj).reduce((acc, key) => {
acc[key] = replaceDomainPlaceholder(obj[key], domain);
return acc;
}, {});
}
return obj;
};
// Get client IP from request
const getClientIp = (req) => core.ipResolver.getClientIp(req);
// Placeholder function for getting the country based on IP
const getCountryFromIp = (ip) => 'US';
// Generate a fake ticket for fallback
const generateFalseTicket = () => {
const start = "ew0KIC";
const end = "KfQ==";
const middleLength = 60;
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let middle = '';
for (let i = 0; i < middleLength; i++) {
middle += chars.charAt(Math.floor(Math.random() * chars.length));
}
return start + middle + end;
};
const atob = (base64) => Buffer.from(base64, 'base64').toString('utf-8');
const parseCustomAuthHeader = (authorization) => {
const [method, encoded] = authorization.split(" ");
if (method !== "uplaypc_v1" || !encoded.includes("t=")) {
return null;
}
// Ambil bagian setelah "t=" pertama saja
const encodedPart = encoded.substring(encoded.indexOf("t=") + 2);
// Decode Base64 setelah "t="
const decoded = atob(encodedPart);
const [tag, encodedProfileId, profileId, username, email, encodedPassword] = decoded.split(":");
if (tag !== "JDParty") return null;
return {
profileId,
username,
email,
password: atob(encodedPassword),
};
};
// Initialize routes
exports.initroute = (app, express, server) => {
// Serve application configuration
app.get('/:version/applications/:appId/configuration', (req, res) => {
res.send(core.main.configuration);
});
// Serve alternative application configuration
app.get('/:version/applications/:appId', (req, res) => {
res.send(core.main.configurationnx);
});
// Handle session creation
app.post("/v3/profiles/sessions", async (req, res) => {
const clientIp = getClientIp(req);
const clientIpCountry = getCountryFromIp(clientIp);
// Helper to generate session data
const generateSessionData = (profileId, username, clientIp, clientIpCountry, ticket) => {
const now = new Date();
const expiration = new Date(now.getTime() + 3 * 60 * 60 * 1000); // 3 hours
const data = {
platformType: "uplay",
ticket,
twoFactorAuthenticationTicket: null,
profileId,
userId: profileId,
nameOnPlatform: username,
environment: "Prod",
expiration: expiration.toISOString(),
spaceId: uuidv4(),
clientIp,
clientIpCountry,
serverTime: now.toISOString(),
sessionId: uuidv4(),
sessionKey: "TqCz5+J0w9e8qpLp/PLr9BCfAc30hKlEJbN0Xr+mbZa=",
rememberMeTicket: null,
};
return data;
};
// Remove Host header
const headers = { ...req.headers };
delete headers.host;
const customAuthData = parseCustomAuthHeader(headers.authorization);
if (customAuthData) {
console.log("[ACC] CustomAuth detected, verifying...");
const { profileId, username, email, password } = customAuthData;
const userData = getUserData(profileId);
const ticket = `CustomAuth${headers.authorization.split(" t=")[1]}`
if (userData && atob(userData.password) === password) {
console.log("[ACC] CustomAuth login: ", atob(username));
updateUser(profileId, { username: atob(username), email, password, userId: profileId, ticket: `Ubi_v1 ${ticket}` });
const sessionData = generateSessionData(profileId, username, clientIp, clientIpCountry, ticket)
res.send(sessionData);
return;
} else if (userData && atob(userData.password) !== password) {
console.log("[ACC] CustomAuth login password failed: ", atob(username));
}
if (!userData) {
console.log("[ACC] CustomAuth register: ", atob(username));
updateUser(profileId, { username: atob(username), email, password, userId: profileId, ticket: `Ubi_v1 ${ticket}` });
res.send(generateSessionData(profileId, atob(username), clientIp, clientIpCountry, ticket));
return;
}
}
try {
console.log("[ACC] Fetching Ticket From Official Server");
const response = await axios.post(`${prodwsurl}/v3/profiles/sessions`, req.body, { headers });
res.send(response.data);
console.log("[ACC] Using Official Ticket");
// Update user mappings
addUserId(response.data.profileId, response.data.userId);
updateUserTicket(response.data.profileId, `Ubi_v1 ${response.data.ticket}`);
} catch (error) {
console.log("[ACC] Error fetching from Ubisoft services", error.message);
if (ipCache[clientIp]) {
console.log(`[ACC] Returning cached session for IP ${clientIp}`);
return res.send(ipCache[clientIp]);
}
const profileId = uuidv4();
const userTicket = generateFalseTicket();
cachedTicket[userTicket] = profileId;
console.log("[ACC] Generating Fake Session for", profileId);
const fakeSession = generateSessionData(profileId, "NintendoSwitch", clientIp, clientIpCountry, userTicket);
ipCache[clientIp] = fakeSession;
res.send(fakeSession);
}
});
// Handle session deletion
app.delete("/v3/profiles/sessions", (req, res) => {
res.send();
});
// Retrieve profiles based on query parameters
app.get("/v3/profiles", (req, res) => {
const profileId = `${cachedTicket[req.header('Authorization')] || 'UnknownTicket'}/userId/${req.query.idOnPlatform}`;
res.send({
profiles: [{
profileId,
userId: profileId,
platformType: "uplay",
idOnPlatform: profileId,
nameOnPlatform: "Ryujinx"
}]
});
});
// Serve population data
app.get("/v1/profiles/me/populations", (req, res) => {
const spaceId = req.query.spaceIds || uuidv4();
res.send({
spaceId,
data: {
US_SDK_APPLICATION_BUILD_ID: "202007232022",
US_SDK_DURABLES: []
}
});
});
// Serve application parameters for JD22
app.get("/v1/applications/34ad0f04-b141-4793-bdd4-985a9175e70d/parameters", (req, res) => {
res.send(replaceDomainPlaceholder(require("../../database/v1/parameters.json"), settings.server.domain));
});
// Serve application parameters for JD18
app.get("/v1/spaces/041c03fa-1735-4ea7-b5fc-c16546d092ca/parameters", (req, res) => {
res.send(replaceDomainPlaceholder(require("../../database/v1/parameters2.json"), settings.server.domain));
});
// Handle user-related requests (stubbed for now)
app.post("/v3/users/:user", (req, res) => {
res.send();
});
};