From a37125ed59ef3874eb802291c6278496f8532c34 Mon Sep 17 00:00:00 2001 From: Matt Date: Tue, 7 Dec 2021 11:06:33 -0800 Subject: [PATCH] Better error handling + general cleanup (#182) * chore: cleanup .gitignore - remove unused /typings, not using typescript - remove .next as this is not using next.js + add editors to ignore file + add exceptions for more .env files, .env.local, etc. + seperate lockfiles and add yarn lockfile * feat(package.json): add dev command * feat: better error handling 1/4 + add status codes to all of the errors. ? I understand this is similar to robtop so I didn't change any of the very cryptic responses, i.e. -1, -2, -3. * feat: cleanup unused params & add status codes * chore(servers.json): remove weird space in json * feat: final status codes * fix(leaderboard): fix comment for private server * fix(merge): merge on 7da5632 * revert: revert icon.js --- .gitignore | 12 +- api/analyze.js | 6 +- api/comments.js | 8 +- api/download.js | 8 +- api/gauntlets.js | 8 +- api/icon.js | 632 +++++++++++++++------------ api/leaderboards/accurate.js | 11 +- api/leaderboards/boomlings.js | 7 +- api/leaderboards/leaderboardLevel.js | 8 +- api/leaderboards/scores.js | 8 +- api/level.js | 6 +- api/mappacks.js | 8 +- api/messages/countMessages.js | 2 + api/messages/deleteMessage.js | 4 +- api/messages/fetchMessage.js | 4 +- api/messages/getMessages.js | 4 +- api/messages/sendMessage.js | 4 +- api/post/like.js | 2 + api/post/postComment.js | 2 + api/post/postProfileComment.js | 2 + api/profile.js | 8 +- api/search.js | 14 +- api/song.js | 7 +- index.js | 65 ++- package.json | 3 + servers.json | 1 - 26 files changed, 472 insertions(+), 372 deletions(-) diff --git a/.gitignore b/.gitignore index b81e949..c80310a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ # Ew extra + +# Package manager lockfiles package-lock.json +yarn.lock # Logs logs @@ -40,9 +43,6 @@ build/Release node_modules/ jspm_packages/ -# TypeScript v1 declaration files -typings/ - # Optional npm cache directory .npm @@ -60,6 +60,8 @@ typings/ # dotenv environment variables file .env +.env.* -# next.js build output -.next \ No newline at end of file +# Editors +.idea +.vscode \ No newline at end of file diff --git a/api/analyze.js b/api/analyze.js index 8d55832..5be9fe3 100644 --- a/api/analyze.js +++ b/api/analyze.js @@ -21,14 +21,14 @@ module.exports = async (app, req, res, level) => { const raw_data = level.data; const response_data = analyze_level(level, raw_data); - return res.send(response_data); + return res.status(200).send(response_data); } else { zlib.unzip(levelString, (err, buffer) => { - if (err) { return res.send("-2"); } + if (err) { return res.status(500).send("-2"); } const raw_data = buffer.toString(); const response_data = analyze_level(level, raw_data); - return res.send(response_data); + return res.status(200).send(response_data); }); } } diff --git a/api/comments.js b/api/comments.js index 4e56a11..8889991 100644 --- a/api/comments.js +++ b/api/comments.js @@ -1,6 +1,6 @@ module.exports = async (app, req, res) => { - if (req.offline) return res.send("-1") + if (req.offline) return res.status(500).send("-1") let count = +req.query.count || 10 if (count > 1000) count = 1000 @@ -20,14 +20,14 @@ module.exports = async (app, req, res) => { req.gdRequest(path, req.gdParams(params), function(err, resp, body) { - if (err) return res.send("-1") + if (err) return res.status(500).send("-1") comments = body.split('|') comments = comments.map(x => x.split(':')) comments = comments.map(x => x.map(x => app.parseResponse(x, "~"))) if (req.query.type == "profile") comments.filter(x => x[0][2]) else comments = comments.filter(x => x[0] && x[0][2]) - if (!comments.length) return res.send("-1") + if (!comments.length) return res.status(204).send("-1") let pages = body.split('#')[1].split(":") let lastPage = +Math.ceil(+pages[0] / +pages[2]); @@ -79,7 +79,7 @@ module.exports = async (app, req, res) => { }) - return res.send(commentArray) + return res.status(200).send(commentArray) }) } \ No newline at end of file diff --git a/api/download.js b/api/download.js index 55fe092..4a3c283 100644 --- a/api/download.js +++ b/api/download.js @@ -6,7 +6,7 @@ module.exports = async (app, req, res, api, ID, analyze) => { function rejectLevel() { if (!api) return res.redirect('search/' + req.params.id) - else return res.send("-1") + else return res.status(500).send("-1") } if (req.offline) { @@ -22,7 +22,7 @@ module.exports = async (app, req, res, api, ID, analyze) => { req.gdRequest('downloadGJLevel22', { levelID }, function (err, resp, body) { if (err) { - if (analyze && api && req.server.downloadsDisabled) return res.send("-3") + if (analyze && api && req.server.downloadsDisabled) return res.status(403).send("-3") else if (!api && levelID < 0) return res.redirect(`/?daily=${levelID * -1}`) else return rejectLevel() } @@ -69,7 +69,7 @@ module.exports = async (app, req, res, api, ID, analyze) => { if (analyze) return app.run.analyze(app, req, res, level) function sendLevel() { - if (api) return res.send(level) + if (api) return res.status(200).send(level) else return fs.readFile('./html/level.html', 'utf8', function (err, data) { let html = data; @@ -78,7 +78,7 @@ module.exports = async (app, req, res, api, ID, analyze) => { let regex = new RegExp(`\\[\\[${x.toUpperCase()}\\]\\]`, "g") html = html.replace(regex, app.clean(level[x])) }) - return res.send(html) + return res.status(200).send(html) }) } diff --git a/api/gauntlets.js b/api/gauntlets.js index 3e3b01a..5a911cc 100644 --- a/api/gauntlets.js +++ b/api/gauntlets.js @@ -3,19 +3,19 @@ let gauntletNames = ["Fire", "Ice", "Poison", "Shadow", "Lava", "Bonus", "Chaos" module.exports = async (app, req, res) => { - if (req.offline) return res.send("-1") + if (req.offline) return res.status(500).send("-1") let cached = cache[req.id] - if (app.config.cacheGauntlets && cached && cached.data && cached.indexed + 2000000 > Date.now()) return res.send(cached.data) // half hour cache + if (app.config.cacheGauntlets && cached && cached.data && cached.indexed + 2000000 > Date.now()) return res.status(200).send(cached.data) // half hour cache req.gdRequest('getGJGauntlets21', {}, function (err, resp, body) { - if (err) return res.send("-1") + if (err) return res.status(500).send("-1") let gauntlets = body.split('#')[0].split('|').map(x => app.parseResponse(x)).filter(x => x[3]) let gauntletList = gauntlets.map(x => ({ id: +x[1], name: gauntletNames[+x[1] - 1] || "Unknown", levels: x[3].split(",") })) if (app.config.cacheGauntlets) cache[req.id] = {data: gauntletList, indexed: Date.now()} - res.send(gauntletList) + res.status(200).send(gauntletList) }) diff --git a/api/icon.js b/api/icon.js index 0ce5cde..9915987 100644 --- a/api/icon.js +++ b/api/icon.js @@ -1,311 +1,389 @@ -const sharp = require('sharp'); -const Canvas = require('canvas') -const psd = require('ag-psd') +// this file is a potential candidate for worst code on github +// i advise you to turn back now +// seriously, it's not too late + +const Jimp = require('jimp'); const fs = require('fs'); -const mainPath = `../icons/` -const icons = require('../misc/icons/gameSheet.json'); -const colors = require('../misc/icons/colors.json'); -const forms = require('../misc/icons/forms.json') -const offsets = require('../misc/icons/offsets.json') -const legOffsets = require('../misc/icons/legOffsets.json') +const icons = require('../icons/gameSheet.json'); +const colors = require('../icons/colors.json'); +const forms = require('../icons/forms.json') +const offsets = require('../icons/offsets.json'); -let canvasSize = 300 -let halfCanvas = canvasSize/2 -let TRANSPARENT = {r: 0, g: 0, b: 0, alpha: 0} -let cache = {} - -let partNames = { - "1": "Primary", - "2": "Secondary", - "3": "UFO Dome", - "glow": "Glow", - "extra": "White", -} - -// convert hex to RGB let hexRegex = /^[A-Fa-f0-9]{6}$/ -function hexConvert(hex) { hex = hex.replace('#', ''); return {val: hex, r: '0x' + hex[0] + hex[1] | 0, g: '0x' + hex[2] + hex[3] | 0, b: '0x' + hex[4] + hex[5] | 0}; } - -// get path name from icon form and ID -function getIconPath(icon, formName) { - return `${mainPath}${formName}_${icon < 10 ? "0" : ""}${icon}` +function hexConvert(hex) { hex = hex.replace('#', ''); return {r: '0x' + hex[0] + hex[1] | 0, g: '0x' + hex[2] + hex[3] | 0, b: '0x' + hex[4] + hex[5] | 0}; } +function recolor(img, col) { + return img.scan(0, 0, img.bitmap.width, img.bitmap.height, function (x, y, idx) { + if (img.bitmap.data.slice(idx, idx+3).every(function(val) {return val >= 20 && val <= 255})) { // If it's not "black, i.e. we want to recolor it" + this.bitmap.data[idx] = colors[col].r / (255 / this.bitmap.data[idx]); + this.bitmap.data[idx + 1] = colors[col].g / (255 / this.bitmap.data[idx + 1]); + this.bitmap.data[idx + 2] = colors[col].b / (255 / this.bitmap.data[idx + 2]); + } + }) } -// get color from param input -function getColor(colInput, defaultCol) { - colInput = String(colInput) - let foundColor = colors[colInput] - if (foundColor) { - foundColor.val = colInput - return foundColor - } - else if (colInput.match(hexRegex)) { // custom hex code - let hexCol = hexConvert(colInput) - colors[colInput.toLowerCase()] = hexCol - return hexCol - } - else if (!foundColor && defaultCol) { - let def = colors[defaultCol] - def.val = defaultCol - return def - } -} +/* Caveat of genFileName is that if there are any falsey values in the arguments they are ignored. +This is usually a good thing though - avoid issues by not putting something like 0 instead of '0' */ +function genFileName(...args) { return args.filter(function(val) {return val}).join('_') +'_001.png' } +function fromIcons(filename) { return `./icons/${filename}` } +let cache = {}; module.exports = async (app, req, res) => { -async function buildIcon(account=[], userCode) { + function buildIcon(account=[], usercode) { - let form = forms[req.query.form] || forms["icon"] + let { form, ind } = forms[req.query.form] || {}; + form = form || 'player'; + ind = ind || 21; - let iconID = req.query.icon || account[form.index] || 1; - let col1 = getColor(req.query.col1 || account[10], "0") - let col2 = getColor(req.query.col2 || account[11], "3") - let colG = getColor(req.query.colG || req.query.colg) - let colW = getColor(req.query.colW || req.query.colw || req.query.col3) + let iconID = req.query.icon || account[ind] || 1; + let col1 = req.query.col1 || account[10] || 0; + let col2 = req.query.col2 || account[11] || 3; + let colG = req.query.colG || req.query.colg + let colW = req.query.colW || req.query.colw || req.query.col3 + let outline = req.query.glow || account[28] || "0"; - let useGlow = req.query.glow || account[28] || false; - if (useGlow && ["false", "0"].includes(useGlow)) useGlow = false - if (col1.r == 0 && col1.g == 0 && col1.b == 0 ) useGlow = true + let topless = form == "bird" && req.query.topless + let drawLegs = !(req.query.noLegs > 0) + let autoSize = req.query.size == "auto" + let sizeParam = autoSize || (req.query.size && !isNaN(req.query.size)) + if (outline == "0" || outline == "false") outline = false; - // bit of a hacky solution for glow color but whatev - let glowColor = colG || col2 - if (glowColor.r == 0 && glowColor.g == 0 && glowColor.b == 0) glowColor = col1 - if (glowColor.r == 0 && glowColor.g == 0 && glowColor.b == 0) glowColor = {r: 255, g: 255, b: 255} + if (iconID && iconID.toString().length == 1) iconID = "0" + iconID; - let psdExport = req.query.psd || false - let topless = form.name == "UFO" ? req.query.topless || false : false + function genImageName(...args) { return genFileName(form, iconID, ...args) } - let customSize = req.query.size == "auto" ? "auto" : +req.query.size || null - - let iconPath = getIconPath(iconID, form.form) - - let iconCode = `${form.name}-${iconID}-${col1.val}-${col2.val}-${colG ? colG.val : "x"}-${colW ? colW.val : "x"}-${useGlow ? 1 : 0}` - let cachable = !topless && !customSize && !psdExport - if (cachable && cache[iconCode]) return res.end(cache[iconCode].buffer) - - // default to 1 if icon ID does not exist - if (!fs.existsSync(getPartName(1).slice(1))) { // slice 1 from filename since fs reads paths differently - iconID = 1 - iconPath = getIconPath(1, form.form) - } - - // get path of icon 'part' (1: primary, 2: secondary, 3: ufo top, extra: white, glow: glow, ) - function getPartName(part, robotPart) { - let path = iconPath - if (form.legs) path += `_0${robotPart || 1}` - if (!part || part == "1") return `${path}_001.png` - else return `${path}_${part}_001.png` - } - - // recolor white parts of icon to specified color - async function recolor(img, col) { - let rawData = await img.raw().toBuffer({resolveWithObject: true}) - for (let i=0; i 0) { - rawData.data[i] = col.r / (255 / rawData.data[i]); - rawData.data[i + 1] = col.g / (255 / rawData.data[i + 1]); - rawData.data[i + 2] = col.b / (255 / rawData.data[i + 2]); - } + let icon, glow, extra; + function setBaseIcons() { + icon = genImageName(isSpecial && '01'); + glow = genImageName(isSpecial && '01', '2'); + extra = genImageName(isSpecial && '01', 'extra'); } - return sharp(rawData.data, {raw: {width: rawData.info.width, height: rawData.info.height, channels: 4, background: TRANSPARENT}}).png() - } + let isSpecial = ['robot', 'spider'].includes(form); + setBaseIcons(); - // color icon part and add to layer list - async function addLayer(part, color, legSection) { - - let leg = legSection ? legSection.leg : null - - let partName = getPartName(part, leg) - let offsetData = icons[partName.slice(mainPath.length)] - let { spriteSize, spriteOffset } = offsetData - - let builtPart = sharp(partName.slice(1)) // slice 1 from filename since sharp also reads paths differently - if (color) builtPart = await recolor(builtPart, color) - - let left = halfCanvas - Math.floor(spriteSize[0] / 2) + spriteOffset[0] - let top = halfCanvas - Math.floor(spriteSize[1] / 2) - spriteOffset[1] - - if (legSection) { - left += Math.floor(legSection.xPos) - top -= Math.floor(legSection.yPos) - // if (legSection.darken) builtPart.tint({r: 100, g: 100, b: 100}) - if (legSection.rotation) { - builtPart.rotate(legSection.rotation, {background: TRANSPARENT}) - if (part == "glow") { left--; top--; } - } - if (legSection.yScale) builtPart.resize({width: spriteSize[0], height: Math.floor(spriteSize[1] * legSection.yScale), fit: "fill"}) - if (legSection.xFlip) builtPart.flop() + if (!fs.existsSync(fromIcons(icon)) || (isSpecial && !fs.existsSync(fromIcons(genImageName('02'))))) { + iconID = '01'; + setBaseIcons(); } - let layerData = { - partName, spriteOffset, spriteSize, leg, - layerName: partNames[part], - behind: legSection && legSection.darken, - isGlow: part == "glow", - input: await builtPart.toBuffer(), - left, top - } + let ex = fromIcons(extra) + let hasExtra = fs.existsSync(ex) - if (legSection) { - if (!legLayers[legSection.leg]) legLayers[legSection.leg] = [layerData] - else legLayers[legSection.leg].push(layerData) - } - - else layers.push(layerData) - } - - // build all layers of icon segment (col1, col2, glow, extra) - async function buildFullLayer(legSection) { - let hasExtra = fs.existsSync(getPartName("extra", legSection ? legSection.leg : null).slice(1)) - - if (form.form == "bird" && !topless) await addLayer(3, null, legSection) // ufo top - await addLayer(2, col2, legSection) // secondary color - if (useGlow) await addLayer("glow", glowColor, legSection) // glow - await addLayer(1, col1, legSection) // primary color - if (hasExtra) await addLayer("extra", colW, legSection) // extra - - // if (legSection) { - // let foundLeg = legLayers[legSection.leg] - // foundLeg.forEach(x => layers.push(x)) - // } - } - - let layers = [] - let legLayers = [] - let legData = form.legs ? legOffsets[form.form] || [] : [] - let parentSize = icons[getPartName(1).slice(mainPath.length)].spriteSize - let canvas = sharp({create: {width: canvasSize, height: canvasSize, channels: 4, background: TRANSPARENT}}) - - // if (legData.length) { - // for (let i=0; i x).sort((a, b) => !!b.behind - !!a.behind).sort((a, b) => !!b.isGlow - !!a.isGlow) - - canvas.composite(layers) - - let rawData = await canvas.toBuffer({resolveWithObject: true}) - let minX = canvasSize; let maxX = 0; - let minY = canvasSize; let maxY = 0; - for (let i=0; i 0) { - if (x < minX) minX = x - if (x > maxX) maxX = x - if (y < minY) minY = y - if (y > maxY) maxY = y - } - } - - // need to make a new sharp instance so everything is merged. bit hacky but it works - let dimensions = [maxX - minX, maxY - minY] - - if (!psdExport) { - let finalIcon = sharp(rawData.data, {raw: {width: canvasSize, height: canvasSize, channels: 4}}) - .extract({left: minX, top: minY, width: dimensions[0], height: dimensions[1]}) - - if (customSize) { - let isThicc = dimensions[0] > dimensions[1] - let squareSize = req.query.size == "auto" ? (isThicc ? dimensions[0] : dimensions[1]) : Math.floor(req.query.size) - if (squareSize < 32) squareSize = 32 - if (squareSize > 256) squareSize = 256 - - // use longest side to make square - if (isThicc) finalIcon.resize({ - width: dimensions[isThicc ? 0 : 1], - height: dimensions[isThicc ? 0 : 1], - fit: "contain", - background: TRANSPARENT - }) - finalIcon.resize({width: squareSize, height: squareSize, fit: "contain", background: TRANSPARENT}) - } - finalIcon.png().toBuffer().then(x => { - res.end(x) // send file - if (cachable) { // cache for a bit - cache[iconCode] = { buffer: x, timeoutID: setTimeout(function() {delete cache[iconCode]}, 10000000) } // cache file for 3 hours - if (userCode) cache[userCode] = { buffer: x, timeoutID: setTimeout(function() {delete cache[userCode]}, 300000) } // 5 min cache for player icons - } - }) - } - - else { - let psdLayers = layers.map(x => { - let Image = Canvas.Image - let canvas = Canvas.createCanvas(...dimensions) - let ctx = canvas.getContext('2d'); - const img = new Image() - img.onload = () => { - ctx.drawImage(img, 0 + x.left - minX, 0 + x.top - minY) - } - img.onerror = err => { throw err } - img.src = x.input - return {name: x.layerName, canvas, leg: x.leg} + let cols = [col1, col2, colG, colW] + cols.forEach(col => { + if (!col) return + col = col.toString() + if (col.match(hexRegex)) colors[col.toLowerCase()] = hexConvert(col) }) - if (form.legs) { - let legLayers = [] - for (let i=1; i<=form.legs + 1; i++) legLayers.push({name: i == 1 ? "Base" : `Leg ${i}`, opened: true, children: []}) - psdLayers.forEach(x => { - legLayers[x.leg-1].children.push(x) - }) - psdLayers = legLayers.reverse() + if (!colors[col1] || isNaN(colors[col1].r)) col1 = colors[+col1] ? +col1 : 0 + if (!colors[col2] || isNaN(colors[col2].r)) col2 = colors[+col2] ? +col2 : 3 + if (!colors[colG] || isNaN(colors[colG].r)) colG = colors[+colG] ? +colG : null + if (!colors[colW] || isNaN(colors[colW].r)) colW = colors[+colW] ? +colW : null + if (colW && (!hasExtra || colW == 12)) colW = null + + if (col1 == 15 || col1 === "000000") outline = true; + + let iconCode = `${req.query.form == "cursed" ? "cursed" : form}${topless ? "top" : ""}-${iconID}-${col1}-${col2}-${colG || "x"}-${colW || "x"}-${outline ? 1 : 0}` + + if (!sizeParam && (!isSpecial || drawLegs) && cache[iconCode]) return res.end(cache[iconCode].value) + + let useExtra = false + let originalOffset = icons[icon].spriteOffset; + let minusOrigOffset = function(x, y) { return x - originalOffset[y] } + let offset = icons[glow].spriteOffset.map(minusOrigOffset); + let robotLeg1, robotLeg2, robotLeg3, robotLeg3b, robotLeg2b, robotLeg1b, robotLeg1c; + let robotOffset1, robotOffset2, robotOffset3, robotOffset1b, robotOffset2b, robotOffset3b; + let robotGlow1, robotGlow2, robotGlow3, glowOffset + let ufoTop, ufoOffset, ufoCoords, ufoSprite + let extrabit, offset2, size2; + + if (isSpecial) { + const legs = [1,2,3].map(function(val) {return genImageName(`0${val+1}`)}); + const glows = [1,2,3].map(function(val) {return genImageName(`0${val+1}`, '2')}); + robotOffset1 = icons[legs[0]].spriteOffset.map(minusOrigOffset).concat(icons[legs[0]].spriteSize); + robotOffset2 = icons[legs[1]].spriteOffset.map(minusOrigOffset).concat(icons[legs[1]].spriteSize); + robotOffset3 = icons[legs[2]].spriteOffset.map(minusOrigOffset).concat(icons[legs[2]].spriteSize); + + robotOffset1b = icons[glows[0]].spriteOffset.map(minusOrigOffset).concat(icons[glows[0]].spriteSize); + robotOffset2b = icons[glows[1]].spriteOffset.map(minusOrigOffset).concat(icons[glows[1]].spriteSize); + robotOffset3b = icons[glows[2]].spriteOffset.map(minusOrigOffset).concat(icons[glows[2]].spriteSize); + + robotLeg1 = new Jimp(fromIcons(legs[0])); robotGlow1 = new Jimp(fromIcons(glows[0])) + robotLeg2 = new Jimp(fromIcons(legs[1])); robotGlow2 = new Jimp(fromIcons(glows[1])) + robotLeg3 = new Jimp(fromIcons(legs[2])); robotGlow3 = new Jimp(fromIcons(glows[2])) + + glowOffset = offsets[form][+iconID] || [] } - - const photoshop = { - width: dimensions[0], - height: dimensions[1], - children: psdLayers - }; - - const buffer = psd.writePsdBuffer(photoshop); - return res.end(buffer) + + 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 (colW) await Jimp.read(eb).then(e => { extra = recolor(e, colW) }) + useExtra = true + } + + Jimp.read(fromIcons(icon)).then(async function (ic) { + + let iconSize = [ic.bitmap.width, ic.bitmap.height] + recolor(ic, col1) + ic.composite(glow, (iconSize[0] / 2) - (size[0] / 2) + offset[0], (iconSize[1] / 2) - (size[1] / 2) - offset[1], { mode: Jimp.BLEND_DESTINATION_OVER }) + + if (form == "bird" && !topless) { + ufoTop = genImageName('3') + ufoOffset = icons[ufoTop].spriteOffset.map(minusOrigOffset).concat(icons[ufoTop].spriteSize); + ufoCoords = [imgOff + (iconSize[0] / 2) - (ufoOffset[2] / 2) + ufoOffset[0], (iconSize[1] / 2) - (ufoOffset[3] / 2) - ufoOffset[1] + 300 - iconSize[1]] + ufoSprite = fromIcons(ufoTop) + ic.contain(iconSize[0], 300, Jimp.HORIZONTAL_ALIGN_CENTER | Jimp.VERTICAL_ALIGN_BOTTOM) + // Only add dome if there's no glow, otherwise the dome will be outlined as well + if (!outline) ic.composite(await Jimp.read(ufoSprite), ufoCoords[0], ufoCoords[1], {mode: Jimp.BLEND_DESTINATION_OVER}) + } + + if (drawLegs && (form == "robot" || req.query.form == "cursed")) { + + ic.contain(iconSize[0], 300, Jimp.HORIZONTAL_ALIGN_CENTER | Jimp.VERTICAL_ALIGN_TOP) + ic.contain(iconSize[0] + 200, 300, Jimp.HORIZONTAL_ALIGN_CENTER | Jimp.VERTICAL_ALIGN_TOP) + + await Jimp.read(new Jimp(robotGlow1)).then(rob => { + rob.rotate(-45) + robotGlow1 = recolor(rob, col2) + }) + + await Jimp.read(new Jimp(robotGlow2)).then(rob => { + rob.rotate(45) + robotGlow2 = recolor(rob, col2) + }) + + await Jimp.read(new Jimp(robotGlow3)).then(rob => { + robotGlow3 = recolor(rob, col2) + }) + + await Jimp.read(new Jimp(robotLeg1)).then(rob => { + rob.rotate(-45) + recolor(rob, col1) + rob.composite(robotGlow1, (robotOffset1[2] - robotOffset1b[2]) + (glowOffset[0] || 1), ((robotOffset1[3] - robotOffset1b[3]) / 2) + (glowOffset[1] || 0), { mode: Jimp.BLEND_DESTINATION_OVER }) + robotLeg1 = rob + }) + + await Jimp.read(new Jimp(robotLeg2)).then(rob => { + rob.rotate(45) + recolor(rob, col1) + rob.composite(robotGlow2, ((robotOffset2[2] - robotOffset2b[2]) / 4) + (glowOffset[4] || 0), ((robotOffset2[3] - robotOffset2b[3]) / 2) + (glowOffset[5] || 0), { mode: Jimp.BLEND_DESTINATION_OVER }) + robotLeg2 = rob + }) + + await Jimp.read(new Jimp(robotLeg2)).then(rob => { + robotLeg2b = rob.color([{ apply: 'darken', params: [20] }]).rotate(-5) + }) + + await Jimp.read(new Jimp(robotLeg3)).then(rob => { + recolor(rob, col1) + rob.composite(robotGlow3, ((robotOffset3[2] - robotOffset3b[2]) / 2) + (glowOffset[2] || 0), ((robotOffset3[3] - robotOffset3b[3]) / 2) + (glowOffset[3] || 0), { mode: Jimp.BLEND_DESTINATION_OVER }) + robotLeg3 = rob + }) + + await Jimp.read(new Jimp(robotLeg3)).then(rob => { + robotLeg3b = rob.color([{ apply: 'darken', params: [10] }]) + }) + + ic.composite(robotLeg2b, 100 + (iconSize[0] / 2) - (robotOffset2[2]) + robotOffset2[0] - 31, (iconSize[1] / 2) - (robotOffset2[3]) - robotOffset2[1] + 73) + ic.composite(robotLeg3b, 100 + (iconSize[0] / 2) - (robotOffset3[2]) + robotOffset3[0] + 20, (iconSize[1] / 2) - (robotOffset3[3]) - robotOffset3[1] + 78) + ic.composite(robotLeg2, 100 + (iconSize[0] / 2) - (robotOffset2[2]) + robotOffset2[0] - 20, (iconSize[1] / 2) - (robotOffset2[3]) - robotOffset2[1] + 73) + ic.composite(robotLeg3, 100 + (iconSize[0] / 2) - (robotOffset3[2]) + robotOffset3[0] + 40, (iconSize[1] / 2) - (robotOffset3[3]) - robotOffset3[1] + 78) + ic.composite(robotLeg1, 100 + (iconSize[0] / 2) - (robotOffset1[2]) + robotOffset1[0] - 20, (iconSize[1] / 2) - (robotOffset1[3]) - robotOffset1[1] + 50) + + } + + else if (drawLegs && form == "spider") { + + let spiderBody; + ic.contain(iconSize[0], 300, Jimp.HORIZONTAL_ALIGN_CENTER | Jimp.VERTICAL_ALIGN_TOP) + ic.contain(iconSize[0] + 200, 300, Jimp.HORIZONTAL_ALIGN_CENTER | Jimp.VERTICAL_ALIGN_TOP) + + if (iconID == "07") { + robotOffset2[2] -= 10 + robotOffset2[1] += 12 + robotOffset1b[3] -= 105 + robotOffset2b[3] -= 150 + robotOffset2b[2] -= 60 + } + + if (iconID == "16") { + robotOffset1b[3] -= 100 + robotOffset2b[3] -= 200 + robotOffset2b[2] -= 30 + } + + await Jimp.read(new Jimp(robotGlow1)).then(rob => { + if (robotGlow1.bitmap.width < 10) robotGlow1.opacity(0) + else robotGlow1 = recolor(rob, col2) + }) + + await Jimp.read(new Jimp(robotGlow2)).then(rob => { + robotGlow2 = recolor(rob, col2) + }) + + await Jimp.read(new Jimp(robotGlow3)).then(rob => { + robotGlow3 = recolor(rob, col2) + }) + + await Jimp.read(new Jimp(robotLeg1)).then(rob => { + recolor(rob, col1) + rob.composite(robotGlow1, ((robotOffset1[2] - robotOffset1b[2]) / 2) + (glowOffset[2] || 0), ((robotOffset1[3] - robotOffset1b[3]) / 4) + (glowOffset[3] || 0), { mode: Jimp.BLEND_DESTINATION_OVER }) + robotLeg1 = rob + }) + + await Jimp.read(new Jimp(robotLeg2)).then(rob => { + recolor(rob, col1) + rob.composite(robotGlow2, ((robotOffset2[2] - robotOffset2b[2]) / 6) + (glowOffset[0] || 0), ((robotOffset2[3] - robotOffset2b[3]) / 6) + (glowOffset[1] || 0), { mode: Jimp.BLEND_DESTINATION_OVER }) + rob.rotate(-40) + robotLeg2 = rob + }) + + await Jimp.read(new Jimp(robotLeg1)).then(rob => { + robotLeg1b = rob.color([{ apply: 'darken', params: [20] }]) + }) + + await Jimp.read(new Jimp(robotLeg1b)).then(rob => { + robotLeg1c = rob.mirror(true, false) + }) + + await Jimp.read(new Jimp(robotLeg3)).then(rob => { + recolor(rob, col1) + rob.composite(robotGlow3, ((robotOffset3[2] - robotOffset3b[2]) / 2) + (glowOffset[4] || 0), ((robotOffset3[3] - robotOffset3b[3]) / 2) + (glowOffset[5] || 0), { mode: Jimp.BLEND_DESTINATION_OVER }) + robotLeg3 = rob + }) + + await Jimp.read(new Jimp(ic)).then(rob => { + spiderBody = rob + }) + + ic.composite(robotLeg3, 100 + (iconSize[0] / 2) - (robotOffset3[2]) + (robotOffset3[0]), (iconSize[1] / 2) - (robotOffset2[3]) - robotOffset2[1] + 77) + ic.composite(robotLeg1b, 100 + (iconSize[0] / 2) - (robotOffset1[2]) + robotOffset1[0] + 35, (iconSize[1] / 2) - (robotOffset1[3]) - robotOffset1[1] + 70) + ic.composite(robotLeg1c, 100 + (iconSize[0] / 2) - (robotOffset1[2]) + robotOffset1[0] + 75, (iconSize[1] / 2) - (robotOffset1[3]) - robotOffset1[1] + 70) + // ^ BELOW + ic.composite(spiderBody, 0, 0) + // v ABOVE + ic.composite(robotLeg2, 100 + (iconSize[0] / 2) - (robotOffset2[2]) + robotOffset2[0] - 60, (iconSize[1] / 2) - (robotOffset2[3]) - robotOffset2[1] + 75) + ic.composite(robotLeg1, 100 + (iconSize[0] / 2) - (robotOffset1[2]) + robotOffset1[0] + 7, (iconSize[1] / 2) - (robotOffset1[3]) - robotOffset1[1] + 70) + } + + // every now and then jimp does a fucky wucky uwu and this line errors. seems to be an issue with the lib itself :v + try { if (useExtra) ic.composite(extra, imgOff + (iconSize[0] / 2) - (size2[0] / 2) + offset2[0], (iconSize[1] / 2) - (size2[1] / 2) - offset2[1] + (form == "bird" && !req.query.topless ? 300 - iconSize[1] : 0)) } + catch(e) {} + + let finalSize = [ic.bitmap.width, ic.bitmap.height] + + function finish(img) { + img.autocrop(0.01, false) + if (form == "swing") img.resize(120, 111) + if (img.bitmap.height == 300) ic.autocrop(1, false) + if (sizeParam) { + 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 (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 && drawLegs) { + cache[iconCode] = { value: buffer, timeoutID: setTimeout(function() {delete cache[iconCode]}, 10000000) } // 3 hour cache + if (usercode) cache[usercode] = { value: buffer, timeoutID: setTimeout(function() {delete cache[usercode]}, 300000) } // 5 min cache for player icons + } + return res.end(buffer, 'base64') + }) + } + + if (!outline) return finish(ic) + + else { + + ic.getBuffer(Jimp.AUTO, function (err, buff) { + + const Canvas = require('canvas') + , Image = Canvas.Image + , canvas = Canvas.createCanvas(finalSize[0] + 10, finalSize[1] + 10) + , ctx = canvas.getContext('2d'); + + if (!colG) colG = (col2 == 15 || col2 == "000000" ? col1 : col2) + if (colG == 15 || colG == "000000") colG = 12 + + const img = new Image() + img.onload = () => { + var dArr = [-1, -1, 0, -1, 1, -1, -1, 0, 1, 0, -1, 1, 0, 1, 1, 1], // offset array + s = 2, i = 0, x = canvas.width / 2 - finalSize[0] / 2, y = canvas.height / 2 - finalSize[1] / 2; + + for (; i < dArr.length; i += 2) ctx.drawImage(img, x + dArr[i] * s, y + dArr[i + 1] * s); + + ctx.globalCompositeOperation = "source-in"; + ctx.fillStyle = `rgba(${colors[colG].r}, ${colors[colG].g}, ${colors[colG].b}, 1})`; + ctx.fillRect(0, 0, canvas.width, canvas.height); + ctx.globalCompositeOperation = "source-over"; + ctx.imageSmoothingEnabled = false; + + // Add UFO top last so it doesn't get glow'd + if (form == "bird" && !topless) { + const dome = new Image() + dome.src = ufoSprite + ctx.drawImage(dome, ufoCoords[0]+5, ufoCoords[1]+5) + } + + ctx.drawImage(img, x, y) + + } + + img.onerror = err => { throw err } + img.src = buff + + Jimp.read(canvas.toBuffer()).then(b => { + return finish(b) + }) + }) + } + }) + }) } - -} + let username = req.params.text + let userCode; + res.contentType('image/png'); -// ==================================== // + if (req.offline || req.query.hasOwnProperty("noUser") || req.query.hasOwnProperty("nouser") || username == "icon") return buildIcon() -// OLD CODE IS BEING USED FOR ROBOTS AND SPIDERS -let formCheck = forms[req.query.form] -if (formCheck && formCheck.legs) return app.run.icon_old(app, req, res) + else if (app.config.cachePlayerIcons && !Object.keys(req.query).filter(x => !["form", "forceGD"].includes(x)).length) { + userCode = `${req.id}u-${username.toLowerCase()}-${forms[req.query.form] ? req.query.form : 'cube'}` + if (cache[userCode]) return res.end(cache[userCode].value) + } -let username = req.params.text -let userCode; -res.contentType('image/png'); + let accountMode = !req.query.hasOwnProperty("player") && Number(req.params.id) + let foundID = app.userCache(req.id, username) + let skipRequest = accountMode || foundID + let forceGD = req.query.hasOwnProperty("forceGD") -if (req.offline || req.query.hasOwnProperty("noUser") || req.query.hasOwnProperty("nouser") || username == "icon") return buildIcon() + // skip request by causing fake error lmao + req.gdRequest(skipRequest ? "" : 'getGJUsers20', skipRequest ? {} : req.gdParams({ str: username, forceGD }, !forceGD), function (err1, res1, body1) { -else if (app.config.cachePlayerIcons && !Object.keys(req.query).filter(x => !["form", "forceGD"].includes(x)).length) { - userCode = `${req.id}u-${username.toLowerCase()}-${forms[req.query.form] ? req.query.form : 'cube'}` - if (cache[userCode]) return res.end(cache[userCode].value) -} + let result = foundID ? foundID[0] : (accountMode || err1) ? username : app.parseResponse(body1)[16]; -let accountMode = !req.query.hasOwnProperty("player") && Number(req.params.id) -let foundID = app.userCache(req.id, username) -let skipRequest = accountMode || foundID -let forceGD = req.query.hasOwnProperty("forceGD") + req.gdRequest('getGJUserInfo20', req.gdParams({ targetAccountID: result, forceGD }, !forceGD), function (err2, res2, body2) { -// skip request by causing fake error lmao -req.gdRequest(skipRequest ? "" : 'getGJUsers20', skipRequest ? {} : req.gdParams({ str: username, forceGD }, !forceGD), function (err1, res1, body1) { + if (err2) return buildIcon(); + let iconData = app.parseResponse(body2) + if (!foundID && !forceGD) app.userCache(req.id, iconData[16], iconData[2], iconData[1]) + return buildIcon(iconData, userCode); - let result = foundID ? foundID[0] : (accountMode || err1) ? username : app.parseResponse(body1)[16]; - - req.gdRequest('getGJUserInfo20', req.gdParams({ targetAccountID: result, forceGD }, !forceGD), function (err2, res2, body2) { - - if (err2) return buildIcon(); - let iconData = app.parseResponse(body2) - if (!foundID && !forceGD) app.userCache(req.id, iconData[16], iconData[2], iconData[1]) - return buildIcon(iconData, userCode); - - }) -}); + }) + }); } \ No newline at end of file diff --git a/api/leaderboards/accurate.js b/api/leaderboards/accurate.js index 108d3ac..2624c4b 100644 --- a/api/leaderboards/accurate.js +++ b/api/leaderboards/accurate.js @@ -9,8 +9,9 @@ let caches = [{"stars": null, "coins": null, "demons": null, "diamonds": null}, module.exports = async (app, req, res, post) => { - if (req.isGDPS) return res.send("-2") - if (!app.sheetsKey) return res.send([]) + // Accurate leaderboard returns 418 because private servers do not use. + if (req.isGDPS) return res.status(418).send("-2") + if (!app.sheetsKey) return res.status(500).send([]) let gdMode = post || req.query.hasOwnProperty("gd") let modMode = !gdMode && req.query.hasOwnProperty("mod") let cache = caches[gdMode ? 2 : modMode ? 1 : 0] @@ -18,7 +19,7 @@ module.exports = async (app, req, res, post) => { let type = req.query.type ? req.query.type.toLowerCase() : 'stars' if (type == "usercoins") type = "coins" if (!indexes.includes(type)) type = "stars" - if (lastIndex[modMode ? 1 : 0][type] + 600000 > Date.now() && cache[type]) return res.send(gdMode ? cache[type] : JSON.parse(cache[type])) // 10 min cache + if (lastIndex[modMode ? 1 : 0][type] + 600000 > Date.now() && cache[type]) return res.status(200).send(gdMode ? cache[type] : JSON.parse(cache[type])) // 10 min cache sheet.useApiKey(app.sheetsKey) sheet.loadInfo().then(async () => { @@ -29,7 +30,7 @@ module.exports = async (app, req, res, post) => { if (modMode) cellIndex += indexes.length let cell = tab.getCell(1, cellIndex).value - if (!cell || typeof cell != "string" || cell.startsWith("GoogleSpreadsheetFormulaError")) { console.log("Spreadsheet Error:"); console.log(cell); return res.send("-1") } + if (!cell || typeof cell != "string" || cell.startsWith("GoogleSpreadsheetFormulaError")) { console.log("Spreadsheet Error:"); console.log(cell); return res.status(500).send("-1") } let leaderboard = JSON.parse(cell.replace(/~( |$)/g, "")) let gdFormatting = "" @@ -40,7 +41,7 @@ module.exports = async (app, req, res, post) => { caches[modMode ? 1 : 0][type] = JSON.stringify(leaderboard) caches[2][type] = gdFormatting lastIndex[modMode ? 1 : 0][type] = Date.now() - return res.send(gdMode ? gdFormatting : leaderboard) + return res.status(200).send(gdMode ? gdFormatting : leaderboard) }) } \ No newline at end of file diff --git a/api/leaderboards/boomlings.js b/api/leaderboards/boomlings.js index 1c57f27..5715e2a 100644 --- a/api/leaderboards/boomlings.js +++ b/api/leaderboards/boomlings.js @@ -2,12 +2,13 @@ const request = require('request') module.exports = async (app, req, res) => { - if (req.isGDPS) return res.send("0") + // Accurate leaderboard returns 418 because Private servers do not use. + if (req.isGDPS) return res.status(418).send("0") request.post('http://robtopgames.com/Boomlings/get_scores.php', { form : { secret: app.config.params.secret || "Wmfd2893gb7", name: "Player" } }, function(err, resp, body) { - if (err || !body || body == 0) return res.send("0") + if (err || !body || body == 0) return res.status(500).send("0") let info = body.split(" ").filter(x => x.includes(";")) let users = [] @@ -36,7 +37,7 @@ module.exports = async (app, req, res) => { users.push(user) }) - return res.send(users) + return res.status(200).send(users) }) } \ No newline at end of file diff --git a/api/leaderboards/leaderboardLevel.js b/api/leaderboards/leaderboardLevel.js index bda8a8d..9b880a1 100644 --- a/api/leaderboards/leaderboardLevel.js +++ b/api/leaderboards/leaderboardLevel.js @@ -1,6 +1,6 @@ module.exports = async (app, req, res) => { - if (req.offline) return res.send("-1") + if (req.offline) return res.status(500).send("-1") let amount = 100; let count = req.query.count ? parseInt(req.query.count) : null @@ -18,9 +18,9 @@ module.exports = async (app, req, res) => { req.gdRequest('getGJLevelScores211', params, function(err, resp, body) { - if (err) return res.send({error: true, lastWorked: app.timeSince(req.id)}) + if (err) return res.status(500).send({error: true, lastWorked: app.timeSince(req.id)}) scores = body.split('|').map(x => app.parseResponse(x)).filter(x => x[1]) - if (!scores.length) return res.send([]) + if (!scores.length) return res.status(500).send([]) else app.trackSuccess(req.id) scores.forEach(x => { @@ -43,7 +43,7 @@ module.exports = async (app, req, res) => { app.userCache(req.id, x.accountID, x.playerID, x.username) }) - return res.send(scores.slice(0, amount)) + return res.status(200).send(scores.slice(0, amount)) }) } \ No newline at end of file diff --git a/api/leaderboards/scores.js b/api/leaderboards/scores.js index 39148e5..7d6a6a9 100644 --- a/api/leaderboards/scores.js +++ b/api/leaderboards/scores.js @@ -1,6 +1,6 @@ module.exports = async (app, req, res) => { - if (req.offline) return res.send("-1") + if (req.offline) return res.status(500).send("-1") let amount = 100; let count = req.query.count ? parseInt(req.query.count) : null @@ -16,9 +16,9 @@ module.exports = async (app, req, res) => { req.gdRequest('getGJScores20', params, function(err, resp, body) { - if (err) return res.send("-1") + if (err) return res.status(500).send("-1") scores = body.split('|').map(x => app.parseResponse(x)).filter(x => x[1]) - if (!scores.length) return res.send("-1") + if (!scores.length) return res.status(500).send("-1") scores.forEach(x => { let keys = Object.keys(x) @@ -42,6 +42,6 @@ module.exports = async (app, req, res) => { keys.forEach(k => delete x[k]) app.userCache(req.id, x.accountID, x.playerID, x.username) }) - return res.send(scores) + return res.status(200).send(scores) }) } \ No newline at end of file diff --git a/api/level.js b/api/level.js index ad138a0..c97a45c 100644 --- a/api/level.js +++ b/api/level.js @@ -6,7 +6,7 @@ module.exports = async (app, req, res, api, analyze) => { function rejectLevel() { if (!api) return res.redirect('search/' + req.params.id) - else return res.send("-1") + else return res.status(500).send("-1") } if (req.offline) return rejectLevel() @@ -37,7 +37,7 @@ module.exports = async (app, req, res, api, analyze) => { function sendLevel() { - if (api) return res.send(level) + if (api) return res.status(200).send(level) else return fs.readFile('./html/level.html', 'utf8', function (err, data) { let html = data; @@ -50,7 +50,7 @@ module.exports = async (app, req, res, api, analyze) => { }) if (req.server.downloadsDisabled) html = html.replace('id="additional" class="', 'id="additional" class="downloadDisabled ') .replace('analyzeBtn"', 'analyzeBtn" style="filter: opacity(30%)"') - return res.send(html) + return res.status(200).send(html) }) } diff --git a/api/mappacks.js b/api/mappacks.js index 7477b1c..eca93f4 100644 --- a/api/mappacks.js +++ b/api/mappacks.js @@ -3,17 +3,17 @@ let cache = {} module.exports = async (app, req, res) => { - if (req.offline) return res.send("-1") + if (req.offline) return res.status(500).send("-1") let cached = cache[req.id] - if (app.config.cacheMapPacks && cached && cached.data && cached.indexed + 5000000 > Date.now()) return res.send(cached.data) // 1.5 hour cache + if (app.config.cacheMapPacks && cached && cached.data && cached.indexed + 5000000 > Date.now()) return res.status(200).send(cached.data) // 1.5 hour cache let params = { count: 250, page: 0 } let packs = [] function mapPackLoop() { req.gdRequest('getGJMapPacks21', params, function (err, resp, body) { - if (err) return res.send("-1") + if (err) return res.status(500).send("-1") let newPacks = body.split('#')[0].split('|').map(x => app.parseResponse(x)).filter(x => x[2]) packs = packs.concat(newPacks) @@ -36,7 +36,7 @@ module.exports = async (app, req, res) => { })) if (app.config.cacheMapPacks) cache[req.id] = {data: mappacks, indexed: Date.now()} - return res.send(mappacks) + return res.status(200).send(mappacks) }) } mapPackLoop() diff --git a/api/messages/countMessages.js b/api/messages/countMessages.js index e98ac53..e075895 100644 --- a/api/messages/countMessages.js +++ b/api/messages/countMessages.js @@ -1,5 +1,7 @@ module.exports = async (app, req, res) => { + if (req.method !== 'POST') return res.status(405).send("Method not allowed.") + 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!") diff --git a/api/messages/deleteMessage.js b/api/messages/deleteMessage.js index 693a5bb..87ac65c 100644 --- a/api/messages/deleteMessage.js +++ b/api/messages/deleteMessage.js @@ -1,4 +1,6 @@ -module.exports = async (app, req, res, api) => { +module.exports = async (app, req, res) => { + + if (req.method !== 'POST') return res.status(405).send("Method not allowed.") 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!") diff --git a/api/messages/fetchMessage.js b/api/messages/fetchMessage.js index b5ff38a..86d5c57 100644 --- a/api/messages/fetchMessage.js +++ b/api/messages/fetchMessage.js @@ -1,4 +1,6 @@ -module.exports = async (app, req, res, api) => { +module.exports = async (app, req, res) => { + + if (req.method !== 'POST') return res.status(405).send("Method not allowed.") 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!") diff --git a/api/messages/getMessages.js b/api/messages/getMessages.js index 3a78a6e..ce4c5f6 100644 --- a/api/messages/getMessages.js +++ b/api/messages/getMessages.js @@ -1,4 +1,6 @@ -module.exports = async (app, req, res, api) => { +module.exports = async (app, req, res) => { + + if (req.method !== 'POST') return res.status(405).send("Method not allowed.") if (req.body.count) return app.run.countMessages(app, req, res) if (!req.body.accountID) return res.status(400).send("No account ID provided!") diff --git a/api/messages/sendMessage.js b/api/messages/sendMessage.js index 49eb4ec..2bccb33 100644 --- a/api/messages/sendMessage.js +++ b/api/messages/sendMessage.js @@ -1,4 +1,6 @@ -module.exports = async (app, req, res, api) => { +module.exports = async (app, req, res) => { + + if (req.method !== 'POST') return res.status(405).send("Method not allowed.") if (!req.body.targetID) return res.status(400).send("No target ID provided!") if (!req.body.message) return res.status(400).send("No message provided!") diff --git a/api/post/like.js b/api/post/like.js index a3b783d..75f7946 100644 --- a/api/post/like.js +++ b/api/post/like.js @@ -3,6 +3,8 @@ function sha1(data) { return crypto.createHash("sha1").update(data, "binary").di module.exports = async (app, req, res) => { + if (req.method !== 'POST') return res.status(405).send("Method not allowed.") + if (!req.body.ID) return res.status(400).send("No ID provided!") 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!") diff --git a/api/post/postComment.js b/api/post/postComment.js index f617ef5..3a5e8ad 100644 --- a/api/post/postComment.js +++ b/api/post/postComment.js @@ -11,6 +11,8 @@ function getTime(time) { module.exports = async (app, req, res) => { + if (req.method !== 'POST') return res.status(405).send("Method not allowed.") + if (!req.body.comment) return res.status(400).send("No comment provided!") if (!req.body.username) return res.status(400).send("No username provided!") if (!req.body.levelID) return res.status(400).send("No level ID provided!") diff --git a/api/post/postProfileComment.js b/api/post/postProfileComment.js index a2a8d2b..e4efef4 100644 --- a/api/post/postProfileComment.js +++ b/api/post/postProfileComment.js @@ -3,6 +3,8 @@ function sha1(data) { return crypto.createHash("sha1").update(data, "binary").di module.exports = async (app, req, res) => { + if (req.method !== 'POST') return res.status(405).send("Method not allowed.") + if (!req.body.comment) return res.status(400).send("No comment provided!") if (!req.body.username) return res.status(400).send("No username provided!") if (!req.body.accountID) return res.status(400).send("No account ID provided!") diff --git a/api/profile.js b/api/profile.js index 55e0589..30ce2f1 100644 --- a/api/profile.js +++ b/api/profile.js @@ -4,7 +4,7 @@ module.exports = async (app, req, res, api, getLevels) => { if (req.offline) { if (!api) return res.redirect('/search/' + req.params.id) - else return res.send("-1") + else return res.status(500).send("-1") } let username = getLevels || req.params.id @@ -42,7 +42,7 @@ module.exports = async (app, req, res, api, getLevels) => { if (err2 || dumbGDPSError) { if (!api) return res.redirect('/search/' + req.params.id) - else return res.send("-1") + else return res.status(500).send("-1") } if (!foundID) app.userCache(req.id, account[16], account[2], account[1]) @@ -78,7 +78,7 @@ module.exports = async (app, req, res, api, getLevels) => { glow: account[28] == "1", } - if (api) return res.send(userData) + if (api) return res.status(200).send(userData) else fs.readFile('./html/profile.html', 'utf8', function(err, data) { let html = data; @@ -87,7 +87,7 @@ module.exports = async (app, req, res, api, getLevels) => { let regex = new RegExp(`\\[\\[${x.toUpperCase()}\\]\\]`, "g") html = html.replace(regex, app.clean(userData[x])) }) - return res.send(html) + return res.status(200).send(html) }) }) diff --git a/api/search.js b/api/search.js index 74591cc..6bcd36d 100644 --- a/api/search.js +++ b/api/search.js @@ -5,17 +5,17 @@ let demonList = {} module.exports = async (app, req, res) => { - if (req.offline) return res.send(req.query.hasOwnProperty("err") ? "err" : "-1") + if (req.offline) return res.status(500).send(req.query.hasOwnProperty("err") ? "err" : "-1") let demonMode = req.query.hasOwnProperty("demonlist") || req.query.hasOwnProperty("demonList") || req.query.type == "demonlist" || req.query.type == "demonList" if (demonMode) { - if (!req.server.demonList) return res.send('-1') + if (!req.server.demonList) return res.status(400).send('-1') let dList = demonList[req.id] if (!dList || !dList.list.length || dList.lastUpdated + 600000 < Date.now()) { // 10 minute cache return request.get(req.server.demonList + 'api/v2/demons/listed/?limit=100', function (err1, resp1, list1) { - if (err1) return res.send("-1") + if (err1) return res.status(500).send("-1") else return request.get(req.server.demonList + 'api/v2/demons/listed/?limit=100&after=100', function (err2, resp2, list2) { - if (err2) return res.send("-1") + if (err2) return res.status(500).send("-1") demonList[req.id] = {list: JSON.parse(list1).concat(JSON.parse(list2)).map(x => String(x.level_id)), lastUpdated: Date.now()} return app.run.search(app, req, res) }) @@ -85,7 +85,7 @@ module.exports = async (app, req, res) => { filters.str = demonMode ? demonList[req.id].list : filters.str.split(",") listSize = filters.str.length filters.str = filters.str.slice(filters.page*amount, filters.page*amount + amount) - if (!filters.str.length) return res.send("-1") + if (!filters.str.length) return res.status(400).send("-1") filters.str = filters.str.map(x => String(Number(x) + (+req.query.l || 0))).join() filters.page = 0 } @@ -95,7 +95,7 @@ module.exports = async (app, req, res) => { req.gdRequest('getGJLevels21', req.gdParams(filters), function(err, resp, body) { - if (err) return res.send("-1") + if (err) return res.status(500).send("-1") let splitBody = body.split('#') let preRes = splitBody[0].split('|') let authorList = {} @@ -154,7 +154,7 @@ module.exports = async (app, req, res) => { }) if (filters.type == 10) parsedLevels = parsedLevels.slice((+filters.page) * amount, (+filters.page + 1) * amount) - return res.send(parsedLevels) + return res.status(200).send(parsedLevels) }) } \ No newline at end of file diff --git a/api/song.js b/api/song.js index c283290..3668ae3 100644 --- a/api/song.js +++ b/api/song.js @@ -4,14 +4,15 @@ module.exports = async (app, req, res) => { // temporary solution until song api is re-enabled - if (req.offline) return res.send('-1') + if (req.offline) return res.status(500).send('-1') let songID = req.params.song req.gdRequest('getGJSongInfo', {songID: songID}, function(err, resp, body) { - if (err) return res.send('-1') + if (err) return res.status(400).send('-1') else if (body < 0) return res.send(false) request.get('https://www.newgrounds.com/audio/listen/' + songID, function(err2, resp2, song) { - return res.send(resp2.statusCode == 200) + console.log(resp2.statusCode) + return res.status(200).send(resp2.statusCode == 200) }) }) } diff --git a/index.js b/index.js index 68af76c..dad4dc7 100644 --- a/index.js +++ b/index.js @@ -184,8 +184,8 @@ app.clean = function(text) {if (!text || typeof text != "string") return text; e app.use('/assets', express.static(__dirname + '/assets', {maxAge: "7d"})); app.use('/assets/css', express.static(__dirname + '/assets/css')); // override maxAge -app.get("/sizecheck.js", function(req, res) { res.sendFile(__dirname + "/misc/sizecheck.js") }) -app.get("/dragscroll.js", function(req, res) { res.sendFile(__dirname + "/misc/dragscroll.js") }) +app.get("/sizecheck.js", function(req, res) { res.status(200).sendFile(__dirname + "/misc/sizecheck.js") }) +app.get("/dragscroll.js", function(req, res) { res.status(200).sendFile(__dirname + "/misc/dragscroll.js") }) app.get("/assets/:dir*?", function(req, res) { let main = (req.params.dir || "").toLowerCase() @@ -193,10 +193,10 @@ app.get("/assets/:dir*?", function(req, res) { if (dir.includes('.') || !req.path.endsWith("/")) { if (!req.params[0]) main = "" - if (req.params.dir == "deatheffects" || req.params.dir == "trails") return res.sendFile(__dirname + "/assets/deatheffects/0.png") - else if (req.params.dir == "gdps" && req.params[0].endsWith("_icon.png")) return res.sendFile(__dirname + "/assets/gdps/unknown_icon.png") - else if (req.params.dir == "gdps" && req.params[0].endsWith("_logo.png")) return res.sendFile(__dirname + "/assets/gdps/unknown_logo.png") - return res.send(`

Looks like this file doesn't exist ¯\\_(ツ)_/¯
View directory listing for /assets/${main}

`) + if (req.params.dir == "deatheffects" || req.params.dir == "trails") return res.status(200).sendFile(__dirname + "/assets/deatheffects/0.png") + else if (req.params.dir == "gdps" && req.params[0].endsWith("_icon.png")) return res.status(200).sendFile(__dirname + "/assets/gdps/unknown_icon.png") + else if (req.params.dir == "gdps" && req.params[0].endsWith("_logo.png")) return res.status(200).sendFile(__dirname + "/assets/gdps/unknown_logo.png") + return res.status(404).send(`

Looks like this file doesn't exist ¯\\_(ツ)_/¯
View directory listing for /assets/${main}

`) } let path = `./assets/${dir}` @@ -205,7 +205,7 @@ app.get("/assets/:dir*?", function(req, res) { assetPage = fs.readFileSync('./html/assets.html', 'utf8') let assetData = JSON.stringify({files: files.filter(x => x.includes('.')), directories: files.filter(x => !x.includes('.'))}) - res.send(assetPage.replace('{NAME}', dir || "assets").replace('{DATA}', assetData)) + res.status(200).send(assetPage.replace('{NAME}', dir || "assets").replace('{DATA}', assetData)) }) @@ -230,7 +230,7 @@ let downloadDisabled = ['daily', 'weekly'] let gdpsHide = ['achievements', 'messages'] app.get("/", function(req, res) { - if (req.query.hasOwnProperty("offline") || (req.offline && !req.query.hasOwnProperty("home"))) res.sendFile(__dirname + "/html/offline.html") + if (req.query.hasOwnProperty("offline") || (req.offline && !req.query.hasOwnProperty("home"))) res.status(200).sendFile(__dirname + "/html/offline.html") else { fs.readFile('./html/home.html', 'utf8', function (err, data) { let html = data; @@ -249,26 +249,26 @@ app.get("/", function(req, res) { html = html.replace('id="dl" style="display: none', 'style="display: block') .replace('No active daily level!', '[Blocked by RobTop]') } - return res.send(html) + return res.status(200).send(html) }) } }) -app.get("/achievements", function(req, res) { res.sendFile(__dirname + "/html/achievements.html") }) -app.get("/analyze/:id", function(req, res) { res.sendFile(__dirname + "/html/analyze.html") }) -app.get("/api", function(req, res) { res.sendFile(__dirname + "/html/api.html") }) -app.get("/boomlings", function(req, res) { res.sendFile(__dirname + "/html/boomlings.html") }) -app.get("/comments/:id", function(req, res) { res.sendFile(__dirname + "/html/comments.html") }) -app.get("/demon/:id", function(req, res) { res.sendFile(__dirname + "/html/demon.html") }) -app.get("/gauntlets", function(req, res) { res.sendFile(__dirname + "/html/gauntlets.html") }) -app.get("/gdps", function(req, res) { res.sendFile(__dirname + "/html/gdps.html") }) -app.get("/iconkit", function(req, res) { res.sendFile(__dirname + "/html/iconkit.html") }) -app.get("/leaderboard", function(req, res) { res.sendFile(__dirname + "/html/leaderboard.html") }) -app.get("/leaderboard/:text", function(req, res) { res.sendFile(__dirname + "/html/levelboard.html") }) -app.get("/mappacks", function(req, res) { res.sendFile(__dirname + "/html/mappacks.html") }) -app.get("/messages", function(req, res) { res.sendFile(__dirname + "/html/messages.html") }) -app.get("/search", function(req, res) { res.sendFile(__dirname + "/html/filters.html") }) -app.get("/search/:text", function(req, res) { res.sendFile(__dirname + "/html/search.html") }) +app.get("/achievements", function(req, res) { res.status(200).sendFile(__dirname + "/html/achievements.html") }) +app.get("/analyze/:id", function(req, res) { res.status(200).sendFile(__dirname + "/html/analyze.html") }) +app.get("/api", function(req, res) { res.status(200).sendFile(__dirname + "/html/api.html") }) +app.get("/boomlings", function(req, res) { res.status(200).sendFile(__dirname + "/html/boomlings.html") }) +app.get("/comments/:id", function(req, res) { res.status(200).sendFile(__dirname + "/html/comments.html") }) +app.get("/demon/:id", function(req, res) { res.status(200).sendFile(__dirname + "/html/demon.html") }) +app.get("/gauntlets", function(req, res) { res.status(200).sendFile(__dirname + "/html/gauntlets.html") }) +app.get("/gdps", function(req, res) { res.status(200).sendFile(__dirname + "/html/gdps.html") }) +app.get("/iconkit", function(req, res) { res.status(200).sendFile(__dirname + "/html/iconkit.html") }) +app.get("/leaderboard", function(req, res) { res.status(200).sendFile(__dirname + "/html/leaderboard.html") }) +app.get("/leaderboard/:text", function(req, res) { res.status(200).sendFile(__dirname + "/html/levelboard.html") }) +app.get("/mappacks", function(req, res) { res.status(200).sendFile(__dirname + "/html/mappacks.html") }) +app.get("/messages", function(req, res) { res.status(200).sendFile(__dirname + "/html/messages.html") }) +app.get("/search", function(req, res) { res.status(200).sendFile(__dirname + "/html/filters.html") }) +app.get("/search/:text", function(req, res) { res.status(200).sendFile(__dirname + "/html/search.html") }) // API @@ -276,7 +276,7 @@ app.get("/search/:text", function(req, res) { res.sendFile(__dirname + "/html/se app.get("/api/analyze/:id", RL, function(req, res) { app.run.level(app, req, res, true, true) }) app.get("/api/boomlings", function(req, res) { app.run.boomlings(app, req, res) }) app.get("/api/comments/:id", RL2, function(req, res) { app.run.comments(app, req, res) }) -app.get("/api/credits", function(req, res) { res.send(require('./misc/credits.json')) }) +app.get("/api/credits", function(req, res) { res.status(200).send(require('./misc/credits.json')) }) app.get("/api/gauntlets", function(req, res) { app.run.gauntlets(app, req, res) }) app.get("/api/leaderboard", function(req, res) { app.run[req.query.hasOwnProperty("accurate") ? "accurate" : "scores"](app, req, res) }) app.get("/api/leaderboardLevel/:id", RL2, function(req, res) { app.run.leaderboardLevel(app, req, res) }) @@ -310,24 +310,23 @@ app.get("/:id", function(req, res) { app.run.level(app, req, res) }) // MISC app.get("/icon/:text", function(req, res) { app.run.icon(app, req, res) }) -app.get("/api/userCache", function(req, res) { res.send(app.accountCache) }) -app.get("/api/achievements", function(req, res) { res.send({achievements, types: achievementTypes, shopIcons, colors: colorList }) }) -app.get("/api/music", function(req, res) { res.send(music) }) -app.get("/api/gdps", function(req, res) {res.send(req.query.hasOwnProperty("current") ? app.safeServers.find(x => req.server.id == x.id) : app.safeServers) }) +app.get("/api/userCache", function(req, res) { res.status(200).send(app.accountCache) }) +app.get("/api/achievements", function(req, res) { res.status(200).send({achievements, types: achievementTypes, shopIcons, colors: colorList }) }) +app.get("/api/music", function(req, res) { res.status(200).send(music) }) +app.get("/api/gdps", function(req, res) {res.status(200).send(req.query.hasOwnProperty("current") ? app.safeServers.find(x => req.server.id == x.id) : app.safeServers) }) app.get('/api/icons', function(req, res) { let sample = [JSON.stringify(sampleIcons[Math.floor(Math.random() * sampleIcons.length)].slice(1))] let iconserver = req.isGDPS ? req.server.name : undefined - res.send({icons: gdIcons, colors: colorList, colorOrder, whiteIcons, server: iconserver, noCopy: req.onePointNine || req.offline, sample}); + res.status(200).send({icons: gdIcons, colors: colorList, colorOrder, whiteIcons, server: iconserver, noCopy: req.onePointNine || req.offline, sample}); }); app.get('*', function(req, res) { - if (req.path.startsWith('/api')) res.send('-1') + if (req.path.startsWith('/api')) res.status(404).send('-1') else res.redirect('/search/404%20') }); app.use(function (err, req, res, next) { - if (err && err.message == "Response timeout") res.status(500).send('Internal server error! (Timed out)') - else if (err) console.log(err) + if (err && err.message == "Response timeout") res.status(504).send('Internal server error! (Timed out)') }) process.on('uncaughtException', (e) => { console.log(e) }); diff --git a/package.json b/package.json index 6dba444..b8e00b6 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,9 @@ "main": "index.js", "license": "MIT", "private": true, + "scripts": { + "dev": "node ./index.js" + }, "dependencies": { "ag-psd": "^14.3.2", "canvas": "^2.8.0", diff --git a/servers.json b/servers.json index 8d9fa0a..026a53a 100644 --- a/servers.json +++ b/servers.json @@ -26,7 +26,6 @@ "targetAccountID": "targetBddpvouKE" } }, - { "name": "1.9 GDPS", "link": "https://absolllute.com/gdps/",