UNPKG

dashtools

Version:

Library for interacting with Geometry Dash servers

324 lines (314 loc) 12.5 kB
import { genericRequest } from "./generic.js" import * as constants from "../constants.js" import * as utils from "../utils.js" /** * Gets levels from a Geometry Dash server. * @param {GDServer} instance The Geometry Dash server instance * @param {object} params The parameters * @param {function} callback The callback function * @param {object} options Extra Axios parameters * @param {string} secret The secret parameter */ export function getLevels(params, instance, opts, callback, options, secret) { const diffMap = { "-1": -3, 0: -1, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: -2, 7: -2, 8: -2, 9: -2, 10: -2 } let len = "-" if (params.length) { if (!isNaN(parseInt(params.length))) len = parseInt(params.length) else len = constants.LENGTHS[params.length] } let diff = {} if (params.difficulties && params.difficulties.length > 0) { let diffs = params.difficulties.map(d => diffMap[d] || 0) if (params.difficulties.length > 1) { if (diffs.includes(-2)) throw new Error("Only one demon difficulty can be searched at a time") diff.diff = Array.from(new Set(diffs)).join(",") } else if (diffs[0] == -2) { params.demonFilter = params.difficulties[0] - 5 } else { diff.diff = Number(diffs[0]) } } let str = params.query || "" if (params.type == 5) str = params.playerID else if (params.type == 10 || params.type == 19) str = params.levelIDs.join(",") else if (params.type == 25) str = params.listID let auth = {} if (params.type == 13) { if (!instance.account) throw new Error("Must be authorized to get friend levels") auth.accountID = instance.account.accountID auth.gjp2 = utils.gjp2(instance.account.password) } params = { page: params.page || 0, type: params.type || 2, str, len, noStar: Number(!!params.unrated) || 0, star: Number(!!params.rated) || 0, featured: Number(!!params.featured) || 0, epic: Number(!!params.epic) || 0, legendary: Number(!!params.legendary) || 0, mythic: Number(!!params.mythic) || 0, coins: Number(!!params.coins) || 0, twoPlayer: Number(!!params.twoPlayer) || 0, original: Number(!!params.original) || 0, followed: params.accountIDs ? params.accountIDs.join(",") : "", uncompleted: !!params.uncompleted || 0, onlyCompleted: !!params.completed || 0, completedLevels: params.uncompleted || params.completed ? `(${params.levelIDs.join(",")})` : "", diff: diff.diff || undefined, customSong: params.customSong || 0, song: params.songID || 0, demonFilter: diff.demonFilter || 0, ...auth } genericRequest("getLevels", params, function(data) { let date = Date.now() let segments = data.split("#") let levels = segments[0].split("|").map(l => utils.parseLevel(l)) let songs let users let pages let hash = "" if (instance.versions.gameVersion >= 19) { users = segments[1] songs = segments[2] pages = segments[3] hash = segments[4] } else { users = segments[1] pages = segments[2] hash = segments[3] } songs = utils.parseSongs(songs) users = utils.parseUsers(users) pages = pages.split(":") callback({ levels, songs, users, total: Number(pages[0]), offset: Number(pages[1]), pageSize: Number(pages[2]), hash, isHashValid: utils.generateLevelsHash(levels) == hash, date }) }, instance, opts, options, secret) } export function getDailyLevel(instance, params, callback, options, secret) { genericRequest("getDailyLevel", {}, function(data) { callback({ dailyID: Number(data.split("|")[0]), timeLeft: Number(data.split("|")[1]) }) }, instance, params, options, secret) } export function getEventLevel(instance, params, callback, options, secret) { genericRequest("getDailyLevel", {type: 2}, function(data) { callback({ eventID: Number(data.split("|")[0]), timeLeft: Number(data.split("|")[1]) }) }, instance, params, options, secret) } export function getMapPacks(instance, params, callback, options, secret) { genericRequest("getMapPacks", {}, function(data) { let segments = data.split("#") let packsRaw = segments[0].split("|") let pages = segments[1].split(":") let hash = segments[2] let packs = [] for (let pack of packsRaw) { packs.push(utils.parseMapPack(pack)) } callback({ packs, total: Number(pages[0]), offset: Number(pages[1]), pageSize: Number(pages[2]), hash, isHashValid: utils.generateMapPacksHash(packs) == hash }) }, instance, params, options, secret) } export function getGauntlets(instance, params, callback, options, secret) { genericRequest("getGauntlets", {special: 1}, function(data) { let segments = data.split("#") let packsRaw = segments[0].split("|") let hash = segments[1] let packs = [] for (let pack of packsRaw) { pack = utils.robTopSplit(pack, ":") packs.push({ id: Number(pack.get("1")), levels: pack.get("3").split(",") }) } callback({ packs, hash, isHashValid: utils.generateGauntletsHash(packs) == hash }) }, instance, params, options, secret) } export function downloadLevel(levelID, instance, params, callback, options, secret) { let creds = {} let opt = { levelID } delete params.levelID creds.rs = utils.rs(10) if (instance.account) { creds.udid = instance.account.udid creds.uuid = instance.account.playerID creds.gjp2 = utils.gjp2(instance.account.password) creds.accountID = instance.account.accountID creds.inc = Number(!!params.increment) creds.chk = utils.chk([levelID, creds.inc, creds.rs, instance.account.accountID, creds.udid, creds.uuid], constants.KEYS.LEVEL, constants.SALTS.LEVEL) // delete params.udid // delete params.uuid // delete params.chk // delete params.inc // delete params.gjp2 // delete params.rs // delete params.gjp // delete params.accountID } else if (params.inc) throw new Error("Must authenticate with an account to increment") opt = { ...opt, ...creds } genericRequest("downloadLevel", opt, function(data) { let segments = data.split("#") let func = utils.parseLevel if (instance.versions.gameVersion < 20) func = utils.parseLevelOld let level = func(segments[0]) let hashes = segments.slice(1, 3) let json = { level, hashes, isHash1Valid: utils.generateDownloadHash(level.levelString) == hashes[0], isHash2Valid: utils.generateDownloadHash2(level.metadata) == hashes[1] } if (segments[3]) { json.unk_segment_4 = segments[3] } if (segments[4]) { json.songs = utils.parseSongs(segments[4]) } if (segments[5]) { json.extraArtists = utils.robTopSplitDict(segments[5], ",") } callback(json) }, instance, params, options, secret) } export function reportLevel(levelID, instance, params, callback, options, secret) { genericRequest("reportLevel", {levelID}, function(data) { if (data > 0) callback(data) else throw new Error(data) }, instance, params, options, secret) } // export function deleteLevel(levelID, instance, params, callback, options, secret) { // genericRequest("deleteLevel", {levelID, accountID: instance.account.accountID, gjp2: utils.gjp2(instance.account.password)}, function(data) { // if (data > 0) callback(data) // else throw new Error(data) // }, instance, params, options, (secret || constants.SECRETS.DELETE)) // } export function rateLevel(levelID, stars, instance, params, callback, options, secret) { if (!instance.account) throw new Error("You must authenticate in order to send rate suggestions for levels") let rs = utils.rs(10) let chk = utils.chk([ levelID, stars, rs, instance.account.accountID, instance.account.udid, instance.account.playerID ], constants.KEYS.RATE, constants.SALTS.LIKE_OR_RATE) genericRequest("rateLevel", { levelID, stars, chk, rs, udid: instance.account.udid, uuid: instance.account.playerID, accountID: instance.account.accountID, gjp2: utils.gjp2(instance.account.password) }, function(data) { callback(data) }, instance, params, options, secret) } export function rateDemon(levelID, rating, instance, params, callback, options, secret) { if (!instance.account) throw new Error("You must authenticate in order to send rate suggestions for levels") genericRequest("rateDemon", {levelID, rating, accountID: instance.account.accountID, gjp2: utils.gjp2(instance.account.password)}, function(data) { callback(data) }, instance, params, options, secret || constants.SECRETS.MOD) } export function updateDescription(levelID, description, instance, params, callback, options, secret) { if (!instance.account) throw new Error("You must authenticate in order to update a level's description") genericRequest("updateDescription", {levelID, levelDesc: utils.base64Encode(description), accountID: instance.account.accountID, gjp2: utils.gjp2(instance.account.password)}, function(data) { callback(data) }, instance, params, options, secret) } export function uploadLevel(opts, instance, params, callback, options, secret) { if (!instance.account) throw new Error("You must authenticate in order to upload a level") let seed2 = utils.generateUploadSeed2(opts.levelString) let song = {} if (opts.songIDs) song.songIDs = opts.songIDs.join(",") if (opts.sfxIDs) song.sfxIDs = opts.sfxIDs.join(",") opts = { accountID: instance.account.accountID, gjp2: utils.gjp2(instance.account.password), userName: instance.account.username, levelID: opts.id || 0, levelName: opts.name, levelDesc: (opts.description ? utils.base64Encode(opts.description) : ""), unlisted: opts.unlistedMode || 0, levelVersion: opts.version || 1, requestedStars: opts.requestedStars || 0, levelString: opts.levelString, levelLength: opts.length || 0, original: opts.originalID || 0, twoPlayer: Number(!!opts.twoPlayer), objects: opts.objects || 0, coins: opts.coins || 0, ldm: Number(!!opts.hasLowDetailMode), password: opts.password != null && opts.password != undefined ? Number(opts.password) : (instance.binaryVersion > 37 ? 1 : 0), // free to copy by default on 2.201+, no copy by default on 2.200- audioTrack: opts.officialSongID || 0, songID: opts.songID || 0, ts: opts.verificationTime || 0, ...song, auto: 0, wt: opts.editorTime || 0, wt2: opts.copiesEditorTime || 0, seed: utils.rs(10), seed2, uuid: instance.account.playerID, udid: instance.account.udid } genericRequest("uploadLevel", opts, function(id) { if (id == -1) throw new Error(-1) callback(id) }, instance, params, options, secret) } export function deleteLevel(levelID, instance, params, callback, options, secret) { if (!instance.account) throw new Error("You must authenticate in order to delete a level") genericRequest("deleteLevel", {levelID, accountID: instance.account.accountID, gjp2: utils.gjp2(instance.account.password)}, (d) => { if (d == -1) throw new Error("-1") callback(data) }, instance, params, options, secret || constants.SECRETS.DELETE) }