node-geometry-dash
Version:
A node.js module for searching for levels, accounts, getting songs, and the leaderboards of the game Geometry Dash
596 lines (580 loc) • 15.1 kB
JavaScript
;
const request = require("request");
const MainURL = "http://boomlings.com/database";
const EventEmitter = require("events");
class GDClient extends EventEmitter {
constructor(options) {
super()
this.options = {
username: "",
password: ""
}
for(let i in options)
this.options[i] = options[i];
this.LEVEL_LENGTH = {};
this.LEVEL_LENGTH.TINY = "0";
this.LEVEL_LENGTH.SHORT = "1";
this.LEVEL_LENGTH.MEDIUM = "2";
this.LEVEL_LENGTH.LONG = "3";
this.LEVEL_LENGTH.XL = "4";
this.LEVEL_TYPE = {};
this.LEVEL_TYPE.DOWNLOADS = "1";
this.LEVEL_TYPE.LIKES = "2";
this.LEVEL_TYPE.UPLOAD_DATE = "4";
this.LEVEL_TYPE.UPLOAD_DATE_FEATURED = "6";
this.LEVEL_TYPE.UPLOAD_DATE_STARS = "11";
}
login() {
console.log("Not finished ¯\\_(ツ)_/¯");
}
levelPassword(levelID, cb) {
return new Promise((res,rej) => {
request.post({
url: `${MainURL}/downloadGJLevel21.php`,
form: {
gameVersion: "20",
binaryVersion: "29",
levelID,
secret: "Wmfd2893gb7"
}
}, (e,r,b) => {
let pass = b.split(":")[b.split(":").length-1].split("#")[0];
let decoded = decodeXor(pass,"26364");
if(cb) {
cb(decoded);
return null;
}
if(decoded === null) return rej();
res(decoded);
function decodeXor(in_,key) {
if(in_.toString()==="0") return null;
in_ = new Buffer(in_,"base64").toString();
let out = "";
for(let i in in_) {
let cc = in_.charCodeAt(i);
let k = key.charCodeAt(i%key.length);
out+=String.fromCharCode(cc^k);
}
return out;
}
});
});
}
levels(name, obj, cb) {
let len = "-";
let type = "0";
let total = "0";
if(typeof name === "object" || obj != undefined && (obj.len||obj.length)&&obj.type&&obj.total) {
len = (name.length ? name.length : (name.len?name.len:"-")).toString();
type = (name.type||type).toString();
total = (name.total||total).toString();
}
let f="-";
let or="-";
let tp="-";
let st="-";
let page = 0;
if(obj) {
if(obj.featured)f=(obj.featured == true ? 1 : 0);
if(obj.original)or=(obj.original == true ? 1 : 0);
if(obj.twoPlayer)tp=(obj.twoPlayer == true ? 1 : 0);
if(obj.star)st=(obj.star == true ? 1 : 0);
if(obj.length)len=obj.length;
if(obj.page)page=obj.page;
if(name.name)name=name.name;
}
let Prom = new Promise((res,rej)=> {
request.post({
url: `${MainURL}/getGJLevels21.php`,
form: {
gameVersion: "20",
binaryVersion: "29",
str: name,
total: total,
len: len,
type: type,
featured: f,
original: or,
twoPlayer: tp,
star: st,
page: page,
secret: "Wmfd2893gb7"
}
}, (e,r,b) => {
let levels = b.split("#")[0].split("|");
let lvlArr = [];
for(let i in levels) {
if(levels[i] == "-1") {
rej();
} else {
let lData = levels[i].split(":");
if(lData[11] == "50") {
if(lData[21] == "1") {
lData[11] = "Demon";
} else if(lData[25] == "1") {
lData[11] = "Auto";
} else {
lData[11] = "Insane";
}
}
if(lData[11] == "40") {
lData[11] = "Harder";
} else if(lData[11] == "30") {
lData[11] = "Hard";
} else if(lData[11] == "20") {
lData[11] = "Normal";
} else if(lData[11] == "10") {
lData[11] = "Easy";
} else if(lData[11] == "0") {
lData[11] = "N/A";
}
const lengtharr = [
"Tiny",
"Short",
"Medium",
"Long",
"XL"
];
const parsedData = {
name: lData[3],
id: lData[1],
author: {
name: "",
id: lData[7]
}, // if you are wondering why the "+2"s are there, they are a fix to the bugs that gdw brought to this module :eyes:
song: {},
difficulty: lData[11],
downloads: lData[13],
likes: lData[19],
stars: lData[27],
featured: (lData[29] != 0 ? true : false),
epic: lData[(33+2)-4] === "1",
description: new Buffer(lData[33+2].toString(), "base64").toString(),
length: lengtharr[parseInt(lData[35+2])],
coins: lData[41+2],
version: lData[5],
verifiedCoins: (lData[43+2] == 1 ? true : false),
requestedStars: lData[45+2]
}
let authors = b.split("#")[1].split("|");
for(let a in authors) {
if(authors[a].split(":")[0] == lData[7]) {
parsedData.author.name = authors[a].split(":")[1];
}
}
let songs = b.split("#")[2].split(":");
if(lData[47+2] != "0") {
for(let s in songs) {
if(songs[s].split("~|~")[1] == lData[47+2]) {
let song = songs[s].split("~|~");
parsedData.song = {
name: song[3],
author: song[7],
id: song[1],
size: song[9] + "MB",
url: decodeURIComponent( song[13] )
}
}
}
} else {
lData[47+2] = lData[15];
let mainSongs = {
"0": ["Stereo Madness", "ForeverBound"],
"1": ["Back On Track", "DJVI"],
"2": ["Polargeist", "Step"],
"3": ["Dry Out", "DJVI"],
"4": ["Base After Base", "DJVI"],
"5": ["Cant Let Go", "DJVI"],
"6": ["Jumper", "Waterflame"],
"7": ["Time Machine", "Waterflame"],
"8": ["Cycles", "DJVI"],
"9": ["xStep", "DJVI"],
"10": ["Clutterfunk", "Waterflame"],
"11": ["Theory of Everything", "DJ-Nate"],
"12": ["Electroman Adventures", "Waterflame"],
"13": ["Clubstep", "DJ-Nate"],
"14": ["Electrodynamix", "DJ-Nate"],
"15": ["Hexagon Force", "Waterflame"],
"16": ["Blast Processing", "Waterflame"],
"17": ["Theory of Everything 2", "DJ-Nate"],
"18": ["Geometrical Dominator", "Waterflame"],
"19": ["Deadlocked", "F-777"],
"20": ["Fingerbang", "MDK"] // why not kek
} // Thanks meganukebmp/Nexrem (https://github.com/meganukebmp/Discord-GD-Stat/blob/master/resources/level/tracks.json) (modified)
let song = mainSongs[lData[47+2]+""] || [
"Unknown", "Unknown"
];
parsedData.song = {
name: song[0],
author: song[1],
id: lData[47+2],
size: null,
url: null
}
}
lvlArr.push(parsedData)
}
}
if(!cb || typeof cb !== "function")
res(lvlArr)
else {
cb(lvlArr)
return null;
}
})
});
name = name.toString();
return Prom;
}
users(name,obj,cb) {
let total = "0";
let page = "0";
if(typeof name==="object") {
total = name.total ? name.total : total;
page = name.page ? name.page : page;
name = name.name ? name.name : "16";
}
if(obj) {
if(obj.total)total=obj.total;
if(obj.page) page =obj.page;
}
let Prom = new Promise((res,rej)=> {
request.post({
url: `${MainURL}/getGJUsers20.php`,
form: {
gameVersion: "20",
binaryVersion: "29",
str: name,
total: total,
page: page,
secret: "Wmfd2893gb7"
}
}, (e,r,b)=>{
if(b == "-1")
rej();
else {
let users = b.split("#")[0].split("|");
let userarr = [];
for(let user of users) {
user = user.split(":");
const parsedData = {
username: user[1],
id: user[3],
coins: user[5],
userCoins: user[7],
stars: user[23],
demons: user[27],
creatorPoints: user[25],
extID: user[21]
}
userarr.push(parsedData);
}
if(!cb || typeof cb !== "function")
res(userarr);
else {
cb(userarr);
return null;
}
}
});
})
return Prom;
}
user(extID, cb) {
let Prom = new Promise((res,rej)=> {
request.post({
url: `${MainURL}/getGJUserInfo20.php`,
form: {
gameVersion: "20",
binaryVersion: "29",
targetAccountID: extID,
secret: "Wmfd2893gb7"
}
}, (e,r,b)=>{
if(b == "-1")
rej();
else {
let user = b.split(":");
const parsedData = {
username: user[1],
id: user[3],
coins: user[5],
userCoins: user[7],
colors: {
primary: user[9],
secondary: user[11],
},
icons: {
cube : user[25+2],
ship : user[27+2],
ball : user[29+2],
ufo : user[31+2],
wave : user[33+2],
robot : user[35+2],
spider: user[37+2]
},
glow: user[37+2]==true,
youtube: user[23+2],
twitter: user[51], // gdw aka 2.1 beta
twitch: user[53],
stars: user[13],
diamonds: user[15],
demons: user[15+2],
creatorPoints: user[17+2],
extID: user[47]
}
if(!cb || typeof cb !== "function")
res(parsedData);
else {
cb(parsedData);
return null;
}
}
});
})
return Prom;
}
mapPacks(page, opt, cb) {
function genMapLvls(mappack, gd, arr) {
return new Promise((r,rej) => {
for(let i of mappack.levels) {
gd.levels(i).then( level => {
arr.push(level[0]);
if(arr.length === mappack.levels.length) {
r(arr);
}
} ).catch(rej);
}
})
}
let galvs = (opt ? opt.getAllMapPackLevels : false); // false to make less get requests to the server (spam it a little less)
let zbint = (opt ? opt.zeroBasedPage : true);
if(!page)
page = 0;
page = page.toString();
let Prom = new Promise((res,rej)=> {
request.post({
url: `${MainURL}/getGJMapPacks21.php`,
form: {
gameVersion: "20",
binaryVersion: "29",
page: (zbint === true ? page : page - 1),
secret: "Wmfd2893gb7"
}
}, (e,r,b)=>{
if(b == "-1")
rej();
else {
let mapPacks = b.split("#")[0].split("|");
let mapPackss = [];
function doIt() {
if(cb || typeof cb === "function") {
cb(mapPackss)
return null;
} else {
res(mapPackss);
}
}
for(let mappack of mapPacks) {
mappack = mappack.split(":");
if(mappack[11]=="1")
mappack[11] = "Easy";
if(mappack[11]=="2")
mappack[11] = "Normal";
if(mappack[11]=="3")
mappack[11] = "Hard";
if(mappack[11]=="4")
mappack[11] = "Harder";
if(mappack[11]=="5")
mappack[11] = "Insane";
if(mappack[11]=="6")
mappack[11] = "Demon";
const parsedData = {
name: mappack[3],
id: mappack[1],
levels: mappack[5].split(","),
stars: mappack[7],
coins: mappack[9],
difficulty: mappack[11],
rgb: mappack[13].split(",")
}
if(galvs) {
genMapLvls(parsedData, this, []).then( levels => {
parsedData.levels = levels;
mapPackss.push(parsedData);
if(mapPackss.length === mapPacks.length)
doIt();
})
} else {
mapPackss.push(parsedData);
if(mapPackss.length === mapPacks.length)
doIt();
}
}
}
});
})
return Prom;
}
comments(level, page="0", cb) {
page = page.toString();
level = level.toString();
let Prom = new Promise((res,rej)=> {
request.post({
url: `${MainURL}/getGJComments21.php`,
form: {
gameVersion: "20",
binaryVersion: "29",
levelID: level,
page: page,
secret: "Wmfd2893gb7"
}
}, (e,r,b) => {
let comments = b.split("#")[0].split("|");
let commentsa = [];
for(let i in comments) {
if(comments[i] == "-1" || comments[i] === "") {
rej();
} else {
let comment = comments[i].split("~");
let parsedData = {
comment: new Buffer(comment[1].toString(), "base64").toString(),
username: comment[14],
userID: comment[3],
likes: comment[5],
date: comment[11],
commentID: comment[13]
}
commentsa.push(parsedData)
}
}
if(!cb || typeof cb !== "function")
res(commentsa);
else{
cb(commentsa);
return null;
}
})
});
return Prom;
}
accountComments(accountID, page="0",cb) {
page = page.toString();
accountID = accountID.toString();
let Prom = new Promise((res,rej)=> {
request.post({
url: `${MainURL}/getGJAccountComments20.php`,
form: {
gameVersion: "20",
binaryVersion: "29",
accountID: accountID,
page: page,
secret: "Wmfd2893gb7"
}
}, (e,r,b) => {
let comments = b.split("#")[0].split("|");
let commentsa = [];
for(let i in comments) {
if(comments[i] == "-1" || comments[i] === "") {
rej();
} else {
let comment = comments[i].split("~");
let parsedData = {
comment: new Buffer(comment[1].toString(), "base64").toString(),
likes: comment[3],
date: comment[5],
commentID: comment[7]
}
commentsa.push(parsedData)
}
}
if(!cb || typeof cb !== "function")
res(commentsa);
else {
cb(commentsa);
return null;
}
})
});
return Prom;
}
leaderboards(type, cb) {
let Prom = new Promise((res,rej)=> {
request.post({
url: `${MainURL}/getGJScores20.php`,
form: {
gameVersion: "20",
binaryVersion: "29",
type: type,
secret: "Wmfd2893gb7"
}
}, (e,r,b) => {
let scores = b.split("#")[0].split("|");
let scoresa = [];
for(let i in scores) {
if(scores[i] == "-1") {
rej();
} else {
let score = scores[i].split(":");
if(score[0] != "") {
const parsedData = {
username: score[1],
id: score[3],
coins: score[5],
userCoins: score[7],
stars: score[23],
demons: score[27],
creatorPoints: score[25],
extID: score[21]
}
scoresa.push(parsedData);
}
}
}
if(!cb)
res(scoresa);
else {
cb(scoresa);
return null;
}
})
});
return Prom;
}
song(id, cb) {
let Prom = new Promise((res,rej)=> {
request.post({
url: `${MainURL}/getGJSongInfo.php`,
form: {
gameVersion: "20",
binaryVersion: "29",
songID: id,
secret: "Wmfd2893gb7"
}
}, (e,r,b) => {
let song = b.split("~|~");
if(b=="-1") {
rej();
}
else {
let parsedData = {
id: song[1],
name: song[3],
authorID: song[5],
author: song[7],
size: song[9]+"MB",
url: decodeURIComponent(song[13])
}
if(!cb)
res(parsedData);
else {
cb(parsedData);
return null;
}
}
})
});
return Prom;
}
}
module.exports = GDClient;