diff --git a/api/analyze.js b/api/analyze.js
index 1b97b25c..68c54994 100644
--- a/api/analyze.js
+++ b/api/analyze.js
@@ -1,3 +1,4 @@
+"use strict";
const zlib = require('zlib')
const blocks = require('../misc/analysis/blocks.json')
const colorStuff = require('../misc/analysis/colorProperties.json')
@@ -7,121 +8,130 @@ const ids = require('../misc/analysis/objects.json')
module.exports = async (app, req, res, level) => {
- if (!level) {
- level = {
- name: (req.body.name || "Unnamed").slice(0, 64),
- data: (req.body.data || "")
- }
+ level ||= {
+ name: (req.body.name || "Unnamed").slice(0, 64),
+ data: (req.body.data || "")
}
let unencrypted = level.data.startsWith('kS') // some gdps'es don't encrypt level data
let levelString = unencrypted ? level.data : Buffer.from(level.data, 'base64')
if (unencrypted) {
- const raw_data = level.data;
-
- const response_data = analyze_level(level, raw_data);
- return res.send(response_data);
- } else {
+ const raw_data = level.data
+ const response_data = analyze_level(level, raw_data)
+ return res.send(response_data)
+ }
+ else {
zlib.unzip(levelString, (err, buffer) => {
- if (err) { return res.status(500).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);
- });
+ const raw_data = buffer.toString()
+ const response_data = analyze_level(level, raw_data)
+ return res.send(response_data)
+ })
}
}
-function sortObj(obj, sortBy) {
- var sorted = {}
- var keys = !sortBy ? Object.keys(obj).sort((a,b) => obj[b] - obj[a]) : Object.keys(obj).sort((a,b) => obj[b][sortBy] - obj[a][sortBy])
+/**
+ * Sorts any `Object` by its keys
+ * @param {{}} obj
+ * @param {PropertyKey} [sortBy] optional inner key to sort
+ */
+const sortObj = (obj, sortBy) => {
+ let keys = Object.keys(obj)
+ .sort((a,b) => sortBy ? obj[b][sortBy] - obj[a][sortBy] : obj[b] - obj[a])
+ let sorted = {}
keys.forEach(x => {sorted[x] = obj[x]})
return sorted
}
-function parse_obj(obj, splitter, name_arr, valid_only) {
- const s_obj = obj.split(splitter);
- let robtop_obj = {};
+/**
+ * game-object (**not** JS `Object`) parser
+ * @param {string} obj
+ * @param {string} splitter
+ * @param {string[]} name_arr
+ * @param {boolean} [valid_only]
+ */
+const parse_obj = (obj, splitter, name_arr, valid_only) => {
+ const s_obj = obj.split(splitter)
+ let robtop_obj = {}
// semi-useless optimization depending on where at node js you're at
for (let i = 0, obj_l = s_obj.length; i < obj_l; i += 2) {
- let k_name = s_obj[i];
+ let k_name = s_obj[i]
if (s_obj[i] in name_arr) {
- if (!valid_only) {
- k_name = name_arr[s_obj[i]];
- }
- robtop_obj[k_name] = s_obj[i + 1];
+ if (!valid_only) k_name = name_arr[s_obj[i]]
+ robtop_obj[k_name] = s_obj[i + 1]
}
}
- return robtop_obj;
+ return robtop_obj
}
+/**
+ * @param {{}} level
+ * @param {string} rawData
+ */
function analyze_level(level, rawData) {
let response = {};
- let blockCounts = {}
- let miscCounts = {}
- let triggerGroups = []
+ let blockCounts = {};
+ let miscCounts = {};
+ /**@type {string[]}*/
+ let triggerGroups = [];
let highDetail = 0
- let alphaTriggers = []
+ /**@type {{}[]}*/
+ let alphaTriggers = [];
let misc_objects = {};
let block_ids = {};
for (const [name, object_ids] of Object.entries(ids.misc)) {
- const copied_ids = object_ids.slice(1);
+ const copied_ids = object_ids.slice(1)
// funny enough, shift effects the original id list
- copied_ids.forEach((object_id) => {
- misc_objects[object_id] = name;
- });
+ copied_ids.forEach(object_id => { misc_objects[object_id] = name })
}
- for (const [name, object_ids] of Object.entries(blocks)) {
- object_ids.forEach((object_id) => {
- block_ids[object_id] = name;
- });
- }
+ for (const [name, object_ids] of Object.entries(blocks))
+ object_ids.forEach(object_id => { block_ids[object_id] = name });
- const data = rawData.split(";");
- const header = data.shift();
+ /**@type {(string|{})[]}*/
+ const data = rawData.split(";")
+ const header = data.shift()
let level_portals = [];
let level_coins = [];
let level_text = [];
+ // "why are these Objects instead of Arrays?" @Rudxain
let orb_array = {};
let trigger_array = {};
- let last = 0;
+ let last = 0
- const obj_length = data.length;
+ const obj_length = data.length
for (let i = 0; i < obj_length; ++i) {
- obj = parse_obj(data[i], ',', properties);
+ let obj = parse_obj(data[i], ',', properties)
- let id = obj.id
+ let {id} = obj
if (id in ids.portals) {
- obj.portal = ids.portals[id];
- level_portals.push(obj);
+ obj.portal = ids.portals[id]
+ level_portals.push(obj)
} else if (id in ids.coins) {
- obj.coin = ids.coins[id];
- level_coins.push(obj);
+ obj.coin = ids.coins[id]
+ level_coins.push(obj)
} else if (id in ids.orbs) {
- obj.orb = ids.orbs[id];
+ obj.orb = ids.orbs[id]
- if (obj.orb in orb_array) {
- orb_array[obj.orb]++;
- } else {
- orb_array[obj.orb] = 1;
- }
+ const orb = orb_array[obj.orb]
+ orb_array[obj.orb] = orb ? +orb + 1 : 1
} else if (id in ids.triggers) {
- obj.trigger = ids.triggers[id];
+ obj.trigger = ids.triggers[id]
if (obj.trigger in trigger_array) {
- trigger_array[obj.trigger]++;
+ trigger_array[obj.trigger]++
} else {
- trigger_array[obj.trigger] = 1;
+ trigger_array[obj.trigger] = 1
}
}
@@ -130,60 +140,66 @@ function analyze_level(level, rawData) {
}
if (obj.triggerGroups) obj.triggerGroups.split('.').forEach(x => triggerGroups.push(x))
- if (obj.highDetail == 1) highDetail += 1
+ if (obj.highDetail == 1) highDetail++
if (id in misc_objects) {
- const name = misc_objects[id];
+ const name = misc_objects[id]
if (name in miscCounts) {
- miscCounts[name][0] += 1;
+ miscCounts[name][0]++
} else {
- miscCounts[name] = [1, ids.misc[name][0]];
+ miscCounts[name] = [1, ids.misc[name][0]]
}
}
if (id in block_ids) {
- const name = block_ids[id];
+ const name = block_ids[id]
if (name in blockCounts) {
- blockCounts[name] += 1;
+ blockCounts[name]++
} else {
- blockCounts[name] = 1;
+ blockCounts[name] = 1
}
}
- if (obj.x) { // sometimes the field doesn't exist
- last = Math.max(last, obj.x);
- }
+ // sometimes the field doesn't exist
+ if (obj.x) last = Math.max(last, obj.x)
- if (obj.trigger == "Alpha") { // invisible triggers
- alphaTriggers.push(obj)
- }
+ // invisible triggers
+ if (obj.trigger == "Alpha") alphaTriggers.push(obj)
- data[i] = obj;
+ data[i] = obj
}
let invisTriggers = []
alphaTriggers.forEach(tr => {
- if (tr.x < 500 && !tr.touchTriggered && !tr.spawnTriggered && tr.opacity == 0 && tr.duration == 0
- && alphaTriggers.filter(x => x.targetGroupID == tr.targetGroupID).length == 1) invisTriggers.push(Number(tr.targetGroupID))
+ if (
+ tr.x < 500
+ && !tr.touchTriggered && !tr.spawnTriggered
+ && tr.opacity == 0 && tr.duration == 0
+ && alphaTriggers.filter(x => x.targetGroupID == tr.targetGroupID).length == 1
+ )
+ invisTriggers.push(Number(tr.targetGroupID))
})
- response.level = {
- name: level.name, id: level.id, author: level.author, playerID: level.playerID, accountID: level.accountID, large: level.large
- }
+ response.level = {};
+ ['name', 'id', 'author', 'playerID', 'accountID', 'large'].forEach(k => {response.level[k] = level[k]})
response.objects = data.length - 2
response.highDetail = highDetail
response.settings = {}
- response.portals = level_portals.sort(function (a, b) {return parseInt(a.x) - parseInt(b.x)}).map(x => x.portal + " " + Math.floor(x.x / (Math.max(last, 529.0) + 340.0) * 100) + "%").join(", ")
- response.coins = level_coins.sort(function (a, b) {return parseInt(a.x) - parseInt(b.x)}).map(x => Math.floor(x.x / (Math.max(last, 529.0) + 340.0) * 100))
+ // "I have no idea what to name this lmao" @Rudxain
+ let WTF = x => Math.floor(x.x / (Math.max(last, 529) + 340) * 100)
+ response.portals = level_portals.sort((a, b) => parseInt(a.x) - parseInt(b.x)).map(x => x.portal + " " + WTF(x) + "%").join(", ")
+ response.coins = level_coins.sort((a, b) => parseInt(a.x) - parseInt(b.x)).map(WTF)
response.coinsVerified = level.verifiedCoins
+ /**@param {number[]} arr*/
+ const sum = arr => arr.reduce((a, x) => a + x, 0)
response.orbs = orb_array
- response.orbs.total = Object.values(orb_array).reduce((a, x) => a + x, 0); // we already have an array of objects, use it
+ response.orbs.total = sum(Object.values(orb_array)) // we already have an array of objects, use it
response.triggers = trigger_array
- response.triggers.total = Object.values(trigger_array).reduce((a, x) => a + x, 0);
+ response.triggers.total = sum(Object.values(trigger_array))
response.triggerGroups = {}
response.blocks = sortObj(blockCounts)
@@ -191,7 +207,7 @@ function analyze_level(level, rawData) {
response.colors = []
triggerGroups.forEach(x => {
- if (response.triggerGroups['Group ' + x]) response.triggerGroups['Group ' + x] += 1
+ if (response.triggerGroups['Group ' + x]) response.triggerGroups['Group ' + x]++
else response.triggerGroups['Group ' + x] = 1
})
@@ -202,99 +218,113 @@ function analyze_level(level, rawData) {
// find alpha group with the most objects
response.invisibleGroup = triggerKeys.find(x => invisTriggers.includes(x))
- response.text = level_text.sort(function (a, b) {return parseInt(a.x) - parseInt(b.x)}).map(x => [Buffer.from(x.message, 'base64').toString(), Math.round(x.x / last * 99) + "%"])
+ response.text = level_text.sort((a, b) => parseInt(a.x) - parseInt(b.x)).map(x => [Buffer.from(x.message, 'base64').toString(), Math.round(x.x / last * 99) + "%"])
- const header_response = parse_header(header);
- response.settings = header_response.settings;
- response.colors = header_response.colors;
+ const header_response = parse_header(header)
+ response.settings = header_response.settings
+ response.colors = header_response.colors
response.dataLength = rawData.length
response.data = rawData
- return response;
+ return response
}
-function parse_header(header) {
- let response = {};
- response.settings = {};
- response.colors = [];
+function parse_header(/**@type {string}*/ header) {
+ let response = {}
+ response.settings = {}
+ response.colors = []
- const header_keyed = parse_obj(header, ',', init.values, true);
+ const header_keyed = parse_obj(header, ',', init.values, true)
- Object.keys(header_keyed).forEach(x => {
- let val = init.values[x]
+ Object.keys(header_keyed).forEach(k => {
+ let val = init.values[k]
+ /**@type {string}*/
let name = val[0]
- let property = header_keyed[x]
+ let property = header_keyed[k]
switch (val[1]) {
case 'list':
val = init[(val[0] + "s")][property];
- break;
+ break
case 'number':
val = Number(property);
- break;
+ break
case 'bump':
val = Number(property) + 1;
- break;
+ break
case 'bool':
val = property != "0";
- break;
+ break
case 'extra-legacy-color': { // scope?
// you can only imagine my fear when i discovered this was a thing
// these literally are keys set the value, and to convert this to the color list we have to do this fun messy thing that shouldn't exist
// since i wrote the 1.9 color before this, a lot of explaination will be there instead
- const colorInfo = name.split('-');
- const color = colorInfo[2]; // r,g,b
- const channel = colorInfo[1];
+ const colorInfo = name.split('-')
+ /** r,g,b */
+ const color = colorInfo[2]
+ const channel = colorInfo[1]
+
+ // first we create the color object
+ if (color == 'r') response.colors.push({"channel": channel, "opacity": 1})
- if (color == 'r') {
- // first we create the color object
- response.colors.push({"channel": channel, "opacity": 1});
- }
// from here we touch the color object
- let currentChannel = response.colors.find(k => k.channel == channel);
- if (color == 'blend') {
- currentChannel.blending = true; // only one color has blending though lol
- } else if (color == 'pcol' && property != 0) {
- currentChannel.pColor = property;
- }
- currentChannel[color] = property;
- break;
+ let currentChannel = response.colors.find(k => k.channel == channel)
+ if (color == 'blend') currentChannel.blending = true // only one color has blending though lol
+ if (color == 'pcol' && property != 0) currentChannel.pColor = property
+
+ currentChannel[color] = property
+ break
}
case 'legacy-color': {
// if a level has a legacy color, we can assume that it does not have a kS38 at all
- const color = parse_obj(property, "_", colorStuff.properties);
+ const color = parse_obj(property, "_", colorStuff.properties)
let colorObj = color
// so here we parse the color to something understandable by the rest
// slightly smart naming but it is also pretty gross
// in a sense - the name would be something like legacy-G -> G
- const colorVal = name.split('-').pop()
+ const colorVal = name.split('-').at(-1)
colorObj.channel = colorVal
// from here stuff can continue as normal, ish
- if (colorObj.pColor == "-1" || colorObj.pColor == "0") delete colorObj.pColor;
- colorObj.opacity = 1; // 1.9 colors don't have this!
- if (colorObj.blending && colorObj.blending == '1') colorObj.blending = true; // 1.9 colors manage to always think they're blending - they're not
- else delete colorObj.blending;
-
- if (colorVal == '3DL') { response.colors.splice(4, 0, colorObj); } // hardcode the position of 3DL, it typically goes at the end due to how RobTop make the headers
- else if (colorVal == 'Line') { colorObj.blending = true; response.colors.push(colorObj); } // in line with 2.1 behavior
- else { response.colors.push(colorObj); } // bruh whatever was done to make the color list originally was long
- break;
+ if (colorObj.pColor == "-1" || colorObj.pColor == "0")
+ delete colorObj.pColor
+ colorObj.opacity = 1 // 1.9 colors don't have this!
+
+ if (colorObj?.blending === '1')
+ colorObj.blending = true // 1.9 colors manage to always think they're blending - they're not
+ else
+ delete colorObj.blending
+
+ switch (colorVal) {
+ case '3DL':
+ response.colors.splice(4, 0, colorObj) // hardcode the position of 3DL, it typically goes at the end due to how RobTop make the headers
+ break
+
+ case 'Line': {
+ colorObj.blending = true; response.colors.push(colorObj) // in line with 2.1 behavior
+ break
+ }
+
+ default:
+ response.colors.push(colorObj) // bruh whatever was done to make the color list originally was long
+ break
+ }
+ break
}
case 'colors': {
let colorList = property.split("|")
colorList.forEach((x, y) => {
const color = parse_obj(x, "_", colorStuff.properties)
let colorObj = color
- if (!color.channel) return colorList = colorList.filter((h, i) => y != i)
+ if (!color.channel) return colorList = colorList.filter((_, i) => y != i)
if (colorStuff.channels[colorObj.channel]) colorObj.channel = colorStuff.channels[colorObj.channel]
- if (colorObj.channel > 1000) return;
+ if (colorObj.channel > 1000) return
if (colorStuff.channels[colorObj.copiedChannel]) colorObj.copiedChannel = colorStuff.channels[colorObj.copiedChannel]
- if (colorObj.copiedChannel > 1000) delete colorObj.copiedChannel;
+ if (colorObj.copiedChannel > 1000) delete colorObj.copiedChannel
if (colorObj.pColor == "-1") delete colorObj.pColor
if (colorObj.blending) colorObj.blending = true
if (colorObj.copiedHSV) {
@@ -303,37 +333,39 @@ function parse_header(header) {
hsv.forEach((x, y) => { colorObj.copiedHSV[colorStuff.hsv[y]] = x })
colorObj.copiedHSV['s-checked'] = colorObj.copiedHSV['s-checked'] == 1
colorObj.copiedHSV['v-checked'] = colorObj.copiedHSV['v-checked'] == 1
- if (colorObj.copyOpacity == 1) colorObj.copyOpacity = true
+ if (colorObj.copyOpacity == 1) colorObj.copyOpacity = true
}
colorObj.opacity = +Number(colorObj.opacity).toFixed(2)
colorList[y] = colorObj
- });
+ })
// we assume this is only going to be run once so... some stuff can go here
colorList = colorList.filter(x => typeof x == "object")
if (!colorList.find(x => x.channel == "Obj")) colorList.push({"r": "255", "g": "255", "b": "255", "channel": "Obj", "opacity": "1"})
const specialSort = ["BG", "G", "G2", "Line", "Obj", "3DL"]
- let specialColors = colorList.filter(x => isNaN(x.channel)).sort(function (a, b) {return specialSort.indexOf( a.channel ) > specialSort.indexOf( b.channel ) } )
- let regularColors = colorList.filter(x => !isNaN(x.channel)).sort(function(a, b) {return (+a.channel) - (+b.channel) } );
+ let specialColors = colorList.filter(x => isNaN(x.channel)).sort((a, b) => specialSort.indexOf(a.channel) > specialSort.indexOf(b.channel))
+ let regularColors = colorList.filter(x => !isNaN(x.channel)).sort((a, b) => a.channel - b.channel)
response.colors = specialColors.concat(regularColors)
- break;
+ break
}
}
response.settings[name] = val
})
- if (!response.settings.ground || response.settings.ground > 17) response.settings.ground = 1
- if (!response.settings.background || response.settings.background > 20) response.settings.background = 1
- if (!response.settings.font) response.settings.font = 1
+ if (!response.settings.ground || response.settings.ground > 17)
+ response.settings.ground = 1
+ if (!response.settings.background || response.settings.background > 20)
+ response.settings.background = 1
+ if (!response.settings.font)
+ response.settings.font = 1
- if (response.settings.alternateLine == 2) response.settings.alternateLine = true
- else response.settings.alternateLine = false
+ response.settings.alternateLine = response.settings.alternateLine == 2
Object.keys(response.settings).filter(k => {
// this should be parsed into color list instead
- if (k.includes('legacy')) delete response.settings[k];
- });
+ if (k.includes('legacy')) delete response.settings[k]
+ })
- delete response.settings['colors'];
- return response;
+ delete response.settings['colors']
+ return response
}
diff --git a/api/comments.js b/api/comments.js
index f48f6e10..9148764d 100644
--- a/api/comments.js
+++ b/api/comments.js
@@ -1,3 +1,4 @@
+"use strict";
const Player = require('../classes/Player.js')
module.exports = async (app, req, res) => {
@@ -8,8 +9,8 @@ module.exports = async (app, req, res) => {
if (count > 1000) count = 1000
let params = {
- userID : req.params.id,
- accountID : req.params.id,
+ userID : req.params.id,
+ accountID : req.params.id,
levelID: req.params.id,
page: +req.query.page || 0,
count,
@@ -20,7 +21,7 @@ module.exports = async (app, req, res) => {
if (req.query.type == "commentHistory") { path = "getGJCommentHistory"; delete params.levelID }
else if (req.query.type == "profile") path = "getGJAccountComments20"
- req.gdRequest(path, req.gdParams(params), function(err, resp, body) {
+ req.gdRequest(path, req.gdParams(params), function(err, resp, body) {
if (err) return res.sendError()
@@ -32,7 +33,7 @@ module.exports = async (app, req, res) => {
if (!comments.length) return res.status(204).send([])
let pages = body.split('#')[1].split(":")
- let lastPage = +Math.ceil(+pages[0] / +pages[2]);
+ let lastPage = +Math.ceil(+pages[0] / +pages[2])
let commentArray = []
@@ -41,7 +42,7 @@ module.exports = async (app, req, res) => {
var x = c[0] //comment info
var y = c[1] //account info
- if (!x[2]) return;
+ if (!x[2]) return
let comment = {}
comment.content = Buffer.from(x[2], 'base64').toString();
@@ -50,9 +51,9 @@ module.exports = async (app, req, res) => {
comment.date = (x[9] || "?") + req.timestampSuffix
if (comment.content.endsWith("⍟") || comment.content.endsWith("☆")) {
comment.content = comment.content.slice(0, -1)
- comment.browserColor = true
+ comment.browserColor = true
}
-
+
if (req.query.type != "profile") {
let commentUser = new Player(y)
Object.keys(commentUser).forEach(k => {
@@ -74,7 +75,7 @@ module.exports = async (app, req, res) => {
commentArray.push(comment)
- })
+ })
return res.send(commentArray)
diff --git a/api/download.js b/api/download.js
index 7d4cc12d..2cdb899e 100644
--- a/api/download.js
+++ b/api/download.js
@@ -1,3 +1,4 @@
+"use strict";
const request = require('request')
const fs = require('fs')
const Level = require('../classes/Level.js')
@@ -5,27 +6,27 @@ const Level = require('../classes/Level.js')
module.exports = async (app, req, res, api, ID, analyze) => {
function rejectLevel() {
- if (!api) return res.redirect('search/' + req.params.id)
- else return res.sendError()
+ return !api ? res.redirect('search/' + req.params.id) : res.sendError()
}
if (req.offline) {
- if (!api && levelID < 0) return res.redirect('/')
- return rejectLevel()
+ return !api && levelID < 0 ? res.redirect('/') : rejectLevel()
}
let levelID = ID || req.params.id
- if (levelID == "daily") levelID = -1
- else if (levelID == "weekly") levelID = -2
- else levelID = levelID.replace(/[^0-9]/g, "")
+ levelID = (
+ levelID == "daily" ? -1 :
+ levelID == "weekly" ? -2 :
+ levelID.replace(/\D/g, "")
+ )
req.gdRequest('downloadGJLevel22', { levelID }, function (err, resp, body) {
- if (err) {
- 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()
- }
+ if (err) return (
+ analyze && api && req.server.downloadsDisabled ? res.status(403).send("-3")
+ : !api && levelID < 0 ? res.redirect(`/?daily=${levelID * -1}`)
+ : rejectLevel()
+ )
let authorData = body.split("#")[3] // daily/weekly only, most likely
@@ -43,7 +44,7 @@ module.exports = async (app, req, res, api, ID, analyze) => {
if (err2 && (foundID || authorData)) {
let authorInfo = foundID || authorData.split(":")
level.author = authorInfo[1] || "-"
- level.accountID = authorInfo[0] && authorInfo[0].includes(",") ? "0" : authorInfo[0]
+ level.accountID = authorInfo[0]?.includes(",") ? "0" : authorInfo[0]
}
else if (!err && b2 != '-1') {
@@ -72,7 +73,7 @@ module.exports = async (app, req, res, api, ID, analyze) => {
if (api) return res.send(level)
else return fs.readFile('./html/level.html', 'utf8', function (err, data) {
- let html = data;
+ let html = data
let variables = Object.keys(level)
variables.forEach(x => {
let regex = new RegExp(`\\[\\[${x.toUpperCase()}\\]\\]`, "g")
@@ -89,7 +90,7 @@ module.exports = async (app, req, res, api, ID, analyze) => {
level.nextDaily = +dailyTime
level.nextDailyTimestamp = Math.round((Date.now() + (+dailyTime * 1000)) / 100000) * 100000
return sendLevel()
- })
+ })
}
else if (req.server.demonList && level.difficulty == "Extreme Demon") {
diff --git a/api/gauntlets.js b/api/gauntlets.js
index d0661f31..efc1368a 100644
--- a/api/gauntlets.js
+++ b/api/gauntlets.js
@@ -1,3 +1,4 @@
+"use strict";
let cache = {}
let gauntletNames = ["Fire", "Ice", "Poison", "Shadow", "Lava", "Bonus", "Chaos", "Demon", "Time", "Crystal", "Magic", "Spike", "Monster", "Doom", "Death"]
@@ -6,17 +7,19 @@ module.exports = async (app, req, res) => {
if (req.offline) return res.sendError()
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.send(cached.data) // half hour cache
req.gdRequest('getGJGauntlets21', {}, function (err, resp, body) {
if (err) return res.sendError()
- let gauntlets = body.split('#')[0].split('|').map(x => app.parseResponse(x)).filter(x => x[3])
+ let gauntlets = body.split('#', 1)[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()}
+ if (app.config.cacheGauntlets)
+ cache[req.id] = {data: gauntletList, indexed: Date.now()}
res.send(gauntletList)
})
-
+
}
\ No newline at end of file
diff --git a/api/leaderboards/accurate.js b/api/leaderboards/accurate.js
index cfea414a..3c15a6ae 100644
--- a/api/leaderboards/accurate.js
+++ b/api/leaderboards/accurate.js
@@ -1,5 +1,6 @@
-const {GoogleSpreadsheet} = require('google-spreadsheet');
-const sheet = new GoogleSpreadsheet('1ADIJvAkL0XHGBDhO7PP9aQOuK3mPIKB2cVPbshuBBHc'); // accurate leaderboard spreadsheet
+"use strict";
+const {GoogleSpreadsheet} = require('google-spreadsheet')
+const sheet = new GoogleSpreadsheet('1ADIJvAkL0XHGBDhO7PP9aQOuK3mPIKB2cVPbshuBBHc') // accurate leaderboard spreadsheet
let indexes = ["stars", "coins", "demons", "diamonds"]
@@ -9,39 +10,41 @@ let caches = [{"stars": null, "coins": null, "demons": null, "diamonds": null},
module.exports = async (app, req, res, post) => {
- // 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]
-
- 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
-
- sheet.useApiKey(app.sheetsKey)
- sheet.loadInfo().then(async () => {
- let tab = sheet.sheetsById[1555821000]
- await tab.loadCells('A2:H2')
-
- let cellIndex = indexes.findIndex(x => type == x)
- 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.sendError() }
- let leaderboard = JSON.parse(cell.replace(/~( |$)/g, ""))
-
- let gdFormatting = ""
- leaderboard.forEach(x => {
- app.userCache(req.id, x.accountID, x.playerID, x.username)
- gdFormatting += `1:${x.username}:2:${x.playerID}:13:${x.coins}:17:${x.usercoins}:6:${x.rank}:9:${x.icon.icon}:10:${x.icon.col1}:11:${x.icon.col2}:14:${forms.indexOf(x.icon.form)}:15:${x.icon.glow ? 2 : 0}:16:${x.accountID}:3:${x.stars}:8:${x.cp}:46:${x.diamonds}:4:${x.demons}|`
- })
- 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)
-
+ // 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([])
+ const gdMode = post || req.query.hasOwnProperty("gd")
+ const modMode = !gdMode && req.query.hasOwnProperty("mod")
+ let cache = caches[gdMode ? 2 : modMode ? 1 : 0]
+
+ 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
+
+ sheet.useApiKey(app.sheetsKey)
+ sheet.loadInfo().then(async () => {
+ let tab = sheet.sheetsById[1555821000]
+ await tab.loadCells('A2:H2')
+
+ let cellIndex = indexes.indexOf(type)
+ 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.sendError()
+ }
+ let leaderboard = JSON.parse(cell.replace(/~( |$)/g, ""))
+
+ let gdFormatting = ""
+ leaderboard.forEach(x => {
+ app.userCache(req.id, x.accountID, x.playerID, x.username)
+ gdFormatting += `1:${x.username}:2:${x.playerID}:13:${x.coins}:17:${x.usercoins}:6:${x.rank}:9:${x.icon.icon}:10:${x.icon.col1}:11:${x.icon.col2}:14:${forms.indexOf(x.icon.form)}:15:${x.icon.glow ? 2 : 0}:16:${x.accountID}:3:${x.stars}:8:${x.cp}:46:${x.diamonds}:4:${x.demons}|`
+ })
+ 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)
})
}
\ No newline at end of file
diff --git a/api/leaderboards/boomlings.js b/api/leaderboards/boomlings.js
index cb0e1822..27096574 100644
--- a/api/leaderboards/boomlings.js
+++ b/api/leaderboards/boomlings.js
@@ -1,12 +1,15 @@
+"use strict";
const request = require('request')
module.exports = async (app, req, res) => {
- // Accurate leaderboard returns 418 because Private servers do not use.
- if (req.isGDPS) return res.status(418).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) {
+ 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.status(500).send("0")
@@ -24,7 +27,7 @@ module.exports = async (app, req, res) => {
score: +scores.slice(3, 10),
boomling: +visuals.slice(5, 7),
boomlingLevel: +visuals.slice(2, 4),
- powerups: [+visuals.slice(7, 9), +visuals.slice(9, 11), +visuals.slice(11, 13)].map(x => (x > 8 || x < 1) ? 0 : x),
+ powerups: [+visuals.slice(7, 9), +visuals.slice(9, 11), +visuals.slice(11, 13)].map(x => (x > 8 || x < 1) ? 0 : x),
unknownVisual: +visuals.slice(0, 2),
unknownScore: +scores.slice(0, 1),
@@ -34,10 +37,10 @@ module.exports = async (app, req, res) => {
if (!user.boomling || user.boomling > 66 || user.boomling < 0) user.boomling = 0
if (!user.boomlingLevel || user.boomlingLevel > 25 || user.boomlingLevel < 1) user.boomlingLevel = 25
- users.push(user)
+ users.push(user)
})
return res.send(users)
-
- })
+ }
+ )
}
\ No newline at end of file
diff --git a/api/leaderboards/leaderboardLevel.js b/api/leaderboards/leaderboardLevel.js
index 86e74804..7ce4c5f4 100644
--- a/api/leaderboards/leaderboardLevel.js
+++ b/api/leaderboards/leaderboardLevel.js
@@ -1,53 +1,50 @@
-const colors = require('../../iconkit/sacredtexts/colors.json');
+"use strict";
+const colors = require('../../iconkit/sacredtexts/colors.json')
module.exports = async (app, req, res) => {
if (req.offline) return res.sendError()
- let amount = 100;
- let count = req.query.count ? parseInt(req.query.count) : null
- if (count && count > 0) {
- if (count > 200) amount = 200
- else amount = count;
- }
-
- let params = {
- levelID: req.params.id,
- accountID: app.id,
- gjp: app.gjp,
- type: req.query.hasOwnProperty("week") ? "2" : "1",
- }
-
- req.gdRequest('getGJLevelScores211', params, function(err, resp, body) {
-
- 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.status(500).send([])
- else app.trackSuccess(req.id)
-
- scores.forEach(x => {
- let keys = Object.keys(x)
- x.rank = x[6]
- x.username = x[1]
- x.percent = +x[3]
- x.coins = +x[13]
- x.playerID = x[2]
- x.accountID = x[16]
- x.date = x[42] + req.timestampSuffix
- x.icon = {
- form: ['icon', 'ship', 'ball', 'ufo', 'wave', 'robot', 'spider'][+x[14]],
- icon: +x[9],
- col1: +x[10],
- col2: +x[11],
- glow: +x[15] > 1,
- col1RGB: colors[x[10]] || colors["0"],
- col2RGB: colors[x[11]] || colors["3"]
- }
- keys.forEach(k => delete x[k])
- app.userCache(req.id, x.accountID, x.playerID, x.username)
- })
-
- return res.send(scores.slice(0, amount))
-
- })
+ let amount = 100
+ let count = req.query.count ? parseInt(req.query.count) : null
+ if (count && count > 0) amount = Math.min(count, 200)
+
+ let params = {
+ levelID: req.params.id,
+ accountID: app.id,
+ gjp: app.gjp,
+ type: req.query.hasOwnProperty("week") ? "2" : "1",
+ }
+
+ req.gdRequest('getGJLevelScores211', params, function(err, resp, body) {
+
+ 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.status(500).send([])
+ app.trackSuccess(req.id)
+
+ scores.forEach(x => {
+ let keys = Object.keys(x)
+ x.rank = x[6]
+ x.username = x[1]
+ x.percent = +x[3]
+ x.coins = +x[13]
+ x.playerID = x[2]
+ x.accountID = x[16]
+ x.date = x[42] + req.timestampSuffix
+ x.icon = {
+ form: ['icon', 'ship', 'ball', 'ufo', 'wave', 'robot', 'spider'][+x[14]],
+ icon: +x[9],
+ col1: +x[10],
+ col2: +x[11],
+ glow: +x[15] > 1,
+ col1RGB: colors[x[10]] || colors["0"],
+ col2RGB: colors[x[11]] || colors["3"]
+ }
+ keys.forEach(k => delete x[k])
+ app.userCache(req.id, x.accountID, x.playerID, x.username)
+ })
+
+ return res.send(scores.slice(0, amount))
+ })
}
\ No newline at end of file
diff --git a/api/leaderboards/scores.js b/api/leaderboards/scores.js
index 3a5baa38..68bf8a11 100644
--- a/api/leaderboards/scores.js
+++ b/api/leaderboards/scores.js
@@ -1,33 +1,35 @@
+"use strict";
const Player = require('../../classes/Player.js')
module.exports = async (app, req, res) => {
- if (req.offline) return res.sendError()
+ if (req.offline) return res.sendError()
- let amount = 100;
- let count = req.query.count ? parseInt(req.query.count) : null
- if (count && count > 0) {
- if (count > 10000) amount = 10000
- else amount = count;
- }
+ let amount = 100
+ let count = req.query.count ? parseInt(req.query.count) : 0
+ if (count > 0) amount = Math.min(count, 10000)
- let params = {count: amount, type: "top"}
+ let params = {count: amount, type: "top"}
- if (["creators", "creator", "cp"].some(x => req.query.hasOwnProperty(x) || req.query.type == x)) params.type = "creators"
- else if (["week", "weekly"].some(x => req.query.hasOwnProperty(x) || req.query.type == x)) params.type = "week"
- else if (["global", "relative"].some(x => req.query.hasOwnProperty(x) || req.query.type == x)) {
- params.type = "relative"
- params.accountID = req.query.accountID
- }
+ let isInQuery = (...args) => args.some(x => req.query.hasOwnProperty(x) || req.query.type == x)
- req.gdRequest('getGJScores20', params, function(err, resp, body) {
+ if (isInQuery("creators", "creator", "cp"))
+ params.type = "creators"
+ else if (isInQuery("week", "weekly"))
+ params.type = "week"
+ else if (isInQuery("global", "relative")) {
+ params.type = "relative"
+ params.accountID = req.query.accountID
+ }
- if (err) return res.sendError()
- scores = body.split('|').map(x => app.parseResponse(x)).filter(x => x[1])
- if (!scores.length) return res.sendError()
+ req.gdRequest('getGJScores20', params, function(err, resp, body) {
- scores = scores.map(x => new Player(x))
- scores.forEach(x => app.userCache(req.id, x.accountID, x.playerID, x.username))
- return res.send(scores.slice(0, amount))
- })
+ if (err) return res.sendError()
+ scores = body.split('|').map(x => app.parseResponse(x)).filter(x => x[1])
+ if (!scores.length) return res.sendError()
+
+ scores = scores.map(x => new Player(x))
+ scores.forEach(x => app.userCache(req.id, x.accountID, x.playerID, x.username))
+ return res.send(scores.slice(0, amount))
+ })
}
\ No newline at end of file
diff --git a/api/level.js b/api/level.js
index b5ea7ebe..887e8ed1 100644
--- a/api/level.js
+++ b/api/level.js
@@ -1,3 +1,4 @@
+"use strict";
const request = require('request')
const fs = require('fs')
const Level = require('../classes/Level.js')
@@ -5,17 +6,14 @@ const Level = require('../classes/Level.js')
module.exports = async (app, req, res, api, analyze) => {
function rejectLevel() {
- if (!api) return res.redirect('search/' + req.params.id)
- else return res.sendError()
+ return !api ? res.redirect('search/' + req.params.id) : res.sendError()
}
if (req.offline) return rejectLevel()
let levelID = req.params.id
- if (levelID == "daily") return app.run.download(app, req, res, api, 'daily', analyze)
- else if (levelID == "weekly") return app.run.download(app, req, res, api, 'weekly', analyze)
- else if (levelID.match(/[^0-9]/)) return rejectLevel()
- else levelID = levelID.replace(/[^0-9]/g, "")
+ if (levelID == "daily" || levelID == "weekly") return app.run.download(app, req, res, api, levelID, analyze)
+ else if (/\D/.test(levelID)) return rejectLevel()
if (analyze || req.query.hasOwnProperty("download")) return app.run.download(app, req, res, api, levelID, analyze)
@@ -23,9 +21,11 @@ module.exports = async (app, req, res, api, analyze) => {
if (err || body.startsWith("##")) return rejectLevel()
- let preRes = body.split('#')[0].split('|', 10)
- let author = body.split('#')[1].split('|')[0].split(':')
- let song = '~' + body.split('#')[2];
+ // "revolutionary name XD" @Rudxain
+ const bodySplit = body.split('#')
+ let preRes = bodySplit[0].split('|', 10)
+ let author = bodySplit[1].split('|', 1)[0].split(':')
+ let song = '~' + bodySplit[2]
song = app.parseResponse(song, '~|~')
let levelInfo = app.parseResponse(preRes.find(x => x.startsWith(`1:${levelID}`)) || preRes[0])
@@ -40,7 +40,7 @@ module.exports = async (app, req, res, api, analyze) => {
if (api) return res.send(level)
else return fs.readFile('./html/level.html', 'utf8', function (err, data) {
- let html = data;
+ let html = data
let filteredSong = level.songName.replace(/[^ -~]/g, "") // strip off unsupported characters
level.songName = filteredSong || level.songName
let variables = Object.keys(level)
diff --git a/api/mappacks.js b/api/mappacks.js
index 5b4a6d3e..eb5006c2 100644
--- a/api/mappacks.js
+++ b/api/mappacks.js
@@ -1,12 +1,14 @@
+"use strict";
let difficulties = ["auto", "easy", "normal", "hard", "harder", "insane", "demon", "demon-easy", "demon-medium", "demon-insane", "demon-extreme"]
let cache = {}
module.exports = async (app, req, res) => {
if (req.offline) return res.sendError()
-
+
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.send(cached.data) // 1.5 hour cache
let params = { count: 250, page: 0 }
let packs = []
@@ -15,7 +17,7 @@ module.exports = async (app, req, res) => {
if (err) return res.sendError()
- let newPacks = body.split('#')[0].split('|').map(x => app.parseResponse(x)).filter(x => x[2])
+ let newPacks = body.split('#', 1)[0].split('|').map(x => app.parseResponse(x)).filter(x => x[2])
packs = packs.concat(newPacks)
// not all GDPS'es support the count param, which means recursion time!!!
@@ -23,7 +25,7 @@ module.exports = async (app, req, res) => {
params.page++
return mapPackLoop()
}
-
+
let mappacks = packs.map(x => ({ // "packs.map()" laugh now please
id: +x[1],
name: x[2],
diff --git a/api/messages/countMessages.js b/api/messages/countMessages.js
index 717457b1..8aea16ea 100644
--- a/api/messages/countMessages.js
+++ b/api/messages/countMessages.js
@@ -1,9 +1,11 @@
+"use strict";
module.exports = async (app, req, res) => {
+ const send = (msg, c=400) => res.status(c).send(msg)
- if (req.method !== 'POST') return res.status(405).send("Method not allowed.")
+ if (req.method !== 'POST') return send("Method not allowed.", 405)
- 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!")
+ if (!req.body.accountID) return send("No account ID provided!")
+ if (!req.body.password) return send("No password provided!")
let params = {
accountID: req.body.accountID,
@@ -13,11 +15,11 @@ module.exports = async (app, req, res) => {
req.gdRequest('getGJUserInfo20', params, function (err, resp, body) {
- if (err) return res.status(400).send(`Error counting messages! Messages get blocked a lot so try again later, or make sure your username and password are entered correctly. Last worked: ${app.timeSince(req.id)} ago.`)
- else app.trackSuccess(req.id)
+ if (err) return send(`Error counting messages! Messages get blocked a lot so try again later, or make sure your username and password are entered correctly. Last worked: ${app.timeSince(req.id)} ago.`)
+ app.trackSuccess(req.id)
let count = app.parseResponse(body)[38]
- if (!count) return res.status(400).send("Error fetching unread messages!")
- else res.send(count)
+ if (!count) return send("Error fetching unread messages!")
+ res.send(count)
})
}
\ No newline at end of file
diff --git a/api/messages/deleteMessage.js b/api/messages/deleteMessage.js
index 8b6cc26f..727d01fd 100644
--- a/api/messages/deleteMessage.js
+++ b/api/messages/deleteMessage.js
@@ -1,23 +1,26 @@
+"use strict";
module.exports = async (app, req, res) => {
+ const send = (msg, c=400) => res.status(c).send(msg)
- if (req.method !== 'POST') return res.status(405).send("Method not allowed.")
+ if (req.method !== 'POST') return send("Method not allowed.", 405)
- 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!")
- if (!req.body.id) return res.status(400).send("No message ID(s) provided!")
+ if (!req.body.accountID) return send("No account ID provided!")
+ if (!req.body.password) return send("No password provided!")
+ if (!req.body.id) return send("No message ID(s) provided!")
let params = {
accountID: req.body.accountID,
gjp: app.xor.encrypt(req.body.password, 37526),
+ // serialize to CSV if needed
messages: Array.isArray(req.body.id) ? req.body.id.map(x => x.trim()).join(",") : req.body.id,
}
- let deleted = params.messages.split(",").length
+ let deleted = params.messages.split(",").length // CSV record count
req.gdRequest('deleteGJMessages20', params, function (err, resp, body) {
- if (body != 1) return res.status(400).send(`The Geometry Dash servers refused to delete the message! Try again later, or make sure your username and password are entered correctly. Last worked: ${app.timeSince(req.id)} ago.`)
- else res.send(`${deleted == 1 ? "1 message" : `${deleted} messages`} deleted!`)
+ if (body != 1) return send(`The Geometry Dash servers refused to delete the message! Try again later, or make sure your username and password are entered correctly. Last worked: ${app.timeSince(req.id)} ago.`)
+ res.send(`${deleted} message${deleted == 1 ? "" : "s"} deleted!`)
app.trackSuccess(req.id)
})
diff --git a/api/messages/fetchMessage.js b/api/messages/fetchMessage.js
index edb871ea..e3b53fe0 100644
--- a/api/messages/fetchMessage.js
+++ b/api/messages/fetchMessage.js
@@ -1,9 +1,11 @@
+"use strict";
module.exports = async (app, req, res) => {
+ const send = (msg, c=400) => res.status(c).send(msg)
- if (req.method !== 'POST') return res.status(405).send("Method not allowed.")
+ if (req.method !== 'POST') return send("Method not allowed.", 405)
- 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!")
+ if (!req.body.accountID) return send("No account ID provided!")
+ if (!req.body.password) return send("No password provided!")
let params = req.gdParams({
accountID: req.body.accountID,
@@ -13,24 +15,25 @@ module.exports = async (app, req, res) => {
req.gdRequest('downloadGJMessage20', params, function (err, resp, body) {
- if (err) return res.status(400).send(`Error fetching message! Try again later, or make sure your username and password are entered correctly. Last worked: ${app.timeSince(req.id)} ago.`)
- else app.trackSuccess(req.id)
+ if (err) return send(`Error fetching message! Try again later, or make sure your username and password are entered correctly. Last worked: ${app.timeSince(req.id)} ago.`)
+ app.trackSuccess(req.id)
let x = app.parseResponse(body)
- let msg = {}
- msg.id = x[1];
- msg.playerID = x[3]
- msg.accountID = x[2]
- msg.author = x[6]
- msg.subject = Buffer.from(x[4], "base64").toString().replace(/^Re: ☆/, "Re: ")
- msg.content = app.xor.decrypt(x[5], 14251)
- msg.date = x[7] + req.timestampSuffix
- if (msg.subject.endsWith("☆") || msg.subject.startsWith("☆")) {
- if (msg.subject.endsWith("☆")) msg.subject = msg.subject.slice(0, -1)
- else msg.subject = msg.subject.slice(1)
- msg.browserColor = true
- }
-
+ let subject = Buffer.from(x[4], "base64").toString().replace(/^Re: ☆/, "Re: ")
+ let msg = {
+ id: x[1],
+ playerID: x[3],
+ accountID: x[2],
+ author: x[6],
+ subject,
+ content: app.xor.decrypt(x[5], 14251),
+ date: x[7] + req.timestampSuffix
+ }
+ if (/^☆|☆$/.test(subject)) {
+ msg.subject = subject.slice(...(subject.endsWith("☆") ? [0, -1] : [1]))
+ msg.browserColor = true
+ }
+
return res.send(msg)
})
diff --git a/api/messages/getMessages.js b/api/messages/getMessages.js
index 40da8a39..e0c18a1c 100644
--- a/api/messages/getMessages.js
+++ b/api/messages/getMessages.js
@@ -1,10 +1,12 @@
+"use strict";
module.exports = async (app, req, res) => {
+ const send = (msg, c=400) => res.status(c).send(msg)
- if (req.method !== 'POST') return res.status(405).send("Method not allowed.")
+ if (req.method !== 'POST') return send("Method not allowed.", 405)
if (req.body.count) return app.run.countMessages(app, req, res)
- 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!")
+ if (!req.body.accountID) return send("No account ID provided!")
+ if (!req.body.password) return send("No password provided!")
let params = req.gdParams({
accountID: req.body.accountID,
@@ -15,26 +17,26 @@ module.exports = async (app, req, res) => {
req.gdRequest('getGJMessages20', params, function (err, resp, body) {
- if (err) return res.status(400).send(`Error fetching messages! Messages get blocked a lot so try again later, or make sure your username and password are entered correctly. Last worked: ${app.timeSince(req.id)} ago.`)
+ if (err) return send(`Error fetching messages! Messages get blocked a lot so try again later, or make sure your username and password are entered correctly. Last worked: ${app.timeSince(req.id)} ago.`)
else app.trackSuccess(req.id)
let messages = body.split("|").map(msg => app.parseResponse(msg))
let messageArray = []
messages.forEach(x => {
- let msg = {}
-
- msg.id = x[1];
- msg.playerID = x[3]
- msg.accountID = x[2]
- msg.author = x[6]
- msg.subject = Buffer.from(x[4], "base64").toString().replace(/^Re: ☆/, "Re: ")
- msg.date = x[7] + req.timestampSuffix
- msg.unread = x[8] != "1"
- if (msg.subject.endsWith("☆") || msg.subject.startsWith("☆")) {
- if (msg.subject.endsWith("☆")) msg.subject = msg.subject.slice(0, -1)
- else msg.subject = msg.subject.slice(1)
- msg.browserColor = true
- }
+ let subject = Buffer.from(x[4], "base64").toString().replace(/^Re: ☆/, "Re: ")
+ let msg = {
+ id: x[1],
+ playerID: x[3],
+ accountID: x[2],
+ author: x[6],
+ subject,
+ date: x[7] + req.timestampSuffix,
+ unread: x[8] != "1"
+ }
+ if (/^☆|☆$/.test(subject)) {
+ msg.subject = subject.slice(...(subject.endsWith("☆") ? [0, -1] : [1]))
+ msg.browserColor = true
+ }
app.userCache(req.id, msg.accountID, msg.playerID, msg.author)
messageArray.push(msg)
diff --git a/api/messages/sendMessage.js b/api/messages/sendMessage.js
index 36bb9c65..463448ec 100644
--- a/api/messages/sendMessage.js
+++ b/api/messages/sendMessage.js
@@ -1,25 +1,33 @@
+"use strict";
module.exports = async (app, req, res) => {
+ const send = (msg, c=400) => res.status(c).send(msg)
- if (req.method !== 'POST') return res.status(405).send("Method not allowed.")
+ if (req.method !== 'POST') return send("Method not allowed.", 405)
- 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!")
- 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!")
+ if (!req.body.targetID) return send("No target ID provided!")
+ if (!req.body.message) return send("No message provided!")
+ if (!req.body.accountID) return send("No account ID provided!")
+ if (!req.body.password) return send("No password provided!")
- 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 subject = Buffer.from(
+ (req.body.color ? "☆" : "") + (req.body.subject ? req.body.subject.slice(0, 50) : "No subject")
+ ).toString('base64url')
let body = app.xor.encrypt(req.body.message.slice(0, 300), 14251)
let params = req.gdParams({
accountID: req.body.accountID,
gjp: app.xor.encrypt(req.body.password, 37526),
toAccountID: req.body.targetID,
- subject, body,
+ subject, body
})
req.gdRequest('uploadGJMessage20', params, function (err, resp, body) {
- if (body != 1) return res.status(400).send(`The Geometry Dash servers refused to send the message! Try again later, or make sure your username and password are entered correctly. Last worked: ${app.timeSince(req.id)} ago.`)
- else res.send('Message sent!')
+ if (body != 1) return send(
+ `The Geometry Dash servers refused to send the message! `+
+ `Try again later, or make sure your username and password are entered correctly. `+
+ `Last worked: ${app.timeSince(req.id)} ago.`
+ )
+ res.send('Message sent!')
app.trackSuccess(req.id)
})
diff --git a/api/post/like.js b/api/post/like.js
index 0997238b..86a61266 100644
--- a/api/post/like.js
+++ b/api/post/like.js
@@ -1,17 +1,31 @@
+"use strict";
const crypto = require('crypto')
-function sha1(data) { return crypto.createHash("sha1").update(data, "binary").digest("hex"); }
+const sha1 = data => crypto.createHash("sha1").update(data, "binary").digest("hex")
module.exports = async (app, req, res) => {
+ const send = (msg, c=400) => res.status(c).send(msg)
- if (req.method !== 'POST') return res.status(405).send("Method not allowed.")
+ if (req.method !== 'POST') return send("Method not allowed.", 405)
+
+ if (!req.body.ID) return send("No ID provided!")
+ if (!req.body.accountID) return send("No account ID provided!")
+ if (!req.body.password) return send("No password provided!")
+ if (!req.body.like) return send("No like flag provided! (1=like, 0=dislike)")
+ if (!req.body.type) return send("No type provided! (1=level, 2=comment, 3=profile")
+ if (!req.body.extraID) return send("No extra ID provided! (this should be a level ID, account ID, or '0' for levels")
+ /*
+ // A compound error message is more helpful, but IDK if this may cause bugs,
+ // so this is commented-out
+ let errMsg = ""
+ if (!req.body.ID) errMsg += "No ID provided!\n"
+ if (!req.body.accountID) errMsg += "No account ID provided!\n"
+ if (!req.body.password) errMsg += "No password provided!\n"
+ if (!req.body.like) errMsg += "No like flag provided! (1=like, 0=dislike)\n"
+ if (!req.body.type) errMsg += "No type provided! (1=level, 2=comment, 3=profile\n"
+ if (!req.body.extraID) errMsg += "No extra ID provided! (this should be a level ID, account ID, or '0' for levels)\n"
+ if (errMsg) return send(errMsg)
+ */
- 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!")
- if (!req.body.like) return res.status(400).send("No like flag provided! (1=like, 0=dislike)")
- if (!req.body.type) return res.status(400).send("No type provided! (1=level, 2=comment, 3=profile")
- if (!req.body.extraID) return res.status(400).send("No extra ID provided! (this should be a level ID, account ID, or '0' for levels")
-
let params = {
udid: '0',
uuid: '0',
@@ -25,14 +39,15 @@ module.exports = async (app, req, res) => {
params.special = req.body.extraID.toString()
params.type = req.body.type.toString()
- let chk = params.special + params.itemID + params.like + params.type + params.rs + params.accountID + params.udid + params.uuid + "ysg6pUrtjn0J"
+ let chk = "";
+ ['special', 'itemID', 'like', 'type', 'rs', 'accountID', 'udid', 'uuid'].forEach(k => chk += params[k])
+ chk += "ysg6pUrtjn0J"
chk = sha1(chk)
chk = app.xor.encrypt(chk, 58281)
-
params.chk = chk
req.gdRequest('likeGJItem211', params, function (err, resp, body) {
- if (err) return res.status(400).send(`The Geometry Dash servers rejected your vote! Try again later, or make sure your username and password are entered correctly. Last worked: ${app.timeSince(req.id)} ago.`)
+ if (err) return send(`The Geometry Dash servers rejected your vote! Try again later, or make sure your username and password are entered correctly. Last worked: ${app.timeSince(req.id)} ago.`)
else app.trackSuccess(req.id)
res.send((params.like == 1 ? 'Successfully liked!' : 'Successfully disliked!') + " (this will only take effect if this is your first time doing so)")
})
diff --git a/api/post/postComment.js b/api/post/postComment.js
index 5027efc2..8b792c88 100644
--- a/api/post/postComment.js
+++ b/api/post/postComment.js
@@ -1,31 +1,46 @@
+"use strict";
const crypto = require('crypto')
-function sha1(data) { return crypto.createHash("sha1").update(data, "binary").digest("hex"); }
+const sha1 = data => crypto.createHash("sha1").update(data, "binary").digest("hex")
-let rateLimit = {};
+let rateLimit = {}
let cooldown = 15000 // GD has a secret rate limit and doesn't return -1 when a comment is rejected, so this keeps track
+// converts timestamp miliseconds to s (wrapped-around minutes)
function getTime(time) {
- let seconds = Math.ceil(time / 1000);
- seconds = seconds % 60;
- return seconds}
+ let seconds = Math.ceil(time / 1000)
+ seconds %= 60
+ return seconds
+}
module.exports = async (app, req, res) => {
+ const send = (msg, c=400) => res.status(c).send(msg)
- if (req.method !== 'POST') return res.status(405).send("Method not allowed.")
+ if (req.method !== 'POST') return send("Method not allowed.", 405)
- 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!")
- 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!")
+ if (!req.body.comment) return send("No comment provided!")
+ if (!req.body.username) return send("No username provided!")
+ if (!req.body.levelID) return send("No level ID provided!")
+ if (!req.body.accountID) return send("No account ID provided!")
+ if (!req.body.password) return send("No password provided!")
+ /*
+ // A compound error message is more helpful, but IDK if this may cause bugs,
+ // so this is commented-out
+ let errMsg = ""
+ if (!req.body.comment) errMsg += "No comment provided!\n"
+ if (!req.body.username) errMsg += "No username provided!\n"
+ if (!req.body.levelID) errMsg += "No level ID provided!\n"
+ if (!req.body.accountID) errMsg += "No account ID provided!\n"
+ if (!req.body.password) errMsg += "No password provided!\n"
+ if (errMsg) return send(errMsg)
+ */
- if (req.body.comment.includes('\n')) return res.status(400).send("Comments cannot contain line breaks!")
+ if (req.body.comment.includes('\n')) return send("Comments cannot contain line breaks!")
+
+ if (rateLimit[req.body.username]) return send(`Please wait ${getTime(rateLimit[req.body.username] + cooldown - Date.now())} seconds before posting another comment!`)
- if (rateLimit[req.body.username]) return res.status(400).send(`Please wait ${getTime(rateLimit[req.body.username] + cooldown - Date.now())} seconds before posting another comment!`)
-
let params = { percent: 0 }
- params.comment = Buffer.from(req.body.comment + (req.body.color ? "☆" : "")).toString('base64').replace(/\//g, '_').replace(/\+/g, "-")
+ params.comment = Buffer.from(req.body.comment + (req.body.color ? "☆" : "")).toString('base64url')
params.gjp = app.xor.encrypt(req.body.password, 37526)
params.levelID = req.body.levelID.toString()
params.accountID = req.body.accountID.toString()
@@ -40,15 +55,22 @@ module.exports = async (app, req, res) => {
params.chk = chk
req.gdRequest('uploadGJComment21', params, function (err, resp, body) {
- if (err) return res.status(400).send(`The Geometry Dash servers rejected your comment! Try again later, or make sure your username and password are entered correctly. Last worked: ${app.timeSince(req.id)} ago.`)
+ if (err) return send(
+ `The Geometry Dash servers rejected your comment! `+
+ `Try again later, or make sure your username and password are entered correctly. `+
+ `Last worked: ${app.timeSince(req.id)} ago.`
+ )
if (body.startsWith("temp")) {
let banStuff = body.split("_")
- return res.status(400).send(`You have been banned from commenting for ${(parseInt(banStuff[1]) / 86400).toFixed(0)} days. Reason: ${banStuff[2] || "None"}`)
+ return send(
+ `You have been banned from commenting for ${(parseInt(banStuff[1]) / 86400).toFixed(0)} days. `+
+ `Reason: ${banStuff[2] || "None"}`
+ )
}
res.send(`Comment posted to level ${params.levelID} with ID ${body}`)
app.trackSuccess(req.id)
- rateLimit[req.body.username] = Date.now();
- setTimeout(() => {delete rateLimit[req.body.username]; }, cooldown);
+ rateLimit[req.body.username] = Date.now()
+ setTimeout(() => {delete rateLimit[req.body.username]}, cooldown);
})
}
\ No newline at end of file
diff --git a/api/post/postProfileComment.js b/api/post/postProfileComment.js
index b9d0ae3a..3eeda13f 100644
--- a/api/post/postProfileComment.js
+++ b/api/post/postProfileComment.js
@@ -1,20 +1,32 @@
+"use strict";
const crypto = require('crypto')
-function sha1(data) { return crypto.createHash("sha1").update(data, "binary").digest("hex"); }
+const sha1 = data => crypto.createHash("sha1").update(data, "binary").digest("hex")
module.exports = async (app, req, res) => {
+ const send = (msg, c=400) => res.status(c).send(msg)
- if (req.method !== 'POST') return res.status(405).send("Method not allowed.")
+ if (req.method !== 'POST') return send("Method not allowed.", 405)
- 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!")
- if (!req.body.password) return res.status(400).send("No password provided!")
+ if (!req.body.comment) return send("No comment provided!")
+ if (!req.body.username) return send("No username provided!")
+ if (!req.body.accountID) return send("No account ID provided!")
+ if (!req.body.password) return send("No password provided!")
+ /*
+ // A compound error message is more helpful, but IDK if this may cause bugs,
+ // so this is commented-out
+ let errMsg = ""
+ if (!req.body.comment) errMsg += "No comment provided!\n"
+ if (!req.body.username) errMsg += "No username provided!\n"
+ if (!req.body.accountID) errMsg += "No account ID provided!\n"
+ if (!req.body.password) errMsg += "No password provided!\n"
+ if (errMsg) return send(errMsg)
+ */
+
+ if (req.body.comment.includes('\n')) return send("Profile posts cannot contain line breaks!")
- if (req.body.comment.includes('\n')) return res.status(400).send("Profile posts cannot contain line breaks!")
-
let params = { cType: '1' }
- params.comment = Buffer.from(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('base64url')
params.gjp = app.xor.encrypt(req.body.password, 37526)
params.accountID = req.body.accountID.toString()
params.userName = req.body.username
@@ -25,10 +37,10 @@ module.exports = async (app, req, res) => {
params.chk = chk
req.gdRequest('uploadGJAccComment20', params, function (err, resp, body) {
- if (err) return res.status(400).send(`The Geometry Dash servers rejected your profile post! Try again later, or make sure your username and password are entered correctly. Try again later, or make sure your username and password are entered correctly. Last worked: ${app.timeSince(req.id)} ago.`)
+ if (err) return send(`The Geometry Dash servers rejected your profile post! Try again later, or make sure your username and password are entered correctly. Try again later, or make sure your username and password are entered correctly. Last worked: ${app.timeSince(req.id)} ago.`)
else if (body.startsWith("temp")) {
let banStuff = body.split("_")
- return res.status(400).send(`You have been banned from commenting for ${(parseInt(banStuff[1]) / 86400).toFixed(0)} days. Reason: ${banStuff[2] || "None"}`)
+ return send(`You have been banned from commenting for ${(parseInt(banStuff[1]) / 86400).toFixed(0)} days. Reason: ${banStuff[2] || "None"}`)
}
else app.trackSuccess(req.id)
res.send(`Comment posted to ${params.userName} with ID ${body}`)
diff --git a/api/profile.js b/api/profile.js
index 15a9587e..95a6813b 100644
--- a/api/profile.js
+++ b/api/profile.js
@@ -1,13 +1,15 @@
+"use strict";
const fs = require('fs')
const Player = require('../classes/Player.js')
module.exports = async (app, req, res, api, getLevels) => {
-
- if (req.offline) {
- if (!api) return res.redirect('/search/' + req.params.id)
- else return res.sendError()
+ function rejectLevel() {
+ // this variant has an extra "/"
+ return !api ? res.redirect('/search/' + req.params.id) : res.sendError()
}
-
+
+ if (req.offline) return rejectLevel()
+
let username = getLevels || req.params.id
let probablyID
if (username.endsWith(".") && req.isGDPS) {
@@ -17,18 +19,18 @@ module.exports = async (app, req, res, api, getLevels) => {
let accountMode = !req.query.hasOwnProperty("player") && Number(req.params.id)
let foundID = app.userCache(req.id, username)
let skipRequest = accountMode || foundID || probablyID
- let searchResult;
+ let searchResult
// if you're searching by account id, an intentional error is caused to skip the first request to the gd servers. see i pulled a sneaky on ya. (fuck callbacks man)
- req.gdRequest(skipRequest ? "" : 'getGJUsers20', skipRequest ? {} : { str: username, page: 0 }, function (err1, res1, b1) {
+ req.gdRequest(skipRequest ? "" : 'getGJUsers20', skipRequest ? {} : { str: username, page: 0 }, function (err1, res1, b1) {
if (foundID) searchResult = foundID[0]
else if (accountMode || err1 || b1 == '-1' || b1.startsWith("<") || !b1) searchResult = probablyID ? username : req.params.id
- else if (!req.isGDPS) searchResult = app.parseResponse(b1.split("|")[0])[16]
+ else if (!req.isGDPS) searchResult = app.parseResponse(b1.split("|", 1)[0])[16]
else { // GDPS's return multiple users, GD no longer does this
let userResults = b1.split("|").map(x => app.parseResponse(x))
searchResult = userResults.find(x => x[1].toLowerCase() == username.toLowerCase() || x[2] == username) || ""
- if (searchResult) searchResult = searchResult[16]
+ searchResult &&= searchResult[16]
}
if (getLevels) {
@@ -40,20 +42,17 @@ module.exports = async (app, req, res, api, getLevels) => {
let account = app.parseResponse(body || "")
let dumbGDPSError = req.isGDPS && (!account[16] || account[1].toLowerCase() == "undefined")
-
- if (err2 || dumbGDPSError) {
- if (!api) return res.redirect('/search/' + req.params.id)
- else return res.sendError()
- }
-
+
+ if (err2 || dumbGDPSError) return rejectLevel()
+
if (!foundID) app.userCache(req.id, account[16], account[2], account[1])
-
- let userData = new Player(account)
-
+
+ let userData = new Player(account)
+
if (api) return res.send(userData)
else fs.readFile('./html/profile.html', 'utf8', function(err, data) {
- let html = data;
+ let html = data
let variables = Object.keys(userData)
variables.forEach(x => {
let regex = new RegExp(`\\[\\[${x.toUpperCase()}\\]\\]`, "g")
@@ -61,7 +60,6 @@ module.exports = async (app, req, res, api, getLevels) => {
})
return res.send(html)
})
-
- })
})
- }
\ No newline at end of file
+ })
+}
\ No newline at end of file
diff --git a/api/search.js b/api/search.js
index 86e7ff37..0c728b1a 100644
--- a/api/search.js
+++ b/api/search.js
@@ -1,94 +1,92 @@
+"use strict";
const request = require('request')
-const music = require('../misc/music.json')
const Level = require('../classes/Level.js')
let demonList = {}
module.exports = async (app, req, res) => {
+ const {query} = req
+ if (req.offline) return res.status(500).send(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"
+ let demonMode = query.hasOwnProperty("demonlist") || query.hasOwnProperty("demonList") || query.type == "demonlist" || query.type == "demonList"
if (demonMode) {
if (!req.server.demonList) return res.sendError(400)
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) {
+ let demonStr = req.server.demonList + 'api/v2/demons/listed/?limit=100'
+ return request.get(demonStr, function (err1, resp1, list1) {
if (err1) return res.sendError()
- else return request.get(req.server.demonList + 'api/v2/demons/listed/?limit=100&after=100', function (err2, resp2, list2) {
+ else return request.get(demonStr + '&after=100', function (err2, resp2, list2) {
if (err2) return res.sendError()
- demonList[req.id] = {list: JSON.parse(list1).concat(JSON.parse(list2)).map(x => String(x.level_id)), lastUpdated: Date.now()}
+ 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)
})
})
}
}
- let amount = 10;
- let count = req.isGDPS ? 10 : +req.query.count
- if (count && count > 0) {
- if (count > 500) amount = 500
- else amount = count;
- }
-
+ let amount = 10
+ let count = req.isGDPS ? 10 : +query.count
+ if (count && count > 0) amount = Math.min(count, 500)
+
let filters = {
str: req.params.text,
- diff: req.query.diff,
- demonFilter: req.query.demonFilter,
- page: req.query.page || 0,
- gauntlet: req.query.gauntlet || 0,
- len: req.query.length,
- song: req.query.songID,
- followed: req.query.creators,
-
- featured: req.query.hasOwnProperty("featured") ? 1 : 0,
- originalOnly: req.query.hasOwnProperty("original") ? 1 : 0,
- twoPlayer: req.query.hasOwnProperty("twoPlayer") ? 1 : 0,
- coins: req.query.hasOwnProperty("coins") ? 1 : 0,
- epic: req.query.hasOwnProperty("epic") ? 1 : 0,
- star: req.query.hasOwnProperty("starred") ? 1 : 0,
- noStar: req.query.hasOwnProperty("noStar") ? 1 : 0,
- customSong: req.query.hasOwnProperty("customSong") ? 1 : 0,
-
- type: req.query.type || 0,
+ diff: query.diff,
+ demonFilter: query.demonFilter,
+ page: query.page || 0,
+ gauntlet: query.gauntlet || 0,
+ len: query.length,
+ song: query.songID,
+ followed: query.creators,
+
+ featured: query.hasOwnProperty("featured") ? 1 : 0,
+ originalOnly: query.hasOwnProperty("original") ? 1 : 0,
+ twoPlayer: query.hasOwnProperty("twoPlayer") ? 1 : 0,
+ coins: query.hasOwnProperty("coins") ? 1 : 0,
+ epic: query.hasOwnProperty("epic") ? 1 : 0,
+ star: query.hasOwnProperty("starred") ? 1 : 0,
+ noStar: query.hasOwnProperty("noStar") ? 1 : 0,
+ customSong: query.hasOwnProperty("customSong") ? 1 : 0,
+
+ type: query.type || 0,
count: amount
}
- if (req.query.type) {
- let filterCheck = req.query.type.toLowerCase()
- switch(filterCheck) {
- case 'mostdownloaded': filters.type = 1; break;
- case 'mostliked': filters.type = 2; break;
- case 'trending': filters.type = 3; break;
- case 'recent': filters.type = 4; break;
- case 'featured': filters.type = 6; break;
- case 'magic': filters.type = 7; break;
- case 'awarded': filters.type = 11; break;
- case 'starred': filters.type = 11; break;
- case 'halloffame': filters.type = 16; break;
- case 'hof': filters.type = 16; break;
- case 'gdw': filters.type = 17; break;
- case 'gdworld': filters.type = 17; break;
+ if (query.type) {
+ let filterCheck = query.type.toLowerCase()
+ let typeMap = {
+ 'mostdownloaded': 1, 'mostliked': 2,
+ 'trending': 3, 'recent': 4,
+ 'featured': 6, 'magic': 7,
+ 'awarded': 11, 'starred': 11,
+ 'halloffame': 16, 'hof': 16,
+ 'gdw': 17, 'gdworld': 17
}
+ if (typeMap.hasOwnProperty(filterCheck)) // JIC there's no match
+ filters.type = typeMap[filterCheck]
}
- if (req.query.hasOwnProperty("user")) {
+ if (query.hasOwnProperty("user")) {
let accountCheck = app.userCache(req.id, filters.str)
filters.type = 5
if (accountCheck) filters.str = accountCheck[1]
- else if (!filters.str.match(/^[0-9]*$/)) return app.run.profile(app, req, res, null, req.params.text)
- }
+ else if ( !(/^\d*$/).test(filters.str) ) return app.run.profile(app, req, res, null, req.params.text)
+ }
- if (req.query.hasOwnProperty("creators")) filters.type = 12
+ if (query.hasOwnProperty("creators")) filters.type = 12
let listSize = 10
- if (demonMode || req.query.gauntlet || req.query.type == "saved" || ["mappack", "list", "saved"].some(x => req.query.hasOwnProperty(x))) {
+ if (demonMode || query.gauntlet || query.type == "saved" || ["mappack", "list", "saved"].some(x => query.hasOwnProperty(x))) {
filters.type = 10
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.sendError(400)
- filters.str = filters.str.map(x => String(Number(x) + (+req.query.l || 0))).join()
+ filters.str = filters.str.map(x => String(Number(x) + (+query.l || 0))).join()
filters.page = 0
}
@@ -103,25 +101,25 @@ module.exports = async (app, req, res) => {
let authorList = {}
let songList = {}
let authors = splitBody[1].split('|')
- let songs = splitBody[2]; songs = songs.split('~:~').map(x => app.parseResponse(`~${x}~`, '~|~'))
+ let songs = splitBody[2].split('~:~').map(x => app.parseResponse(`~${x}~`, '~|~'))
songs.forEach(x => {songList[x['~1']] = x['2']})
authors.forEach(x => {
- if (x.startsWith('~')) return
- let arr = x.split(':')
- authorList[arr[0]] = [arr[1], arr[2]]})
+ if (x.startsWith('~')) return
+ let arr = x.split(':')
+ authorList[arr[0]] = [arr[1], arr[2]]
+ })
let levelArray = preRes.map(x => app.parseResponse(x)).filter(x => x[1])
let parsedLevels = []
levelArray.forEach((x, y) => {
-
let songSearch = songs.find(y => y['~1'] == x[35]) || []
let level = new Level(x, req.server).getSongInfo(songSearch)
if (!level.id) return
- level.author = authorList[x[6]] ? authorList[x[6]][0] : "-";
- level.accountID = authorList[x[6]] ? authorList[x[6]][1] : "0";
+ level.author = authorList[x[6]] ? authorList[x[6]][0] : "-"
+ level.accountID = authorList[x[6]] ? authorList[x[6]][1] : "0"
if (demonMode) {
if (!y) level.demonList = req.server.demonList
@@ -133,21 +131,21 @@ module.exports = async (app, req, res) => {
//this is broken if you're not on page 0, blame robtop
if (filters.page == 0 && y == 0 && splitBody[3]) {
- let pages = splitBody[3].split(":");
+ let pages = splitBody[3].split(":")
if (filters.gauntlet) { // gauntlet page stuff
- level.results = levelArray.length
+ level.results = levelArray.length
level.pages = 1
}
else if (filters.type == 10) { // custom page stuff
level.results = listSize
- level.pages = +Math.ceil(listSize / (amount || 10))
+ level.pages = Math.ceil(listSize / (amount || 10))
}
else { // normal page stuff
- level.results = +pages[0];
- level.pages = +pages[0] == 9999 ? 1000 : +Math.ceil(pages[0] / amount);
+ level.results = +pages[0]
+ level.pages = +pages[0] == 9999 ? 1000 : Math.ceil(pages[0] / amount)
}
}
diff --git a/api/song.js b/api/song.js
index 1439318d..efcf294f 100644
--- a/api/song.js
+++ b/api/song.js
@@ -1,10 +1,12 @@
+"use strict";
module.exports = async (app, req, res) => {
if (req.offline) return res.sendError()
let songID = req.params.song
req.gdRequest('getGJSongInfo', {songID: songID}, function(err, resp, body) {
- if (err) return res.sendError(400)
- return res.send(!body.startsWith("-") && body.length > 10)
+ return err
+ ? res.sendError(400)
+ : res.send(!body.startsWith("-") && body.length > 10)
})
-}
\ No newline at end of file
+}
diff --git a/assets/css/boomlings.css b/assets/css/boomlings.css
index 814fdd60..4aff6a06 100644
--- a/assets/css/boomlings.css
+++ b/assets/css/boomlings.css
@@ -184,7 +184,7 @@ h2 {
.brownBox {
border: 2.5vh solid transparent;
border-radius: 3vh;
- background-color: #995533;
+ background-color: #953;
border-image: url('../../assets/brownbox.png') 10% round;
}
@@ -205,4 +205,4 @@ h2 {
-webkit-transform: rotate(360deg);
transform: rotate(360deg)
}
-}
\ No newline at end of file
+}
diff --git a/assets/css/browser.css b/assets/css/browser.css
index 1df0dac9..dc2cbf66 100644
--- a/assets/css/browser.css
+++ b/assets/css/browser.css
@@ -54,9 +54,9 @@ img, .noSelect {
}
.supercenter {
- position: absolute;
- top: 50%;
- left: 50%;
+ position: absolute;
+ top: 50%;
+ left: 50%;
transform: translate(-50%,-50%);
}
@@ -252,7 +252,7 @@ textarea::-webkit-scrollbar {
width: 1.5vh;
background: #6d3c24;
}
-
+
textarea::-webkit-scrollbar-thumb {
background: rgb(83, 47, 28);
overflow: hidden;
@@ -345,7 +345,7 @@ input::-webkit-outer-spin-button, input::-webkit-inner-spin-button {
border-width: 2.5vh;
border-style: solid;
border-radius: 3vh;
- background-color: #995533;
+ background-color: #953;
border-image: url('../../assets/brownbox.png') 10% round;
}
@@ -353,7 +353,7 @@ input::-webkit-outer-spin-button, input::-webkit-inner-spin-button {
border-width: 2.5vh;
border-style: solid;
border-radius: 3vh;
- background-color: #334499;
+ background-color: #349;
border-image: url('../../assets/bluebox.png') 10% round;
}
@@ -563,8 +563,8 @@ input::-webkit-outer-spin-button, input::-webkit-inner-spin-button {
}
.popup {
- position: fixed;
- display: none;
+ position: fixed;
+ display: none;
width: 100%;
height: 100%;
top: 0; left: 0; right: 0; bottom: 0;
@@ -619,9 +619,9 @@ input::-webkit-outer-spin-button, input::-webkit-inner-spin-button {
}
#scoreTabs {
- position: absolute;
+ position: absolute;
text-align: center;
- top: 2.45%;
+ top: 2.45%;
width: 100%;
margin-left: -13.5vh
}
@@ -629,13 +629,13 @@ input::-webkit-outer-spin-button, input::-webkit-inner-spin-button {
#searchBox {
background-color: rgb(173, 115, 76);
width: 122vh;
- height: 75%;
- overflow-y: auto;
+ height: 75%;
+ overflow-y: auto;
overflow-x: hidden;
}
-#searchBox::-webkit-scrollbar, #statusDiv::-webkit-scrollbar, #commentBox::-webkit-scrollbar {
- display: none;
+#searchBox::-webkit-scrollbar, #statusDiv::-webkit-scrollbar, #commentBox::-webkit-scrollbar {
+ display: none;
}
.searchResult {
@@ -703,7 +703,7 @@ input::-webkit-outer-spin-button, input::-webkit-inner-spin-button {
.comment h2, .smallGold {
- background: -webkit-linear-gradient(#e28000, #ffee44);
+ background: -webkit-linear-gradient(#e28000, #fe4);
background-clip: text;
width: fit-content;
vertical-align: top;
@@ -902,7 +902,7 @@ input::-webkit-outer-spin-button, input::-webkit-inner-spin-button {
background-color: #BE6F3F;
overflow-y: scroll;
scrollbar-width: none;
- -ms-overflow-style: none;
+ -ms-overflow-style: none;
}
#msgList::-webkit-scrollbar {
@@ -1029,7 +1029,7 @@ input::-webkit-outer-spin-button, input::-webkit-inner-spin-button {
}
.analysis::-webkit-scrollbar-thumb, .levelCode::-webkit-scrollbar-thumb {
- background: rgba(0, 0, 0, 0.3);
+ background: rgba(0, 0, 0, 0.3);
overflow: hidden;
}
@@ -1292,7 +1292,7 @@ input::-webkit-outer-spin-button, input::-webkit-inner-spin-button {
.gdpslogo {
margin-bottom: 0%;
border-radius: 10%;
- filter: drop-shadow(0.5vh 0.5vh 0.15vh rgba(0, 0, 0, 0.6))
+ filter: drop-shadow(0.5vh 0.5vh 0.15vh rgba(0, 0, 0, 0.6))
}
.menuDisabled, .downloadDisabled {
@@ -1311,14 +1311,14 @@ input::-webkit-outer-spin-button, input::-webkit-inner-spin-button {
cr { color: #ff5a5a }
co { color: #ffa54b }
-cy { color: #ffff00 }
+cy { color: #ff0 }
cg { color: #40e348 }
-ca { color: #00ffff }
+ca { color: #0ff }
cb { color: #60abef }
-cp { color: #ff00ff }
+cp { color: #f0f }
.red {
- color: #ff0000 !important;
+ color: #f00 !important;
}
.yellow {
@@ -1350,11 +1350,11 @@ cp { color: #ff00ff }
}
.brightblue {
- color: #99ffff
+ color: #9ff
}
.brightred {
- color: #ffaaaa;
+ color: #faa;
}
.gray {
@@ -1380,10 +1380,10 @@ cp { color: #ff00ff }
@-moz-keyframes spin { 100% { -moz-transform: rotate(360deg); } }
@-webkit-keyframes spin { 100% { -webkit-transform: rotate(360deg); } }
-@keyframes spin {
- 100% {
- -webkit-transform: rotate(360deg);
- transform:rotate(360deg); }
+@keyframes spin {
+ 100% {
+ -webkit-transform: rotate(360deg);
+ transform:rotate(360deg); }
}
@keyframes boxAnimator {
@@ -1434,11 +1434,11 @@ cp { color: #ff00ff }
* Also disabled on devices that may be slow to render new frames for performance optimization
*/
@media screen and
- (prefers-reduced-motion: reduce),
+ (prefers-reduced-motion: reduce),
(update: slow) {
*, *::before, *::after {
animation-duration: 0.001ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.001ms !important;
}
-}
\ No newline at end of file
+}
diff --git a/assets/css/iconkit.css b/assets/css/iconkit.css
index 96f0846a..d976b564 100644
--- a/assets/css/iconkit.css
+++ b/assets/css/iconkit.css
@@ -14,7 +14,7 @@
}
body {
- background-color: 555555;
+ background-color: #555;
}
p {
@@ -144,9 +144,9 @@ input:focus, select:focus, textarea:focus, button:focus {
}
.supercenter {
- position: absolute;
- top: 50%;
- left: 50%;
+ position: absolute;
+ top: 50%;
+ left: 50%;
transform: translate(-50%,-50%);
}
@@ -194,7 +194,7 @@ input:focus, select:focus, textarea:focus, button:focus {
padding: 10 10 10 10;
overflow: auto;
white-space: nowrap;
- border-radius: 8px;
+ border-radius: 8px;
}
#symbols p {
@@ -238,8 +238,8 @@ input:focus, select:focus, textarea:focus, button:focus {
#generate {
font-family: "Roboto";
border: rgba(0, 0, 0, 0);
- background-color: #88FF33;
- box-shadow: 2px 2px 3px #66AA22;
+ background-color: #8F3;
+ box-shadow: 2px 2px 3px #6A2;
border-radius: 10px;
color: black;
padding: 10px 15px 10px 15px;
@@ -259,8 +259,8 @@ input:focus, select:focus, textarea:focus, button:focus {
.miniButton {
font-family: "Roboto";
border: rgba(0, 0, 0, 0);
- background-color: #88FF33;
- box-shadow: 1.5px 1.5px 2px #66AA22;
+ background-color: #8F3;
+ box-shadow: 1.5px 1.5px 2px #6A2;
border-radius: 10px;
color: black;
padding: 7px 9px 7px 9px;
@@ -375,11 +375,11 @@ input:focus, select:focus, textarea:focus, button:focus {
#colors::-webkit-scrollbar, #iconKitParent::-webkit-scrollbar {
width: 9px;
height: 10px;
- background: rgba(0, 0, 0, 0.5);
+ background: rgba(0, 0, 0, 0.5);
}
#colors::-webkit-scrollbar-thumb, #iconKitParent::-webkit-scrollbar-thumb {
- background: rgb(185, 185, 185);
+ background: rgb(185, 185, 185);
}
#iconKitParent {
@@ -394,7 +394,7 @@ input:focus, select:focus, textarea:focus, button:focus {
.iconColor div {
width: 50px;
- height: 50px;
+ height: 50px;
}
#gdfloor {
@@ -423,13 +423,13 @@ input:focus, select:focus, textarea:focus, button:focus {
}
#iconprogressbar {
- background: rgba(0, 0, 0, 0.5);
+ background: rgba(0, 0, 0, 0.5);
height: 7px;
margin-bottom: 12px;
}
#iconloading {
- background: rgba(255, 255, 255, 0.5);
+ background: rgba(255, 255, 255, 0.5);
height: 7px;
width: 0%;
}
@@ -447,8 +447,8 @@ body::-webkit-scrollbar {
}
.popup {
- position: fixed;
- display: none;
+ position: fixed;
+ display: none;
width: 100%;
height: 100%;
top: 0; left: 0; right: 0; bottom: 0;
@@ -459,7 +459,7 @@ body::-webkit-scrollbar {
.brownBox {
border: 17px solid rgba(0, 0, 0, 0);
border-radius: 25px;
- background-color: #995533;
+ background-color: #953;
border-image: url('../../assets/brownbox.png') 10% round;
}
@@ -686,4 +686,4 @@ input[type="range"]::-webkit-slider-thumb:active { background-image: url("../../
@-moz-keyframes spin { 100% { -moz-transform: rotate(360deg); } }
@-webkit-keyframes spin { 100% { -webkit-transform: rotate(360deg); } }
-@keyframes spin { 100% { -webkit-transform: rotate(360deg); transform:rotate(360deg); } }
\ No newline at end of file
+@keyframes spin { 100% { -webkit-transform: rotate(360deg); transform:rotate(360deg); } }
diff --git a/classes/Level.js b/classes/Level.js
index ad3b1065..17dc44a4 100644
--- a/classes/Level.js
+++ b/classes/Level.js
@@ -1,16 +1,19 @@
-const XOR = require(__dirname + "/../classes/XOR");
-const music = require(__dirname + "/../misc/music.json");
+"use strict";
+const XOR = require("./XOR")
+const music = require("../misc/music.json")
let orbs = [0, 0, 50, 75, 125, 175, 225, 275, 350, 425, 500]
let length = ['Tiny', 'Short', 'Medium', 'Long', 'XL']
+// this can't be shortened with a loop
let difficulty = { 0: 'Unrated', 10: 'Easy', 20: 'Normal', 30: 'Hard', 40: 'Harder', 50: 'Insane' }
let demonTypes = { 3: "Easy", 4: "Medium", 5: "Insane", 6: "Extreme" }
+let dailyLimit = 100000
class Level {
constructor(levelInfo, server, download, author = []) {
- this.name = levelInfo[2] || "-";
- this.id = levelInfo[1] || 0;
- this.description = Buffer.from((levelInfo[3] || ""), "base64").toString() || "(No description provided)";
+ this.name = levelInfo[2] || "-"
+ this.id = levelInfo[1] || 0
+ this.description = Buffer.from((levelInfo[3] || ""), "base64").toString() || "(No description provided)"
this.author = author[1] || "-"
this.playerID = levelInfo[6] || 0
this.accountID = author[2] || 0
@@ -29,8 +32,8 @@ class Level {
if (levelInfo[29]) this.updated = levelInfo[29] + (server.timestampSuffix || "")
if (levelInfo[46]) this.editorTime = +levelInfo[46] || 0
if (levelInfo[47]) this.totalEditorTime = +levelInfo[47] || 0
- if (levelInfo[27]) this.password = levelInfo[27];
- this.version = +levelInfo[5] || 0;
+ if (levelInfo[27]) this.password = levelInfo[27]
+ this.version = +levelInfo[5] || 0
this.copiedID = levelInfo[30] || "0"
this.twoPlayer = levelInfo[31] > 0
this.officialSong = +levelInfo[35] ? 0 : parseInt(levelInfo[12]) + 1
@@ -39,21 +42,24 @@ class Level {
this.verifiedCoins = levelInfo[38] > 0
this.starsRequested = +levelInfo[39] || 0
this.ldm = levelInfo[40] > 0
- if (+levelInfo[41] > 100000) this.weekly = true
- if (+levelInfo[41]) { this.dailyNumber = (+levelInfo[41] > 100000 ? +levelInfo[41] - 100000 : +levelInfo[41]); this.nextDaily = null; this.nextDailyTimestamp = null }
+ if (+levelInfo[41] > dailyLimit) this.weekly = true
+ if (+levelInfo[41]) {
+ this.dailyNumber = (+levelInfo[41] > dailyLimit ? +levelInfo[41] - dailyLimit : +levelInfo[41])
+ this.nextDaily = null
+ this.nextDailyTimestamp = null
+ }
this.objects = +levelInfo[45] || 0
- this.large = levelInfo[45] > 40000;
+ this.large = levelInfo[45] > 40000
this.cp = Number((this.stars > 0) + this.featured + this.epic)
if (levelInfo[17] > 0) this.difficulty = (demonTypes[levelInfo[43]] || "Hard") + " Demon"
if (levelInfo[25] > 0) this.difficulty = 'Auto'
- this.difficultyFace = `${levelInfo[17] != 1 ? this.difficulty.toLowerCase() : `demon-${this.difficulty.toLowerCase().split(' ')[0]}`}${this.epic ? '-epic' : `${this.featured ? '-featured' : ''}`}`
+ this.difficultyFace = `${levelInfo[17] != 1 ? this.difficulty.toLowerCase() : `demon-${this.difficulty.toLowerCase().split(' ', 1)[0]}`}${this.epic ? '-epic' : `${this.featured ? '-featured' : ''}`}`
if (this.password && this.password != 0) {
- let xor = new XOR();
- let pass = xor.decrypt(this.password, 26364);
- if (pass.length > 1) this.password = pass.slice(1);
- else this.password = pass;
+ let xor = new XOR()
+ let pass = xor.decrypt(this.password, 26364)
+ this.password = pass.length > 1 ? pass.slice(1) : pass
}
if (server.onePointNine) {
@@ -83,9 +89,9 @@ class Level {
this.songSize = "0MB"
this.songID = "Level " + this.officialSong
}
-
+
return this
}
}
-module.exports = Level;
\ No newline at end of file
+module.exports = Level
\ No newline at end of file
diff --git a/classes/Player.js b/classes/Player.js
index 51a70d79..fe4db924 100644
--- a/classes/Player.js
+++ b/classes/Player.js
@@ -1,4 +1,4 @@
-const colors = require('../iconkit/sacredtexts/colors.json');
+const colors = require('../iconkit/sacredtexts/colors.json')
class Player {
constructor(account) {
diff --git a/classes/XOR.js b/classes/XOR.js
index 87287ca0..51bbb4e6 100644
--- a/classes/XOR.js
+++ b/classes/XOR.js
@@ -1,5 +1,16 @@
+//https://nodejs.org/docs/latest/api/buffer.html#buffers-and-character-encodings
+//both only work on "binary strings" and "URI-safe B64"
+let toB64 = str => Buffer.from(str).toString('base64url')
+let fromB64 = str => Buffer.from(str, 'base64').toString()
+
+const defKey = 37526
+
module.exports = class XOR {
- 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(/./gs, c => ({'/': '_', '+': '-'}[c] || c)); }
- decrypt(str, key = 37526) { return this.xor(Buffer.from(str.replace(/./gs, c => ({'/': '_', '+': '-'}[c] || c)), 'base64').toString(), key) }
-}
+ xor(str, key) {
+ key = key.toString()
+ return String.fromCodePoint(...str.split('')
+ .map((c, i) => c.charCodeAt(0) ^ key.charCodeAt(i % key.length)))
+ }
+ encrypt(str, key = defKey) { return toB64(this.xor(str, key)) }
+ decrypt(str, key = defKey) { return this.xor(fromB64(str), key) }
+}
\ No newline at end of file
diff --git a/html/achievements.html b/html/achievements.html
index ba6858fa..4b37a183 100644
--- a/html/achievements.html
+++ b/html/achievements.html
@@ -21,23 +21,23 @@
Filters
-
Reward
+
Reward
-
+
-
Requirement
+
Requirement
-
+
-
Game
+
Game
-
+
@@ -59,7 +59,7 @@
Game
-
+
@@ -79,10 +79,10 @@
-
-
+
+
\ No newline at end of file
diff --git a/html/analyze.html b/html/analyze.html
index a5d7cfaa..0dd89c63 100644
--- a/html/analyze.html
+++ b/html/analyze.html
@@ -3,11 +3,12 @@
-
+
-
+
+
@@ -100,25 +101,27 @@
-
+
diff --git a/html/api.html b/html/api.html
index d85368ea..cf86340b 100644
--- a/html/api.html
+++ b/html/api.html
@@ -3,11 +3,11 @@
// smooth scrolling through anchors
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function(e) {
- e.preventDefault();
+ e.preventDefault()
document.querySelector(this.getAttribute('href')).scrollIntoView({
behavior: 'smooth'
});
});
});
-
+
// menu button
document.getElementById('menu-btn').onclick = function(){
- document.getElementsByClassName('header-links')[0].classList.toggle('hid');
+ document.getElementsByClassName('header-links')[0].classList.toggle('hid')
document.getElementById('menu-btn').classList.toggle('active');
}
-
+
for(let i = 0; i < document.getElementsByClassName('header-link').length; i++){
document.getElementsByClassName('header-link')[i].onclick = function(){
- document.getElementsByClassName('header-links')[0].classList.toggle('hid');
+ document.getElementsByClassName('header-links')[0].classList.toggle('hid')
document.getElementById('menu-btn').classList.toggle('active');
}
}
diff --git a/html/api_old.html b/html/api_old.html
index 45125f2c..92e81c34 100644
--- a/html/api_old.html
+++ b/html/api_old.html
@@ -3,11 +3,11 @@
I decided to put all 4 of these requests in one section because they're fairly similar ¯\_(ツ)_/¯
// smooth scrolling through anchors
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function(e) {
- e.preventDefault();
+ e.preventDefault()
document.querySelector(this.getAttribute('href')).scrollIntoView({
behavior: 'smooth'
});
});
});
-
+
// menu button
document.getElementById('menu-btn').onclick = function(){
- document.getElementsByClassName('header-links')[0].classList.toggle('hid');
+ document.getElementsByClassName('header-links')[0].classList.toggle('hid')
document.getElementById('menu-btn').classList.toggle('active');
}
-
+
for(let i = 0; i < document.getElementsByClassName('header-link').length; i++){
document.getElementsByClassName('header-link')[i].onclick = function(){
- document.getElementsByClassName('header-links')[0].classList.toggle('hid');
+ document.getElementsByClassName('header-links')[0].classList.toggle('hid')
document.getElementById('menu-btn').classList.toggle('active');
}
}
diff --git a/html/assets.html b/html/assets.html
index 20644c28..5b2c9ae4 100644
--- a/html/assets.html
+++ b/html/assets.html
@@ -3,13 +3,13 @@
-
+
diff --git a/html/boomlings.html b/html/boomlings.html
index 04ace9c0..4eecdbc0 100644
--- a/html/boomlings.html
+++ b/html/boomlings.html
@@ -1,10 +1,10 @@
-
-
-
+
+
+
-
diff --git a/html/demon.html b/html/demon.html
index 9cb842b1..c2395beb 100644
--- a/html/demon.html
+++ b/html/demon.html
@@ -1,9 +1,9 @@