diff --git a/api/accurateLeaderboard.js b/api/accurateLeaderboard.js index b6b8a6e..56dc2ad 100644 --- a/api/accurateLeaderboard.js +++ b/api/accurateLeaderboard.js @@ -20,7 +20,7 @@ module.exports = async (app, req, res) => { sheet.loadInfo().then(async () => { let tab = sheet.sheetsById[1555821000] await tab.loadCells('A2:C2') - let topPlayers = tab.getCell(1, type == "demons" ? 2 : type == "usercoins" ? 1 : 0).value + let topPlayers = tab.getCell(1, type == "demons" ? 2 : type == "coins" ? 1 : 0).value let idArray = topPlayers.split(",") diff --git a/api/download.js b/api/download.js index dbfe70b..08485b1 100644 --- a/api/download.js +++ b/api/download.js @@ -23,7 +23,7 @@ module.exports = async (app, req, res, api, ID, analyze) => { } }, async function (err, resp, body) { - if (err || !body || body == '-1') { + if (err || !body || body == '-1' || body.startsWith(" { if (!glowOffset.some(x => x != 0)) glowOffset = [] let topless = form == "bird" && req.query.topless - let sizeParam = req.query.size && !isNaN(req.query.size) + let autoSize = req.query.size == "auto" + let sizeParam = autoSize || (req.query.size && !isNaN(req.query.size)) if (outline == "0") outline = false; if (iconID && iconID.toString().length == 1) iconID = "0" + iconID; @@ -69,14 +69,20 @@ module.exports = async (app, req, res) => { if (!fs.existsSync(fromIcons(icon))) return res.sendFile(path.join(__dirname, '../assets/unknownIcon.png')) } + let ex = fromIcons(extra) + let hasExtra = fs.existsSync(ex) + if (!colors[col1]) col1 = 0 if (!colors[col2]) col2 = 3 - let iconCode = `${req.query.form == "cursed" ? "cursed" : form}${topless ? "topless" : ""}-${iconID}-${col1}-${col2}-${outline ? 1 : 0}` - + let col3 = req.query.col3 + if (col3 && (!hasExtra || !colors[col3] || col3 == "12")) col3 = null + + let iconCode = `${req.query.form == "cursed" ? "cursed" : form}${topless ? "top" : ""}-${iconID}-${col1}-${col2}-${col3 || "x"}-${outline ? 1 : 0}` + if (!sizeParam && !glowOffset.length && cache[iconCode]) { clearTimeout(cache[iconCode].timeoutID); - cache[iconCode].timeoutID = setTimeout(function() {delete cache[iconCode]}, 600000); + cache[iconCode].timeoutID = setTimeout(function() {delete cache[iconCode]}, 1800000); return res.end(cache[iconCode].value); } @@ -89,6 +95,7 @@ module.exports = async (app, req, res) => { let robotOffset1, robotOffset2, robotOffset3, robotOffset1b, robotOffset2b, robotOffset3b; let robotGlow1, robotGlow2, robotGlow3 let ufoTop, ufoOffset, ufoCoords, ufoSprite + let extrabit, offset2, size2; if (isSpecial) { const legs = [1,2,3].map(function(val) {return genImageName(`0${val+1}`)}); @@ -108,23 +115,22 @@ module.exports = async (app, req, res) => { if (!glowOffset.length) glowOffset = offsets[form][+iconID] || [] } - res.contentType('image/png'); - let extrabit, offset2, size2; - if (fs.existsSync(fromIcons(extra))) { - extrabit = icons[extra] - offset2 = extrabit.spriteOffset.map(minusOrigOffset); - size2 = extrabit.spriteSize; - - extra = new Jimp(fromIcons(extra)); - useExtra = true - } - Jimp.read(fromIcons(glow)).then(async function (image) { let size = [image.bitmap.width, image.bitmap.height] let glow = recolor(image, col2) let imgOff = isSpecial ? 100 : 0 + let eb = fromIcons(extra) + if (fs.existsSync(eb)) { + extrabit = icons[extra] + offset2 = extrabit.spriteOffset.map(minusOrigOffset); + size2 = extrabit.spriteSize; + extra = new Jimp(eb); + if (col3) await Jimp.read(eb).then(e => { extra = recolor(e, col3) }) + useExtra = true + } + Jimp.read(fromIcons(icon)).then(async function (ic) { let iconSize = [ic.bitmap.width, ic.bitmap.height] @@ -279,18 +285,19 @@ module.exports = async (app, req, res) => { if (form == "swing") img.resize(120, 111) if (img.bitmap.height == 300) ic.autocrop(1, false) if (sizeParam) { - let imgSize = Math.round(req.query.size) + let thicc = img.bitmap.width > img.bitmap.height + let imgSize = req.query.size == "auto" ? (thicc ? img.bitmap.width : img.bitmap.height) : Math.round(req.query.size) if (imgSize < 32) imgSize = 32 if (imgSize > 512) imgSize = 512 - if (img.bitmap.width > img.bitmap.height) img.contain(img.bitmap.width, img.bitmap.width, Jimp.HORIZONTAL_ALIGN_CENTER | Jimp.VERTICAL_ALIGN_MIDDLE) - else if (img.bitmap.width < img.bitmap.height) img.contain(img.bitmap.height, img.bitmap.height, Jimp.HORIZONTAL_ALIGN_CENTER | Jimp.VERTICAL_ALIGN_MIDDLE) + if (thicc) img.contain(img.bitmap.width, img.bitmap.width, Jimp.HORIZONTAL_ALIGN_CENTER | Jimp.VERTICAL_ALIGN_MIDDLE) + else img.contain(img.bitmap.height, img.bitmap.height, Jimp.HORIZONTAL_ALIGN_CENTER | Jimp.VERTICAL_ALIGN_MIDDLE) img.resize(imgSize, Jimp.AUTO) } img.getBuffer(Jimp.AUTO, (err, buffer) => { if (!sizeParam && !glowOffset.length) { cache[iconCode] = { value: buffer, - timeoutID: setTimeout(function() {delete cache[iconCode]}, 600000) + timeoutID: setTimeout(function() {delete cache[iconCode]}, 1800000) } } return res.end(buffer, 'base64') @@ -351,6 +358,7 @@ module.exports = async (app, req, res) => { let result = [] if (app.offline || req.query.hasOwnProperty("noUser") || req.query.hasOwnProperty("nouser") || username == "icon") return buildIcon() + res.contentType('image/png'); request.post(app.endpoint + 'getGJUsers20.php', { form: { diff --git a/api/level.js b/api/level.js index 2d1210f..b05e868 100644 --- a/api/level.js +++ b/api/level.js @@ -2,6 +2,8 @@ const request = require('request') const fs = require('fs') const Level = require('../classes/Level.js') +function xor(str, key) { return Buffer.from(String.fromCodePoint(...str.split('').map((char, i) => char.charCodeAt(0) ^ key.charCodeAt(i % key.length)))).toString('base64') } + module.exports = async (app, req, res, api, analyze) => { if (app.offline) { @@ -28,7 +30,7 @@ module.exports = async (app, req, res, api, analyze) => { } }, async function (err, resp, body) { - if (err || !body || body == '-1') { + if (err || !body || body == '-1' || body.startsWith(" { let levelInfo = app.parseResponse(preRes[0]) let level = new Level(levelInfo, author) - if (song[2]) { level.songName = song[2] || "Unknown" level.songAuthor = song[4] || "Unknown" diff --git a/api/messages/sendMessage.js b/api/messages/sendMessage.js index 71fbdf8..e24c901 100644 --- a/api/messages/sendMessage.js +++ b/api/messages/sendMessage.js @@ -9,7 +9,7 @@ module.exports = async (app, req, res, api) => { if (!req.body.accountID) return res.status(400).send("No account ID provided!") if (!req.body.password) return res.status(400).send("No password provided!") - let subject = new Buffer(req.body.subject ? (req.body.color ? "☆" : "") + (req.body.subject.slice(0, 50)) : (req.body.color ? "☆" : "") + "No subject").toString('base64').replace(/\//g, '_').replace(/\+/g, "-") + let subject = Buffer.from(req.body.subject ? (req.body.color ? "☆" : "") + (req.body.subject.slice(0, 50)) : (req.body.color ? "☆" : "") + "No subject").toString('base64').replace(/\//g, '_').replace(/\+/g, "-") let body = xor.encrypt(req.body.message.slice(0, 300), 14251) let params = { diff --git a/api/post/postComment.js b/api/post/postComment.js index 15c0228..e854c21 100644 --- a/api/post/postComment.js +++ b/api/post/postComment.js @@ -31,7 +31,7 @@ module.exports = async (app, req, res) => { percent: 0 } - params.comment = new Buffer(req.body.comment + (req.body.color ? "☆" : "")).toString('base64').replace(/\//g, '_').replace(/\+/g, "-") + params.comment = Buffer.from(req.body.comment + (req.body.color ? "☆" : "")).toString('base64').replace(/\//g, '_').replace(/\+/g, "-") params.gjp = xor.encrypt(req.body.password, 37526) params.levelID = req.body.levelID.toString() params.accountID = req.body.accountID.toString() diff --git a/api/post/postProfileComment.js b/api/post/postProfileComment.js index 57cb506..66e1d62 100644 --- a/api/post/postProfileComment.js +++ b/api/post/postProfileComment.js @@ -20,7 +20,7 @@ module.exports = async (app, req, res) => { cType: '1' } - params.comment = new Buffer(req.body.comment.slice(0, 190) + (req.body.color ? "☆" : "")).toString('base64').replace(/\//g, '_').replace(/\+/g, "-") + params.comment = Buffer.from(req.body.comment.slice(0, 190) + (req.body.color ? "☆" : "")).toString('base64').replace(/\//g, '_').replace(/\+/g, "-") params.gjp = xor.encrypt(req.body.password, 37526) params.accountID = req.body.accountID.toString() params.userName = req.body.username diff --git a/api/profile.js b/api/profile.js index 633fbd7..53ac198 100644 --- a/api/profile.js +++ b/api/profile.js @@ -8,11 +8,11 @@ module.exports = async (app, req, res, api, getLevels) => { request.post(app.endpoint + 'getGJUsers20.php', { form: { str: getLevels || req.params.id, - secret: app.secret + secret: app.secret, } }, function (err1, res1, b1) { - let searchResult = (err1 || b1 == '-1' || !b1) ? req.params.id : app.parseResponse(b1)[16] + let searchResult = (req.query.hasOwnProperty("account") || err1 || b1 == '-1' || b1.startsWith(" { request.post(app.endpoint + 'getGJLevels21.php', { form : filters}, async function(err, resp, body) { - if (err || !body || body == '-1') return res.send("-1") + if (err || !body || body == '-1' || body.startsWith(" letter.charCodeAt()); - } - cipher(data, key) { - key = this.text2ascii(key); - data = this.text2ascii(data); - let keysize = key.length; - let input_size = data.length; - let cipher = ''; - - for (let i = 0; i < input_size; i++) { - cipher += this.chr(data[i] ^ key[i % keysize]); - } - return cipher; - } - encrypt(password, key = 37526) { - let encode = this.cipher(password, key); - encode = Buffer.from(encode).toString('base64'); - encode = encode - .replace(/\//g, '_') - .replace(/\+/g, '-'); - - return encode; - } - decrypt(gjp, key = 37526) { - let decode = gjp - .replace(/_/g, '/') - .replace(/-/g, '+'); - decode = Buffer.from(decode, 'base64').toString(); - decode = this.cipher(decode, key); - - return decode; - } -}; \ No newline at end of file + xor(str, key) { return String.fromCodePoint(...str.split('').map((char, i) => char.charCodeAt(0) ^ key.toString().charCodeAt(i % key.toString().length))) } + encrypt(str, key = 37526) { return Buffer.from(this.xor(str, key)).toString('base64').replace(/\//g, '_').replace(/\+/g, '-'); } + decrypt(str, key = 37526) { return this.xor(Buffer.from(str.replace(/\//g, '_').replace(/\+/g, '-'), 'base64').toString(), key) } +} \ No newline at end of file diff --git a/html/api.html b/html/api.html index 5a60c1f..13e76eb 100644 --- a/html/api.html +++ b/html/api.html @@ -185,9 +185,9 @@
Unlike the Geometry Dash API, both username and ID can be used to fetch a user profile.
Parameters (0)
+Parameters (1)
No parameters for this one!
+account: Forces the account ID to be used for fetching (normally Player ID is tried first)
This one isn't really part of the API, but dammit, my website my rules
Parameters (7)
+Parameters (9)
Parameters can be used to modify parts of a fetched user's icon
IDs generally correspond to their order of appearance in GD
-form: The form of the icon (cube/ship/ball/ufo/wave/robot/spider/cursed)
+form: The form of the icon (cube/ship/ball/ufo/wave/robot/spider/swing/cursed)
icon: The ID of the icon to use
col1: The ID of the primary color to use
col2: The ID of the secondary color to use
+col3: Optional color ID to overwrite the 'white' layer used by some detailed icons
glow: If the icon should have a glow/outline (0 = off, anything else = on)
-size: The size (in pixels) the icon should be, in case you don't want the default. Will always be square
+size: The size in pixels that the icon should be (always square), in case you don't want the default. "Auto" also works.
topless: Removes the glass 'dome' from generated UFOs (legacy)
noUser: Disables fetching the icon from the GD servers. Slightly faster, but comes at the cost of having to build icons from the ground up using the parameters listed above. It completely ignores the entered username and always returns the default icon