mirror of
https://github.com/ibratabian17/OpenParty.git
synced 2026-01-15 14:22:54 -03:00
Improve Profiles & Leaderboard Function
This commit is contained in:
@@ -1,133 +1,369 @@
|
|||||||
// core/route/account.js
|
|
||||||
//shit implementation, i need to fix it asap
|
|
||||||
const fs = require("fs");
|
const fs = require("fs");
|
||||||
const axios = require("axios");
|
const axios = require("axios");
|
||||||
const path = require('path')
|
const path = require('path');
|
||||||
const { getSavefilePath } = require('../helper');
|
const { getSavefilePath } = require('../helper');
|
||||||
const { encrypt, decrypt } = require('../lib/encryptor')
|
const { encrypt, decrypt } = require('../lib/encryptor');
|
||||||
|
|
||||||
const secretKey = require('../../database/encryption.json').encrpytion.userEncrypt;
|
const secretKey = require('../../database/encryption.json').encrpytion.userEncrypt;
|
||||||
const ubiwsurl = "https://public-ubiservices.ubi.com";
|
const ubiwsurl = "https://public-ubiservices.ubi.com";
|
||||||
const prodwsurl = "https://prod.just-dance.com";
|
const prodwsurl = "https://prod.just-dance.com";
|
||||||
var decryptedData = {}
|
let decryptedData = null;
|
||||||
|
let cachedLeaderboard = null;
|
||||||
|
let cachedDotw = null;
|
||||||
|
|
||||||
|
const LEADERBOARD_PATH = path.join(getSavefilePath(), 'leaderboard/leaderboard.json');
|
||||||
|
const DOTW_PATH = path.join(getSavefilePath(), 'leaderboard/dotw.json');
|
||||||
|
|
||||||
|
// Helper function to load user data
|
||||||
|
function loadUserData(dataFilePath) {
|
||||||
|
if (!decryptedData) { // Load data from disk only if not already in memory
|
||||||
|
try {
|
||||||
|
const encryptedData = fs.readFileSync(dataFilePath, 'utf8');
|
||||||
|
decryptedData = JSON.parse(decrypt(encryptedData, secretKey));
|
||||||
|
} catch (err) {
|
||||||
|
console.log('[ACC] Unable to read user.json');
|
||||||
|
console.log('[ACC] Is the key correct? Are the files corrupted?');
|
||||||
|
console.log('[ACC] Ignore this message if this is the first run');
|
||||||
|
console.log('[ACC] Resetting All User Data...');
|
||||||
|
console.log(err);
|
||||||
|
decryptedData = {}; // Initialize as an empty object if file read fails
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return decryptedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getWeekNumber() {
|
||||||
|
const now = new Date();
|
||||||
|
const startOfWeek = new Date(now.getFullYear(), 0, 1);
|
||||||
|
const daysSinceStartOfWeek = Math.floor((now - startOfWeek) / (24 * 60 * 60 * 1000));
|
||||||
|
return Math.ceil((daysSinceStartOfWeek + 1) / 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to save user data
|
||||||
|
function saveUserData(dataFilePath, data) {
|
||||||
|
const encryptedUserProfiles = encrypt(JSON.stringify(data), secretKey);
|
||||||
|
fs.writeFileSync(dataFilePath, encryptedUserProfiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find user by ticket
|
||||||
|
function findUserFromTicket(ticket) {
|
||||||
|
return Object.values(decryptedData).find(profile => profile.ticket === ticket);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find user by nickname
|
||||||
|
function findUserFromNickname(nickname) {
|
||||||
|
return Object.values(decryptedData).find(profile => profile.name === nickname);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a new user
|
||||||
|
function addUser(profileId, userProfile) {
|
||||||
|
decryptedData[profileId] = userProfile;
|
||||||
|
console.log(`[ACC] Added User With UUID: `, profileId);
|
||||||
|
const dataFilePath = path.join(getSavefilePath(), `/account/profiles/user.json`);
|
||||||
|
saveUserData(dataFilePath, decryptedData);
|
||||||
|
}
|
||||||
|
|
||||||
|
function findUserFromTicket(ticket) {
|
||||||
|
return Object.values(decryptedData).find(profile => profile.ticket === ticket);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to read the leaderboard
|
||||||
|
function readLeaderboard(isDotw = false) {
|
||||||
|
if (!isDotw) {
|
||||||
|
if (!cachedLeaderboard) {
|
||||||
|
if (fs.existsSync(LEADERBOARD_PATH)) {
|
||||||
|
const data = fs.readFileSync(LEADERBOARD_PATH, 'utf-8');
|
||||||
|
cachedLeaderboard = data
|
||||||
|
return JSON.parse(data);
|
||||||
|
} else {
|
||||||
|
return cachedLeaderboard
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {}; // Return empty object if file doesn't exist
|
||||||
|
} else {
|
||||||
|
if (!cachedDotw) {
|
||||||
|
if (fs.existsSync(DOTW_PATH)) {
|
||||||
|
const data = fs.readFileSync(DOTW_PATH, 'utf-8');
|
||||||
|
cachedDotw = data
|
||||||
|
return JSON.parse(data);
|
||||||
|
} else {
|
||||||
|
return cachedDotw
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {}; // Return empty object if file doesn't exist
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to save the leaderboard
|
||||||
|
function saveLeaderboard(leaderboard, isDotw = false) {
|
||||||
|
fs.writeFileSync(isDotw ? DOTW_PATH : LEADERBOARD_PATH, JSON.stringify(leaderboard, null, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize routes
|
||||||
exports.initroute = (app) => {
|
exports.initroute = (app) => {
|
||||||
|
|
||||||
// Endpoint to get profiles based on profileIds
|
// Endpoint to get profiles based on profileIds
|
||||||
app.get("/profile/v2/profiles", (req, res) => {
|
app.get("/profile/v2/profiles", async (req, res) => {
|
||||||
const ticket = req.header("Authorization") || ''; // Extract Authorization header
|
const ticket = req.header("Authorization");
|
||||||
const profilesid = req.query.profileIds.split(','); // Split profileIds into an array
|
const profileIds = req.query.profileIds.split(',');
|
||||||
const dataFilePath = path.join(getSavefilePath(), `/account/profiles/user.json`); // Path to user data file
|
const dataFilePath = path.join(getSavefilePath(), `/account/profiles/user.json`);
|
||||||
try {
|
|
||||||
if (decryptedData == {}) {
|
// Load user data if not already loaded
|
||||||
const encryptedData = fs.readFileSync(dataFilePath, 'utf8'); // Read encrypted user data
|
if (!decryptedData) {
|
||||||
decryptedData = JSON.parse(decrypt(encryptedData, secretKey)); // Parse decrypted user data
|
loadUserData(dataFilePath);
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
decryptedData = {}; // Set empty object if data cannot be parsed
|
|
||||||
console.log('[ACC] Unable to read user.json')
|
|
||||||
console.log('[ACC] Is the key correct? are the files corrupted?')
|
|
||||||
console.log('[ACC] Ignore this message if this first run')
|
|
||||||
console.log('[ACC] Resetting All User Data...')
|
|
||||||
console.log(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map over profileIds to retrieve corresponding user profiles or create default profiles
|
const responseProfiles = await Promise.all(profileIds.map(async (profileId) => {
|
||||||
const responseProfiles = profilesid.map(profileId => {
|
let userProfile = decryptedData[profileId];
|
||||||
const userProfile = decryptedData[profileId]; // Get user profile based on profileId
|
|
||||||
if (userProfile) {
|
// If the profile is found in the local data
|
||||||
return { ...userProfile, ip: req.clientIp, ticket: ticket }; // Add IP to userProfile but not in the response
|
if (userProfile && Object.keys(userProfile).length >= 2) {
|
||||||
|
console.log(`[ACC] Account Found For: `, profileId);
|
||||||
|
return { ...userProfile, ip: req.clientIp, ticket: ticket };
|
||||||
} else {
|
} else {
|
||||||
const defaultProfile = { ip: req.clientIp, ticket: ticket }; // Create a default profile with IP address
|
// If the profile is not found locally, fetch from external source
|
||||||
decryptedData[profileId] = defaultProfile; // Add default profile to decrypted data
|
console.error(`[ACC] Asking Official Server For: `, profileId);
|
||||||
return {}; // Return an empty object (don't include defaultProfile in response)
|
const url = `https://prod.just-dance.com/profile/v2/profiles?profileIds=${encodeURIComponent(profileId)}`;
|
||||||
}
|
try {
|
||||||
});
|
const profileResponse = await axios.get(url, {
|
||||||
|
headers: {
|
||||||
|
'Host': 'prod.just-dance.com',
|
||||||
|
'User-Agent': req.headers['user-agent'],
|
||||||
|
'Accept': req.headers['accept'] || '*/*',
|
||||||
|
'Accept-Language': req.headers['accept-language'] || 'en-us,en',
|
||||||
|
'Authorization': ticket,
|
||||||
|
'X-SkuId': req.headers['x-skuid'],
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const encryptedUserProfiles = encrypt(JSON.stringify(decryptedData), secretKey); // Stringify decrypted data
|
// Assume the external response contains the profile as `profileData`
|
||||||
fs.writeFileSync(dataFilePath, encryptedUserProfiles); // Write updated data to file
|
const profileData = profileResponse.data[profileId]; // Adjust according to the actual response format
|
||||||
res.send(responseProfiles); // Send response containing user profiles
|
if (profileData) {
|
||||||
|
const defaultProfile = { ...profileData, ip: req.clientIp, ticket: ticket };
|
||||||
|
|
||||||
|
// Add the fetched profile to local storage
|
||||||
|
addUser(profileId, defaultProfile);
|
||||||
|
|
||||||
|
return defaultProfile;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`[ACC] Error fetching profile for ${profileId}:`, error.message);
|
||||||
|
addUser(profileId, { ip: req.clientIp, ticket: ticket });
|
||||||
|
return {}; // If fetch fails, return an empty profile object
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
res.send(responseProfiles);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Endpoint to update or create a user profile
|
|
||||||
app.post("/profile/v2/profiles", (req, res) => {
|
app.post("/profile/v2/profiles", (req, res) => {
|
||||||
const ticket = req.header("Authorization"); // Extract Authorization header
|
const ticket = req.header("Authorization");
|
||||||
const content = req.body; // Extract content from request body
|
const content = req.body;
|
||||||
const dataFilePath = path.join(getSavefilePath(), `/account/profiles/user.json`); // Path to user data file
|
content.ticket = ticket;
|
||||||
try {
|
const dataFilePath = path.join(getSavefilePath(), `/account/profiles/user.json`);
|
||||||
if (decryptedData == {}) {
|
|
||||||
const encryptedData = fs.readFileSync(dataFilePath, 'utf8'); // Read encrypted user data
|
// Load user data if not already loaded
|
||||||
decryptedData = JSON.parse(decrypt(encryptedData, secretKey)); // Parse decrypted user data
|
if (!decryptedData) {
|
||||||
}
|
loadUserData(dataFilePath);
|
||||||
} catch (err) {
|
|
||||||
decryptedData = {}; // Set empty object if data cannot be parsed
|
|
||||||
console.log('[ACC] Unable to read user.json')
|
|
||||||
console.log('[ACC] Is the key correct? are the files corrupted?')
|
|
||||||
console.log('[ACC] Ignore this message if this first run')
|
|
||||||
console.log('[ACC] Resetting All User Data...')
|
|
||||||
console.log(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find a matching profile based on name or IP address (only one profile)
|
|
||||||
// Check whether this user is a cracked game user
|
|
||||||
if (content.name === "ALI123") {
|
|
||||||
return res.status(400).send({
|
|
||||||
error: "Cracked user is not allowed to use profiles"
|
|
||||||
}); // Send 400 status with error message
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Find matching profile by name or ticket
|
||||||
const matchedProfileId = Object.keys(decryptedData).find(profileId => {
|
const matchedProfileId = Object.keys(decryptedData).find(profileId => {
|
||||||
const userProfile = decryptedData[profileId]; // Get user profile based on profileId
|
const userProfile = decryptedData[profileId];
|
||||||
return userProfile.name === content.name || userProfile.ticket === ticket || userProfile.ip === req.clientIp; // Check for name or IP match
|
return userProfile.name === content.name || userProfile.ticket === ticket;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (matchedProfileId) {
|
if (matchedProfileId) {
|
||||||
decryptedData[matchedProfileId] = content; // Update existing profile with posted content
|
const userProfile = decryptedData[matchedProfileId];
|
||||||
const encryptedUserProfiles = encrypt(JSON.stringify(decryptedData), secretKey); // Stringify decrypted data
|
let scoreChanged = false;
|
||||||
fs.writeFileSync(dataFilePath, encryptedUserProfiles); // Write updated data to file
|
let updatedScoreMapName = "";
|
||||||
res.send(encryptedUserProfiles); // Send updated encrypted data as response
|
let updatedScore = 0;
|
||||||
|
|
||||||
|
// Merge new content into existing user profile, overriding or adding properties
|
||||||
|
Object.assign(userProfile, content);
|
||||||
|
|
||||||
|
// Implement Leaderboard System for scores
|
||||||
|
if (content.scores) {
|
||||||
|
Object.keys(content.scores).forEach(mapName => {
|
||||||
|
const newScore = content.scores[mapName].highest;
|
||||||
|
const oldScore = (userProfile.scores && userProfile.scores[mapName]?.highest) || 0;
|
||||||
|
|
||||||
|
if (newScore > oldScore) {
|
||||||
|
userProfile.scores[mapName] = { highest: newScore };
|
||||||
|
scoreChanged = true;
|
||||||
|
updatedScoreMapName = mapName;
|
||||||
|
updatedScore = newScore;
|
||||||
|
console.log(`[ACC] New highest score for ${mapName}: ${newScore}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scoreChanged) {
|
||||||
|
let leaderboard = readLeaderboard(false);
|
||||||
|
if (!leaderboard[updatedScoreMapName]) {
|
||||||
|
leaderboard[updatedScoreMapName] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentScores = leaderboard[updatedScoreMapName];
|
||||||
|
const existingEntryIndex = currentScores.findIndex(entry => entry.profileId === matchedProfileId);
|
||||||
|
|
||||||
|
if (existingEntryIndex !== -1) {
|
||||||
|
// Update leaderboard entry if the new score is higher
|
||||||
|
if (currentScores[existingEntryIndex].score < updatedScore) {
|
||||||
|
currentScores[existingEntryIndex].score = updatedScore;
|
||||||
|
console.log(`[ACC] Updated leaderboard for map ${updatedScoreMapName}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Add a new leaderboard entry
|
||||||
|
const newScoreEntry = {
|
||||||
|
__class: "LeaderboardEntry",
|
||||||
|
score: updatedScore,
|
||||||
|
profileId: matchedProfileId,
|
||||||
|
gameVersion: getGameVersion(req),
|
||||||
|
rank: userProfile.rank,
|
||||||
|
name: userProfile.name,
|
||||||
|
avatar: userProfile.avatar,
|
||||||
|
country: userProfile.country,
|
||||||
|
platformId: userProfile.platformId,
|
||||||
|
alias: userProfile.alias,
|
||||||
|
aliasGender: userProfile.aliasGender,
|
||||||
|
jdPoints: userProfile.jdPoints,
|
||||||
|
portraitBorder: userProfile.portraitBorder,
|
||||||
|
weekOptain: getWeekNumber()
|
||||||
|
};
|
||||||
|
|
||||||
|
currentScores.push(newScoreEntry);
|
||||||
|
leaderboard[updatedScoreMapName] = currentScores;
|
||||||
|
saveLeaderboard(leaderboard, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save updated user profile data
|
||||||
|
decryptedData[matchedProfileId] = userProfile;
|
||||||
|
saveUserData(dataFilePath, decryptedData);
|
||||||
|
|
||||||
|
res.send(decryptedData[matchedProfileId]);
|
||||||
} else {
|
} else {
|
||||||
res.status(404).send("Profile not found."); // Send 404 status if profile not found
|
console.error("[ACC] Can't Find UUID: ", matchedProfileId);
|
||||||
|
res.status(404).send("Profile not found.");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
app.post("/profile/v2/map-ended", async (req, res) => {
|
||||||
|
const ticket = req.header("Authorization");
|
||||||
|
const clientIp = req.ip;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const mapList = req.body;
|
||||||
|
let leaderboard = readLeaderboard(true); // Load the current leaderboard data
|
||||||
|
|
||||||
|
for (let song of mapList) {
|
||||||
|
core.updateMostPlayed(song.mapName);
|
||||||
|
|
||||||
|
// Initialize the map in the leaderboard if it doesn't exist
|
||||||
|
if (!leaderboard[song.mapName]) {
|
||||||
|
leaderboard[song.mapName] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the user profile
|
||||||
|
const profile = findUserFromTicket(ticket);
|
||||||
|
if (!profile) {
|
||||||
|
return res.send('1');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if an entry for this profileId already exists
|
||||||
|
const currentScores = leaderboard[song.mapName];
|
||||||
|
const existingEntryIndex = currentScores.findIndex(entry => entry.profileId === profile.profileId);
|
||||||
|
|
||||||
|
if (existingEntryIndex !== -1) {
|
||||||
|
// Entry exists for this profile, update if the new score is higher
|
||||||
|
if (currentScores[existingEntryIndex].score < song.score) {
|
||||||
|
currentScores[existingEntryIndex].score = song.score;
|
||||||
|
console.log(`[LEADERBOARD] Updated score dotw list on map ${song.mapName}`);
|
||||||
|
} else {
|
||||||
|
return res.send('1'); // Do nothing if the new score is lower
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No existing entry for this profile, add a new one
|
||||||
|
const newScoreEntry = {
|
||||||
|
__class: "LeaderboardEntry",
|
||||||
|
score: song.score,
|
||||||
|
profileId: profile.profileId,
|
||||||
|
gameVersion: getGameVersion(req),
|
||||||
|
rank: profile.rank,
|
||||||
|
name: profile.name,
|
||||||
|
avatar: profile.avatar,
|
||||||
|
country: profile.country,
|
||||||
|
platformId: profile.platformId,
|
||||||
|
alias: profile.alias,
|
||||||
|
aliasGender: profile.aliasGender,
|
||||||
|
jdPoints: profile.jdPoints,
|
||||||
|
portraitBorder: profile.portraitBorder,
|
||||||
|
weekOptain: getWeekNumber()
|
||||||
|
};
|
||||||
|
|
||||||
|
currentScores.push(newScoreEntry);
|
||||||
|
leaderboard[song.mapName] = currentScores
|
||||||
|
console.log(`[LEADERBOARD] Added new score for ${profile.name} on map ${song.mapName}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the updated leaderboard back to the file
|
||||||
|
saveLeaderboard(leaderboard, true);
|
||||||
|
res.send('');
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
res.status(200).send(''); // Keep sending response even in case of error
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Delete favorite map
|
||||||
app.delete("/profile/v2/favorites/maps/:MapName", async (req, res) => {
|
app.delete("/profile/v2/favorites/maps/:MapName", async (req, res) => {
|
||||||
try {
|
try {
|
||||||
var MapName = req.params.MapName;
|
const MapName = req.params.MapName;
|
||||||
var ticket = req.header("Authorization");
|
const ticket = req.header("Authorization");
|
||||||
var SkuId = req.header("X-SkuId");
|
const SkuId = req.header("X-SkuId");
|
||||||
var response = await axios.delete(
|
|
||||||
prodwsurl + "/profile/v2/favorites/maps/" + MapName,
|
const response = await axios.delete(
|
||||||
{
|
`${prodwsurl}/profile/v2/favorites/maps/${MapName}`, {
|
||||||
headers: {
|
headers: {
|
||||||
"X-SkuId": SkuId,
|
"X-SkuId": SkuId,
|
||||||
Authorization: ticket,
|
Authorization: ticket,
|
||||||
},
|
},
|
||||||
}
|
});
|
||||||
);
|
|
||||||
res.send(response.data);
|
res.send(response.data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).send(error.message);
|
res.status(500).send(error.message);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Get profile sessions
|
||||||
app.get("/v3/profiles/sessions", async (req, res) => {
|
app.get("/v3/profiles/sessions", async (req, res) => {
|
||||||
try {
|
try {
|
||||||
var ticket = req.header("Authorization");
|
const ticket = req.header("Authorization");
|
||||||
var appid = req.header("Ubi-AppId");
|
const appid = req.header("Ubi-AppId");
|
||||||
var response = await axios.get(ubiwsurl + "/v3/profiles/sessions", {
|
|
||||||
|
const response = await axios.get(`${ubiwsurl}/v3/profiles/sessions`, {
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
"Ubi-AppId": appid,
|
"Ubi-AppId": appid,
|
||||||
Authorization: ticket,
|
Authorization: ticket,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
res.send(response.data);
|
res.send(response.data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).send(error.message);
|
res.status(500).send(error.message);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post("/profile/v2/filter-players", function (request, response) {
|
// Endpoint to filter players
|
||||||
response.send(["00000000-0000-0000-0000-000000000000"]);
|
app.post("/profile/v2/filter-players", (req, res) => {
|
||||||
|
res.send(["00000000-0000-0000-0000-000000000000"]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ const core = {
|
|||||||
CloneObject: require('../helper').CloneObject, getSavefilePath: require('../helper').getSavefilePath,
|
CloneObject: require('../helper').CloneObject, getSavefilePath: require('../helper').getSavefilePath,
|
||||||
generateCarousel: require('../carousel/carousel').generateCarousel, generateSweatCarousel: require('../carousel/carousel').generateSweatCarousel, generateCoopCarousel: require('../carousel/carousel').generateCoopCarousel, updateMostPlayed: require('../carousel/carousel').updateMostPlayed
|
generateCarousel: require('../carousel/carousel').generateCarousel, generateSweatCarousel: require('../carousel/carousel').generateSweatCarousel, generateCoopCarousel: require('../carousel/carousel').generateCoopCarousel, updateMostPlayed: require('../carousel/carousel').updateMostPlayed
|
||||||
}
|
}
|
||||||
const DOTW_PATH = path.join(core.getSavefilePath(), 'leaderboard/dotw/');
|
const LEADERBOARD_PATH = path.join(core.getSavefilePath(), 'leaderboard/leaderboard.json');
|
||||||
|
const DOTW_PATH = path.join(core.getSavefilePath(), 'leaderboard/dotw.json');
|
||||||
|
|
||||||
const { getSavefilePath } = require('../helper');
|
const { getSavefilePath } = require('../helper');
|
||||||
const { encrypt, decrypt } = require('../lib/encryptor')
|
const { encrypt, decrypt } = require('../lib/encryptor')
|
||||||
|
|
||||||
@@ -32,34 +34,6 @@ function generateToolNickname() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getProfileData(ticket, content, clientIp) {
|
|
||||||
const dataFilePath = path.join(getSavefilePath(), '/account/profiles/user.json');
|
|
||||||
try {
|
|
||||||
if (Object.keys(decryptedData).length === 0) {
|
|
||||||
const encryptedData = fs.readFileSync(dataFilePath, 'utf8');
|
|
||||||
decryptedData = JSON.parse(decrypt(encryptedData, secretKey));
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
decryptedData = {};
|
|
||||||
console.log('[ACC] Unable to read user.json');
|
|
||||||
console.log('[ACC] Is the key correct? are the files corrupted?');
|
|
||||||
console.log('[ACC] Ignore this message if this first run');
|
|
||||||
console.log('[ACC] Resetting All User Data...');
|
|
||||||
console.log(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
const matchedProfileId = Object.keys(decryptedData).find(profileId => {
|
|
||||||
const userProfile = decryptedData[profileId];
|
|
||||||
return userProfile.ticket === ticket || userProfile.ip === clientIp;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (matchedProfileId) {
|
|
||||||
return decryptedData[matchedProfileId];
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const getGameVersion = (req) => {
|
const getGameVersion = (req) => {
|
||||||
const sku = req.header('X-SkuId') || "jd2019-pc-ww";
|
const sku = req.header('X-SkuId') || "jd2019-pc-ww";
|
||||||
return sku.substring(0, 6) || "jd2019";
|
return sku.substring(0, 6) || "jd2019";
|
||||||
@@ -129,108 +103,128 @@ const initroute = (app) => {
|
|||||||
|
|
||||||
app.get("/leaderboard/v1/maps/:mapName/world", async (req, res) => {
|
app.get("/leaderboard/v1/maps/:mapName/world", async (req, res) => {
|
||||||
const { mapName } = req.params;
|
const { mapName } = req.params;
|
||||||
|
|
||||||
let leaderboardData = {
|
let leaderboardData = {
|
||||||
"__class": "LeaderboardList",
|
"__class": "LeaderboardList",
|
||||||
"entries": []
|
"entries": []
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
leaderboardData.entries.push(
|
// Read the leaderboard file
|
||||||
{
|
const leaderboardFilePath = LEADERBOARD_PATH;
|
||||||
"__class": "LeaderboardEntry_Online",
|
if (fs.existsSync(leaderboardFilePath)) {
|
||||||
"profileId": "00000000-0000-0000-0000-000000000000",
|
const data = fs.readFileSync(leaderboardFilePath, 'utf-8');
|
||||||
"score": Math.floor(Math.random() * 1333) + 12000,
|
const leaderboard = JSON.parse(data);
|
||||||
"name": generateToolNickname(),
|
|
||||||
"avatar": Math.floor(Math.random() * 100),
|
// Check if there are entries for the mapName
|
||||||
"country": Math.floor(Math.random() * 20),
|
if (leaderboard[mapName]) {
|
||||||
"platformId": "e3",
|
// Sort the leaderboard entries by score in descending order
|
||||||
"alias": 0,
|
const sortedEntries = leaderboard[mapName].sort((a, b) => b.score - a.score);
|
||||||
"aliasGender": 0,
|
|
||||||
"jdPoints": 0,
|
leaderboardData.entries = sortedEntries.map(entry => ({
|
||||||
"portraitBorder": 0,
|
"__class": "LeaderboardEntry_Online",
|
||||||
"mapName": mapName
|
"profileId": entry.profileId,
|
||||||
});
|
"score": entry.score,
|
||||||
|
"name": entry.name,
|
||||||
|
"avatar": entry.avatar,
|
||||||
|
"country": entry.country,
|
||||||
|
"platformId": entry.platformId,
|
||||||
|
"alias": entry.alias,
|
||||||
|
"aliasGender": entry.aliasGender,
|
||||||
|
"jdPoints": entry.jdPoints,
|
||||||
|
"portraitBorder": entry.portraitBorder,
|
||||||
|
"mapName": mapName
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
res.json(leaderboardData);
|
res.json(leaderboardData);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error:", error.message);
|
console.error("Error:", error.message);
|
||||||
res.status(500).send("Internal Server Error");
|
res.status(500).send("Internal Server Error");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post("/profile/v2/map-ended", async (req, res) => {
|
|
||||||
const ticket = req.header("Authorization");
|
|
||||||
const clientIp = req.ip;
|
|
||||||
try {
|
|
||||||
const mapList = req.body;
|
|
||||||
for (let song of mapList) {
|
|
||||||
core.updateMostPlayed(song.mapName);
|
|
||||||
|
|
||||||
const dotwFilePath = path.join(DOTW_PATH, `${song.mapName}.json`);
|
|
||||||
if (fs.existsSync(dotwFilePath)) {
|
|
||||||
const readFile = fs.readFileSync(dotwFilePath, 'utf-8');
|
|
||||||
const JSONParFile = JSON.parse(readFile);
|
|
||||||
if (JSONParFile.score > song.score) {
|
|
||||||
return res.send('1');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const profiljson1 = await getProfileData(ticket, song, clientIp);
|
|
||||||
if (!profiljson1) {
|
|
||||||
return res.send('1')
|
|
||||||
}
|
|
||||||
|
|
||||||
const jsontodancerweek = {
|
|
||||||
__class: "DancerOfTheWeek",
|
|
||||||
score: song.score,
|
|
||||||
profileId: profiljson1.profileId,
|
|
||||||
gameVersion: getGameVersion(req),
|
|
||||||
rank: profiljson1.rank,
|
|
||||||
name: profiljson1.name,
|
|
||||||
avatar: profiljson1.avatar,
|
|
||||||
country: profiljson1.country,
|
|
||||||
platformId: profiljson1.platformId,
|
|
||||||
alias: profiljson1.alias,
|
|
||||||
aliasGender: profiljson1.aliasGender,
|
|
||||||
jdPoints: profiljson1.jdPoints,
|
|
||||||
portraitBorder: profiljson1.portraitBorder,
|
|
||||||
};
|
|
||||||
|
|
||||||
fs.writeFileSync(dotwFilePath, JSON.stringify(jsontodancerweek, null, 2));
|
|
||||||
console.log(`DOTW file for ${song.mapName} created!`);
|
|
||||||
res.send('');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error)
|
|
||||||
res.status(200).send(''); //keep send
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
app.get("/leaderboard/v1/maps/:map/dancer-of-the-week", (req, res) => {
|
app.get("/leaderboard/v1/maps/:map/dancer-of-the-week", (req, res) => {
|
||||||
const dotwFilePath = path.join(DOTW_PATH, `${req.params.map}.json`);
|
const { map } = req.params;
|
||||||
if (fs.existsSync(dotwFilePath)) {
|
|
||||||
const readFile = fs.readFileSync(dotwFilePath, 'utf-8');
|
// Path to leaderboard file
|
||||||
res.send(readFile);
|
const leaderboardFilePath = DOTW_PATH;
|
||||||
} else {
|
|
||||||
res.setHeader('Content-Type', 'application/json; charset=utf-8');
|
try {
|
||||||
res.setHeader('Access-Control-Allow-Origin', '*');
|
if (fs.existsSync(leaderboardFilePath)) {
|
||||||
res.send({
|
const data = fs.readFileSync(leaderboardFilePath, 'utf-8');
|
||||||
"__class": "DancerOfTheWeek",
|
const leaderboard = JSON.parse(data);
|
||||||
"profileId": "00000000-0000-0000-0000-000000000000",
|
|
||||||
"score": 69,
|
// Check if the map exists in the leaderboard
|
||||||
"gameVersion": "jd2019",
|
if (leaderboard[map] && leaderboard[map].length > 0) {
|
||||||
"rank": 1,
|
// Find the highest score entry for this map
|
||||||
"name": "NO DOTW",
|
const highestEntry = leaderboard[map].reduce((max, entry) => entry.score > max.score ? entry : max);
|
||||||
"avatar": 1,
|
|
||||||
"country": 0,
|
const dancerOfTheWeek = {
|
||||||
"platformId": "3935074714266132752",
|
"__class": "DancerOfTheWeek",
|
||||||
"alias": 0,
|
"profileId": highestEntry.profileId,
|
||||||
"aliasGender": 0,
|
"score": highestEntry.score,
|
||||||
"jdPoints": 0,
|
"gameVersion": highestEntry.gameVersion || "jd2020",
|
||||||
"portraitBorder": 0
|
"rank": highestEntry.rank, // Since it's the highest, assign rank 1
|
||||||
});
|
"name": highestEntry.name,
|
||||||
|
"avatar": highestEntry.avatar,
|
||||||
|
"country": highestEntry.country,
|
||||||
|
"platformId": highestEntry.platformId,
|
||||||
|
"alias": highestEntry.alias,
|
||||||
|
"aliasGender": highestEntry.aliasGender,
|
||||||
|
"jdPoints": highestEntry.jdPoints,
|
||||||
|
"portraitBorder": highestEntry.portraitBorder
|
||||||
|
};
|
||||||
|
|
||||||
|
res.json(dancerOfTheWeek);
|
||||||
|
} else {
|
||||||
|
// If no entries for the map, return default "NO DOTW" response
|
||||||
|
res.setHeader('Content-Type', 'application/json; charset=utf-8');
|
||||||
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
||||||
|
res.json({
|
||||||
|
"__class": "DancerOfTheWeek",
|
||||||
|
"profileId": "00000000-0000-0000-0000-000000000000",
|
||||||
|
"score": 69,
|
||||||
|
"gameVersion": "jd2019",
|
||||||
|
"rank": 1,
|
||||||
|
"name": "NO DOTW",
|
||||||
|
"avatar": 1,
|
||||||
|
"country": 0,
|
||||||
|
"platformId": "3935074714266132752",
|
||||||
|
"alias": 0,
|
||||||
|
"aliasGender": 0,
|
||||||
|
"jdPoints": 0,
|
||||||
|
"portraitBorder": 0
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If leaderboard file does not exist, return default "NO DOTW" response
|
||||||
|
res.setHeader('Content-Type', 'application/json; charset=utf-8');
|
||||||
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
||||||
|
res.json({
|
||||||
|
"__class": "DancerOfTheWeek",
|
||||||
|
"profileId": "00000000-0000-0000-0000-000000000000",
|
||||||
|
"score": 69,
|
||||||
|
"gameVersion": "jd2019",
|
||||||
|
"rank": 1,
|
||||||
|
"name": "NO DOTW",
|
||||||
|
"avatar": 1,
|
||||||
|
"country": 0,
|
||||||
|
"platformId": "3935074714266132752",
|
||||||
|
"alias": 0,
|
||||||
|
"aliasGender": 0,
|
||||||
|
"jdPoints": 0,
|
||||||
|
"portraitBorder": 0
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error:", error.message);
|
||||||
|
res.status(500).send("Internal Server Error");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = { initroute };
|
module.exports = { initroute };
|
||||||
|
|||||||
Reference in New Issue
Block a user