A whole lotta hoopla
CHANGELOG: - Profile posts! This one is long overdue but... yeah! You can now leave profile posts, and drop likes on them as well! - Classes folder! It's a folder... for classes! Don't ask me to explain what a class is, because I'm still not sure myself - Le Level Class has arrived! Instead of parsing level data in three different files, the level class has stepped up to do the dirty work. (thanks to memimoe for helping) - Renamed generate.css to iconkit.css, and deleted soon.css because it's ugly - Removed 404 redirect when trying to view an asset that doesn't exist - Updated readme to get with the times - Removed Herobrine
This commit is contained in:
parent
a2151fcd4c
commit
60da852534
22 changed files with 493 additions and 391 deletions
31
README.md
31
README.md
|
@ -1,6 +1,6 @@
|
|||
# GDBrowser
|
||||
|
||||
Uh... so I've never actually used GitHub. But I'll try to explain everything going on here.
|
||||
Uh... so I've never actually used GitHub before this. But I'll try to explain everything going on here.
|
||||
|
||||
Sorry for my messy code. It's why I was skeptical about making this open source, but you know what, the code runs fine in the end.
|
||||
|
||||
|
@ -16,21 +16,36 @@ Assets! Assets everywhere!
|
|||
|
||||
All the GD stuff was ripped straight from the GD spritesheets via [Absolute's texture splitter hack](https://youtu.be/pYQgIyNhow8). If you want a nice categorized version, [I've done all the dirty work for you.](https://www.mediafire.com/file/4d99bw1zhwcl507/textures.zip/file)
|
||||
|
||||
/blocks and /objects are used for the analysis page. I just put them in seperate folders for extra neatness.
|
||||
/blocks, /objects and /initial are used for the analysis page. I just put them in seperate folders for extra neatness.
|
||||
|
||||
/gdfaces holds all the difficulty faces
|
||||
|
||||
/css has the CSS stuff. They're in a special folder so browsers won't cache them (in case of updates)
|
||||
|
||||
Figure out what /gauntlets and /iconkitbuttons have.
|
||||
|
||||
## Classes
|
||||
What's a class you ask? I still have no idea.
|
||||
|
||||
Seriously, these things are *confusing*
|
||||
|
||||
I guess the best way to put it is uh... super fancy functions???
|
||||
|
||||
Level.js parses the server's disgusting response and sends back a nice object with all the level info
|
||||
|
||||
XOR.js encrypts/decrypts stuff like GD passwords. I stole the code from somewhere so uh if you wrote it, please don't hunt me down
|
||||
|
||||
## HTML
|
||||
The HTML files! Nothing too fancy, since it can all be seen directly from gdbrowser. Note that profile.html and level.html have [[VARIABLES]] (name, id, etc) replaced by the server when they're sent.
|
||||
|
||||
comingsoon.html was used while the site was still in development, I just left it in there as a nice little throwback
|
||||
|
||||
## Icons
|
||||
It's GJ_Gamesheet02 but split into a much more intimidating cluster of a million files. These icons are put together and colored in the monstrosity that is icon.js
|
||||
|
||||
Also contains the very important .plist file
|
||||
parsePlist.js reads GJ_GameSheet02-uhd.plist and magically transforms it into gameSheet.json. Props to 101arrowz for making this
|
||||
|
||||
/cache is a folder for cached generated icons
|
||||
forms.json is a list of the different icon forms, their ingame filenames, and their index in responses from the GD servers
|
||||
|
||||
/iconkit is a folder for the little grey preview icons on the icon kit
|
||||
|
||||
|
@ -53,14 +68,16 @@ objects.json - IDs for portals, orbs, triggers, and misc stuff
|
|||
|
||||
colors.json - The colors for generating icons
|
||||
|
||||
credits.json - Credits! (shown on the homepage)
|
||||
|
||||
level.json - An array of the official GD tracks, and also difficulty face stuff for level searching
|
||||
|
||||
mapPacks.json - The IDs for the levels in map packs. I can't believe I have to hardcode this.
|
||||
mapPacks.json - The IDs for the levels in map packs. I can't believe I have to hardcode this
|
||||
|
||||
secretStuff.json - GJP goes here, needed for level leaderboards. Not included in the repo for obvious reasons
|
||||
|
||||
sizecheck.js - Excecuted on most pages, used for the 'page isn't wide enough' message, back button, and a few other things
|
||||
|
||||
XOR.js - Decrypts ciphered GD passwords. I stole the code from somewhere so uh if you wrote it, please don't hunt me down
|
||||
|
||||
---
|
||||
|
||||
happy painting and god bless.
|
|
@ -41,6 +41,11 @@ module.exports = async (app, req, res) => {
|
|||
comment.ID = x[6]
|
||||
comment.likes = x[4]
|
||||
comment.date = (x[9] || "?") + " ago"
|
||||
if (comment.content.endsWith("⍟")) {
|
||||
comment.content = comment.content.slice(0, -1)
|
||||
comment.browserColor = true
|
||||
}
|
||||
|
||||
if (req.query.type != "profile") {
|
||||
comment.username = y[1] || "Unknown"
|
||||
comment.levelID = x[1] || req.params.id
|
||||
|
@ -48,9 +53,6 @@ module.exports = async (app, req, res) => {
|
|||
comment.accountID = y[16]
|
||||
comment.form = ['icon', 'ship', 'ball', 'ufo', 'wave', 'robot', 'spider'][Number(y[14])]
|
||||
if (x[10] > 0) comment.percent = x[10]
|
||||
if (comment.content.endsWith("⍟")) {
|
||||
comment.content = comment.content.slice(0, -1)
|
||||
comment.browserColor = true }
|
||||
if (x[12] && x[12].includes(',')) comment.modColor = true
|
||||
}
|
||||
|
||||
|
|
209
api/download.js
209
api/download.js
|
@ -1,152 +1,101 @@
|
|||
const request = require('request')
|
||||
const fs = require('fs')
|
||||
const XOR = require('../misc/XOR.js');
|
||||
const xor = new XOR();
|
||||
const Level = require('../classes/Level.js')
|
||||
module.exports = async (app, req, res, api, ID, analyze) => {
|
||||
|
||||
let orbs = [0, 0, 50, 75, 125, 175, 225, 275, 350, 425, 500]
|
||||
let length = ['Tiny', 'Short', 'Medium', 'Long', 'XL']
|
||||
let difficulty = {0: 'Unrated', 10: 'Easy', 20: 'Normal', 30: 'Hard', 40: 'Harder', 50: 'Insane'}
|
||||
let levelID = ID || req.params.id
|
||||
if (levelID == "daily") levelID = -1
|
||||
else if (levelID == "weekly") levelID = -2
|
||||
else levelID = levelID.replace(/[^0-9]/g, "")
|
||||
|
||||
let levelID = ID || req.params.id
|
||||
if (levelID == "daily") levelID = -1
|
||||
else if (levelID == "weekly") levelID = -2
|
||||
else levelID = levelID.replace(/[^0-9]/g, "")
|
||||
request.post('http://boomlings.com/database/downloadGJLevel22.php', {
|
||||
form: {
|
||||
levelID,
|
||||
secret: app.secret
|
||||
}
|
||||
}, async function (err, resp, body) {
|
||||
|
||||
request.post('http://boomlings.com/database/downloadGJLevel22.php', {
|
||||
form : {
|
||||
levelID,
|
||||
secret : app.secret
|
||||
}}, async function(err, resp, body) {
|
||||
if (err || !body || body == '-1') {
|
||||
if (!api && levelID < 0) return res.redirect('/')
|
||||
if (!api) return res.redirect('search/' + req.params.id)
|
||||
else return res.send("-1")
|
||||
}
|
||||
|
||||
if (err || !body || body == '-1') {
|
||||
if (!api && levelID < 0) return res.redirect('/')
|
||||
if (!api) return res.redirect('search/' + req.params.id)
|
||||
else return res.send("-1")
|
||||
}
|
||||
let levelInfo = app.parseResponse(body)
|
||||
let level = new Level(levelInfo)
|
||||
|
||||
let levelInfo = app.parseResponse(body)
|
||||
let level = {
|
||||
name: levelInfo[2],
|
||||
id: levelInfo[1],
|
||||
description: Buffer.from(levelInfo[3], 'base64').toString() || "(No description provided)",
|
||||
author: "-",
|
||||
authorID: levelInfo[6],
|
||||
accountID: 0,
|
||||
difficulty: difficulty[levelInfo[9]],
|
||||
downloads: levelInfo[10],
|
||||
likes: levelInfo[14],
|
||||
disliked : levelInfo[14] < 0,
|
||||
length: length[levelInfo[15]],
|
||||
stars: levelInfo[18],
|
||||
orbs: orbs[levelInfo[18]],
|
||||
diamonds: levelInfo[18] < 2 ? 0 : parseInt(levelInfo[18]) + 2,
|
||||
featured: levelInfo[19] > 0,
|
||||
epic: levelInfo[42] == 1,
|
||||
uploaded: levelInfo[28] + ' ago', //not given in search
|
||||
updated: levelInfo[29] + ' ago', //not given in search
|
||||
version: levelInfo[5],
|
||||
password: levelInfo[27],
|
||||
copiedID: levelInfo[30],
|
||||
officialSong: levelInfo[12] != 0 ? parseInt(levelInfo[12]) + 1 : 0,
|
||||
customSong: levelInfo[35],
|
||||
coins: levelInfo[37],
|
||||
verifiedCoins: levelInfo[38] == 1,
|
||||
starsRequested: levelInfo[39],
|
||||
ldm: levelInfo[40] == 1, //not given in search
|
||||
objects: levelInfo[45] == "65535" ? "65000+" : levelInfo[45],
|
||||
large: levelInfo[45] > 40000,
|
||||
}
|
||||
if (level.password != "0") {
|
||||
request.post('http://boomlings.com/database/getGJUsers20.php', {
|
||||
form: { str: level.authorID, secret: app.secret }
|
||||
}, function (err1, res1, b1) {
|
||||
let gdSearchResult = app.parseResponse(b1)
|
||||
request.post('http://boomlings.com/database/getGJUserInfo20.php', {
|
||||
form: { targetAccountID: gdSearchResult[16], secret: app.secret }
|
||||
}, function (err2, res2, b2) {
|
||||
if (b2 != '-1') {
|
||||
let account = app.parseResponse(b2)
|
||||
level.author = account[1]
|
||||
level.accountID = gdSearchResult[16]
|
||||
}
|
||||
|
||||
let pass = level.password
|
||||
pass = xor.decrypt(pass, 26364);
|
||||
if (pass.length > 1) level.password = pass.slice(1);
|
||||
else level.password = pass
|
||||
}
|
||||
|
||||
level.cp = (level.stars > 0) + level.featured + level.epic
|
||||
|
||||
if (levelInfo[17] == 1) level.difficulty += ' Demon'
|
||||
if (level.difficulty == "Insane Demon") level.difficulty = "Extreme Demon"
|
||||
else if (level.difficulty == "Harder Demon") level.difficulty = "Insane Demon"
|
||||
else if (level.difficulty == "Normal Demon") level.difficulty = "Medium Demon"
|
||||
else if (levelInfo[25] == 1) level.difficulty = 'Auto'
|
||||
level.difficultyFace = `${levelInfo[17] != 1 ? level.difficulty.toLowerCase() : `demon-${level.difficulty.toLowerCase().split(' ')[0]}`}${level.epic ? '-epic' : `${level.featured ? '-featured' : ''}`}`
|
||||
|
||||
|
||||
request.post('http://boomlings.com/database/getGJUsers20.php', {
|
||||
form: {str: level.authorID, secret: app.secret}
|
||||
}, function (err1, res1, b1) {
|
||||
let gdSearchResult = app.parseResponse(b1)
|
||||
request.post('http://boomlings.com/database/getGJUserInfo20.php', {
|
||||
form: {targetAccountID: gdSearchResult[16], secret: app.secret}
|
||||
}, function (err2, res2, b2) {
|
||||
if (b2 != '-1') {
|
||||
let account = app.parseResponse(b2)
|
||||
level.author = account[1]
|
||||
level.accountID = gdSearchResult[16]
|
||||
}
|
||||
|
||||
else {
|
||||
level.author = "-"
|
||||
level.accountID = "0"
|
||||
}
|
||||
else {
|
||||
level.author = "-"
|
||||
level.accountID = "0"
|
||||
}
|
||||
|
||||
request.post('http://boomlings.com/database/getGJSongInfo.php', {
|
||||
form : {
|
||||
songID : level.customSong,
|
||||
secret : app.secret
|
||||
}}, async function(err, resp, songRes) {
|
||||
form: {
|
||||
songID: level.customSong,
|
||||
secret: app.secret
|
||||
}
|
||||
}, async function (err, resp, songRes) {
|
||||
|
||||
if (songRes != '-1') {
|
||||
let songData = app.parseResponse(songRes, '~|~')
|
||||
level.songName = songData[2] || "Unknown"
|
||||
level.songAuthor = songData[4] || "Unknown"
|
||||
level.songSize = (songData[5] || "0") + "MB"
|
||||
level.songID = songData[1] || level.customSong
|
||||
if (!songData[2]) level.invalidSong = true
|
||||
}
|
||||
if (songRes != '-1') {
|
||||
let songData = app.parseResponse(songRes, '~|~')
|
||||
level.songName = songData[2] || "Unknown"
|
||||
level.songAuthor = songData[4] || "Unknown"
|
||||
level.songSize = (songData[5] || "0") + "MB"
|
||||
level.songID = songData[1] || level.customSong
|
||||
if (!songData[2]) level.invalidSong = true
|
||||
}
|
||||
|
||||
else {
|
||||
let foundSong = require('../misc/level.json').music[parseInt(levelInfo[12]) + 1] || {"null": true}
|
||||
level.songName = foundSong[0] || "Unknown"
|
||||
level.songAuthor = foundSong[1] || "Unknown"
|
||||
level.songSize = "0MB"
|
||||
level.songID = "Level " + [parseInt(levelInfo[12]) + 1]
|
||||
}
|
||||
else {
|
||||
let foundSong = require('../misc/level.json').music[parseInt(levelInfo[12]) + 1] || { "null": true }
|
||||
level.songName = foundSong[0] || "Unknown"
|
||||
level.songAuthor = foundSong[1] || "Unknown"
|
||||
level.songSize = "0MB"
|
||||
level.songID = "Level " + [parseInt(levelInfo[12]) + 1]
|
||||
}
|
||||
|
||||
level.data = levelInfo[4]
|
||||
level.data = levelInfo[4]
|
||||
|
||||
if (analyze) return app.modules.analyze(app, req, res, level)
|
||||
if (analyze) return app.modules.analyze(app, req, res, level)
|
||||
|
||||
function sendLevel() {
|
||||
if (api) return res.send(level)
|
||||
function sendLevel() {
|
||||
if (api) return res.send(level)
|
||||
|
||||
else return fs.readFile('./html/level.html', 'utf8', function (err, data) {
|
||||
let html = data;
|
||||
let variables = Object.keys(level)
|
||||
variables.forEach(x => {
|
||||
let regex = new RegExp(`\\[\\[${x.toUpperCase()}\\]\\]`, "g")
|
||||
html = html.replace(regex, app.clean(level[x]))
|
||||
})
|
||||
return res.send(html)
|
||||
})
|
||||
}
|
||||
|
||||
if (level.difficulty == "Extreme Demon") {
|
||||
request.get('https://www.pointercrate.com/api/v1/demons/?name=' + level.name.trim(), function (err, resp, demonList) {
|
||||
let demon = JSON.parse(demonList)
|
||||
if (demon[0] && demon[0].position <= 150) level.demonList = demon[0].position
|
||||
return sendLevel()
|
||||
})
|
||||
}
|
||||
|
||||
return sendLevel()
|
||||
|
||||
else return fs.readFile('./html/level.html', 'utf8', function(err, data) {
|
||||
let html = data;
|
||||
let variables = Object.keys(level)
|
||||
variables.forEach(x => {
|
||||
let regex = new RegExp(`\\[\\[${x.toUpperCase()}\\]\\]`, "g")
|
||||
html = html.replace(regex, app.clean(level[x]))
|
||||
})
|
||||
return res.send(html)
|
||||
})
|
||||
}
|
||||
|
||||
//demon list stuff
|
||||
if (level.difficulty == "Extreme Demon") {
|
||||
request.get('https://www.pointercrate.com/api/v1/demons/?name=' + level.name.trim(), async function(err, resp, demonList) {
|
||||
let demon = JSON.parse(demonList)
|
||||
if (demon[0] && demon[0].position <= 150) level.demonList = demon[0].position
|
||||
return sendLevel()
|
||||
})
|
||||
}
|
||||
|
||||
else return sendLevel()
|
||||
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
116
api/level.js
116
api/level.js
|
@ -1,29 +1,27 @@
|
|||
const request = require('request')
|
||||
const fs = require('fs')
|
||||
const Level = require('../classes/Level.js')
|
||||
|
||||
module.exports = async (app, req, res, api, analyze) => {
|
||||
|
||||
let orbs = [0, 0, 50, 75, 125, 175, 225, 275, 350, 425, 500]
|
||||
let length = ['Tiny', 'Short', 'Medium', 'Long', 'XL']
|
||||
let difficulty = {0: 'Unrated', 10: 'Easy', 20: 'Normal', 30: 'Hard', 40: 'Harder', 50: 'Insane'}
|
||||
|
||||
let levelID = req.params.id
|
||||
if (levelID == "daily") return app.modules.download(app, req, res, api, 'daily', analyze)
|
||||
else if (levelID == "weekly") return app.modules.download(app, req, res, api, 'weekly', analyze)
|
||||
else if (levelID.match(/[^0-9]/)) {
|
||||
if (!api) return res.redirect('search/' + req.params.id)
|
||||
else return res.send("-1")
|
||||
if (!api) return res.redirect('search/' + req.params.id)
|
||||
else return res.send("-1")
|
||||
}
|
||||
else levelID = levelID.replace(/[^0-9]/g, "")
|
||||
|
||||
if (analyze || req.query.hasOwnProperty("download")) return app.modules.download(app, req, res, api, levelID, analyze)
|
||||
|
||||
request.post('http://boomlings.com/database/getGJLevels21.php', {
|
||||
form : {
|
||||
str : levelID,
|
||||
secret : app.secret,
|
||||
form: {
|
||||
str: levelID,
|
||||
secret: app.secret,
|
||||
type: 0
|
||||
}}, async function(err, resp, body) {
|
||||
}
|
||||
}, async function (err, resp, body) {
|
||||
|
||||
if (err || !body || body == '-1') {
|
||||
if (!api) return res.redirect('search/' + req.params.id)
|
||||
|
@ -32,90 +30,52 @@ module.exports = async (app, req, res, api, analyze) => {
|
|||
|
||||
let preRes = body.split('#')[0].split('|', 10)
|
||||
let author = body.split('#')[1].split('|')[0].split(':')
|
||||
let song = '~' + body.split('#')[2];
|
||||
song = app.parseResponse(song, '~|~')
|
||||
let song = '~' + body.split('#')[2];
|
||||
song = app.parseResponse(song, '~|~')
|
||||
|
||||
let levelInfo = app.parseResponse(preRes[0])
|
||||
let level = {
|
||||
name: levelInfo[2],
|
||||
id: levelInfo[1],
|
||||
description: Buffer.from(levelInfo[3], 'base64').toString() || "(No description provided)",
|
||||
author: author[1] || "-",
|
||||
authorID: levelInfo[6],
|
||||
accountID: author[2] || 0,
|
||||
difficulty: difficulty[levelInfo[9]],
|
||||
downloads: levelInfo[10],
|
||||
likes: levelInfo[14],
|
||||
disliked : levelInfo[14] < 0,
|
||||
length: length[levelInfo[15]] || "?",
|
||||
stars: levelInfo[18],
|
||||
orbs: orbs[levelInfo[18]],
|
||||
diamonds: levelInfo[18] < 2 ? 0 : parseInt(levelInfo[18]) + 2,
|
||||
featured: levelInfo[19] > 0,
|
||||
epic: levelInfo[42] == 1,
|
||||
//uploaded: levelInfo[28] + ' ago',
|
||||
//updated: levelInfo[29] + ' ago',
|
||||
version: levelInfo[5],
|
||||
copiedID: levelInfo[30],
|
||||
officialSong: levelInfo[12] != 0 ? parseInt(levelInfo[12]) + 1 : 0,
|
||||
customSong: levelInfo[35],
|
||||
coins: levelInfo[37],
|
||||
verifiedCoins: levelInfo[38] == 1,
|
||||
starsRequested: levelInfo[39],
|
||||
//ldm: levelInfo[40] == 1, //not given in search
|
||||
objects: levelInfo[45] == "65535" ? "65000+" : levelInfo[45],
|
||||
large: levelInfo[45] > 40000
|
||||
}
|
||||
let level = new Level(levelInfo, author)
|
||||
|
||||
level.cp = (level.stars > 0) + level.featured + level.epic
|
||||
|
||||
if (levelInfo[17] == 1) level.difficulty += ' Demon'
|
||||
if (level.difficulty == "Insane Demon") level.difficulty = "Extreme Demon"
|
||||
else if (level.difficulty == "Harder Demon") level.difficulty = "Insane Demon"
|
||||
else if (level.difficulty == "Normal Demon") level.difficulty = "Medium Demon"
|
||||
else if (levelInfo[25] == 1) level.difficulty = 'Auto'
|
||||
level.difficultyFace = `${levelInfo[17] != 1 ? level.difficulty.toLowerCase() : `demon-${level.difficulty.toLowerCase().split(' ')[0]}`}${level.epic ? '-epic' : `${level.featured ? '-featured' : ''}`}`
|
||||
if (song[2]) {
|
||||
level.songName = song[2] || "Unknown"
|
||||
level.songAuthor = song[4] || "Unknown"
|
||||
level.songSize = (song[5] || "0") + "MB"
|
||||
level.songID = song[1] || level.customSong
|
||||
}
|
||||
|
||||
if (song[2]) {
|
||||
level.songName = song[2] || "Unknown"
|
||||
level.songAuthor = song[4] || "Unknown"
|
||||
level.songSize = (song[5] || "0") + "MB"
|
||||
level.songID = song[1] || level.customSong
|
||||
}
|
||||
|
||||
else {
|
||||
let foundSong = require('../misc/level.json').music[parseInt(levelInfo[12]) + 1] || {"null": true}
|
||||
level.songName = foundSong[0] || "Unknown"
|
||||
level.songAuthor = foundSong[1] || "Unknown"
|
||||
level.songSize = "0MB"
|
||||
level.songID = "Level " + [parseInt(levelInfo[12]) + 1]
|
||||
}
|
||||
else {
|
||||
let foundSong = require('../misc/level.json').music[parseInt(levelInfo[12]) + 1] || { "null": true }
|
||||
level.songName = foundSong[0] || "Unknown"
|
||||
level.songAuthor = foundSong[1] || "Unknown"
|
||||
level.songSize = "0MB"
|
||||
level.songID = "Level " + [parseInt(levelInfo[12]) + 1]
|
||||
}
|
||||
|
||||
function sendLevel() {
|
||||
|
||||
if (api) return res.send(level)
|
||||
if (api) return res.send(level)
|
||||
|
||||
else return fs.readFile('./html/level.html', 'utf8', function(err, data) {
|
||||
let html = data;
|
||||
let variables = Object.keys(level)
|
||||
variables.forEach(x => {
|
||||
let regex = new RegExp(`\\[\\[${x.toUpperCase()}\\]\\]`, "g")
|
||||
html = html.replace(regex, app.clean(level[x]))
|
||||
else return fs.readFile('./html/level.html', 'utf8', function (err, data) {
|
||||
let html = data;
|
||||
let variables = Object.keys(level)
|
||||
variables.forEach(x => {
|
||||
let regex = new RegExp(`\\[\\[${x.toUpperCase()}\\]\\]`, "g")
|
||||
html = html.replace(regex, app.clean(level[x]))
|
||||
})
|
||||
return res.send(html)
|
||||
})
|
||||
return res.send(html)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//demon list stuff
|
||||
if (level.difficulty == "Extreme Demon") {
|
||||
request.get('https://www.pointercrate.com/api/v1/demons/?name=' + level.name.trim(), async function(err, resp, demonList) {
|
||||
if (level.difficulty == "Extreme Demon") {
|
||||
request.get('https://www.pointercrate.com/api/v1/demons/?name=' + level.name.trim(), function (err, resp, demonList) {
|
||||
let demon = JSON.parse(demonList)
|
||||
if (demon[0] && demon[0].position <= 150) level.demonList = demon[0].position
|
||||
return sendLevel()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
else return sendLevel()
|
||||
|
||||
})
|
||||
})
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
const request = require('request')
|
||||
const XOR = require('../misc/XOR.js');
|
||||
const XOR = require('../classes/XOR.js');
|
||||
const xor = new XOR();
|
||||
const crypto = require('crypto')
|
||||
function sha1(data) { return crypto.createHash("sha1").update(data, "binary").digest("hex"); }
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
const request = require('request')
|
||||
const XOR = require('../misc/XOR.js');
|
||||
const XOR = require('../classes/XOR.js');
|
||||
const xor = new XOR();
|
||||
const crypto = require('crypto')
|
||||
function sha1(data) { return crypto.createHash("sha1").update(data, "binary").digest("hex"); }
|
||||
|
|
40
api/postProfileComment.js
Normal file
40
api/postProfileComment.js
Normal file
|
@ -0,0 +1,40 @@
|
|||
const request = require('request')
|
||||
const XOR = require('../classes/XOR.js');
|
||||
const xor = new XOR();
|
||||
const crypto = require('crypto')
|
||||
function sha1(data) { return crypto.createHash("sha1").update(data, "binary").digest("hex"); }
|
||||
|
||||
module.exports = async (app, req, res) => {
|
||||
|
||||
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.includes('\n')) return res.status(400).send("Profile posts cannot contain line breaks!")
|
||||
|
||||
let params = {
|
||||
gameVersion: '21',
|
||||
binaryVersion: '35',
|
||||
secret: app.secret,
|
||||
cType: '1'
|
||||
}
|
||||
|
||||
params.comment = new Buffer(req.body.comment.slice(0, 190) + (req.body.color ? "⍟" : "")).toString('base64').replace(/\//g, '_').replace(/\+/g, "-")
|
||||
params.gjp = xor.encrypt(req.body.password, 37526)
|
||||
params.accountID = req.body.accountID.toString()
|
||||
params.userName = req.body.username
|
||||
|
||||
let chk = params.userName + params.comment + "1xPT6iUrtws0J"
|
||||
chk = sha1(chk)
|
||||
chk = xor.encrypt(chk, 29481)
|
||||
params.chk = chk
|
||||
|
||||
request.post('http://boomlings.com/database/uploadGJAccComment20.php', {
|
||||
form: params
|
||||
}, function (err, resp, body) {
|
||||
if (err) return res.status(400).send("The Geometry Dash servers returned an error! Perhaps they're down for maintenance")
|
||||
if (!body || body == "-1") 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.")
|
||||
res.status(200).send(`Comment posted to ${params.userName} with ID ${body}`)
|
||||
})
|
||||
}
|
|
@ -4,6 +4,7 @@ const difficulty = {0: 'Unrated', 10: 'Easy', 20: 'Normal', 30: 'Hard', 40: 'Har
|
|||
const length = ['Tiny', 'Short', 'Medium', 'Long', 'XL']
|
||||
const mapPacks = require('../misc/mapPacks.json')
|
||||
const levels = require('../misc/level.json').music
|
||||
const Level = require('../classes/Level.js')
|
||||
|
||||
module.exports = async (app, req, res) => {
|
||||
|
||||
|
@ -85,71 +86,47 @@ module.exports = async (app, req, res) => {
|
|||
authorList[arr[0]] = [arr[1], arr[2]]})
|
||||
|
||||
let levelArray = preRes.map(x => app.parseResponse(x))
|
||||
let parsedLevels = []
|
||||
|
||||
await levelArray.forEach(async (x, y) => {
|
||||
let keys = Object.keys(x)
|
||||
|
||||
if (filters.page == 0) { //this is broken if you're not on page 0
|
||||
let pages = splitBody[3].split(":");
|
||||
x.results = +pages[0];
|
||||
x.pages = +Math.ceil(pages[0] / 10);
|
||||
}
|
||||
|
||||
x.name = x[2];
|
||||
x.id = x[1];
|
||||
x.description = Buffer.from(x[3], 'base64').toString() || "(No description provided)",
|
||||
x.author = authorList[x[6]] ? authorList[x[6]][0] : "-";
|
||||
x.authorID = x[6];
|
||||
x.accountID = authorList[x[6]] ? authorList[x[6]][1] : "0";
|
||||
x.difficulty = difficulty[x[9]];
|
||||
x.downloads = x[10];
|
||||
x.likes = x[14];
|
||||
x.disliked = x[14] < 0;
|
||||
x.length = length[x[15]] || "?";
|
||||
x.stars = x[18];
|
||||
x.orbs = orbs[x[18]];
|
||||
x.diamonds = x[18] < 2 ? 0 : parseInt(x[18]) + 2;
|
||||
x.featured = x[19] > 0;
|
||||
x.epic = x[42] == 1;
|
||||
x.version = x[5];
|
||||
x.copiedID = x[30];
|
||||
x.officialSong = x[12] != 0 ? parseInt(x[12]) + 1 : 0;
|
||||
x.customSong = x[35];
|
||||
x.coins = x[37];
|
||||
x.verifiedCoins = x[38] == 1;
|
||||
x.starsRequested = x[39];
|
||||
x.objects = x[45];
|
||||
x.large = x[45] > 40000;
|
||||
x.cp = (x.stars > 0) + x.featured + x.epic;
|
||||
|
||||
if (x[17] == 1) x.difficulty += ' Demon'
|
||||
if (x.difficulty == "Insane Demon") x.difficulty = "Extreme Demon"
|
||||
else if (x.difficulty == "Harder Demon") x.difficulty = "Insane Demon"
|
||||
else if (x.difficulty == "Normal Demon") x.difficulty = "Medium Demon"
|
||||
else if (x[25] == 1) x.difficulty = 'Auto'
|
||||
x.difficultyFace = `${x[17] != 1 ? x.difficulty.toLowerCase() : `demon-${x.difficulty.toLowerCase().split(' ')[0]}`}${x.epic ? '-epic' : `${x.featured ? '-featured' : ''}`}`
|
||||
|
||||
let level = new Level(x)
|
||||
let songSearch = songs.find(y => y['~1'] == x[35])
|
||||
|
||||
level.author = authorList[x[6]] ? authorList[x[6]][0] : "-";
|
||||
level.accountID = authorList[x[6]] ? authorList[x[6]][1] : "0";
|
||||
|
||||
if (songSearch) {
|
||||
x.songName = app.clean(songSearch[2] || "Unknown")
|
||||
x.songAuthor = songSearch[4] || "Unknown"
|
||||
x.songSize = (songSearch[5] || "0") + "MB"
|
||||
x.songID = songSearch[1] || x.customSong
|
||||
level.songName = app.clean(songSearch[2] || "Unknown")
|
||||
level.songAuthor = songSearch[4] || "Unknown"
|
||||
level.songSize = (songSearch[5] || "0") + "MB"
|
||||
level.songID = songSearch[1] || level.customSong
|
||||
}
|
||||
|
||||
else {
|
||||
let foundSong = require('../misc/level.json').music[parseInt(x[12]) + 1] || {"null": true}
|
||||
x.songName = foundSong[0] || "Unknown"
|
||||
x.songAuthor = foundSong[1] || "Unknown"
|
||||
x.songSize = "0MB"
|
||||
x.songID = "Level " + [parseInt(x[12]) + 1]
|
||||
}
|
||||
level.songName = foundSong[0] || "Unknown"
|
||||
level.songAuthor = foundSong[1] || "Unknown"
|
||||
level.songSize = "0MB"
|
||||
level.songID = "Level " + [parseInt(x[12]) + 1]
|
||||
}
|
||||
|
||||
keys.forEach(k => delete x[k])
|
||||
//this is broken if you're not on page 0, blame robtop
|
||||
if (filters.page == 0 && y == 0) {
|
||||
let pages = splitBody[3].split(":");
|
||||
level.results = +pages[0];
|
||||
level.pages = +Math.ceil(pages[0] / 10);
|
||||
|
||||
if (filters.gauntlet || foundPack) {
|
||||
level.results = levelArray.length
|
||||
level.pages = 1
|
||||
}
|
||||
}
|
||||
|
||||
parsedLevels[y] = level
|
||||
})
|
||||
|
||||
return res.send(levelArray.slice(0, amount))
|
||||
return res.send(parsedLevels.slice(0, amount))
|
||||
|
||||
})
|
||||
}
|
|
@ -14,7 +14,7 @@ main {
|
|||
background: #556c7d;
|
||||
}
|
||||
|
||||
main.alt {
|
||||
main:nth-child(even) {
|
||||
background: #3979b8;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,82 +0,0 @@
|
|||
@font-face {font-family: Pusab; src: url('./../assets/Pusab.ttf')}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
height: 100vh;
|
||||
background: #000;
|
||||
}
|
||||
|
||||
.supercenter {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%,-50%);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.levelBG {
|
||||
background-image: linear-gradient(#0065FD, #002E73);
|
||||
height: 100vh;
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
background-attachment: fixed;
|
||||
}
|
||||
|
||||
p {
|
||||
font-family: aller, helvetica;
|
||||
color: white;
|
||||
font-size: 2.9vh;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-weight: normal;
|
||||
margin: 0% 0%;
|
||||
font-size: 90px;
|
||||
font-family: Pusab;
|
||||
color: white;
|
||||
letter-spacing: 0.02em;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-shadow: -2.5px -2.5px 0px #000, 2.5px -2.5px 0px #000, -2.5px 2.5px 0px #000, 2.5px 2.5px 0px #000, 0.5px 0.6px 0px rgba(0,0,0,0.4);
|
||||
-webkit-text-size-adjust: 100%;
|
||||
line-height: 100%;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-weight: normal;
|
||||
margin: 0% 0%;
|
||||
font-size: 40px;
|
||||
font-family: Pusab;
|
||||
color: white;
|
||||
letter-spacing: 0.02em;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-shadow: -1.5px -1.5px 0px #000, 1.5px -1.5px 0px #000, -1.5px 1.5px 0px #000, 1.5px 1.5px 0px #000, 0.5px 0.6px 0px rgba(0,0,0,0.4);
|
||||
-webkit-text-size-adjust: 100%;
|
||||
line-height: 100%;
|
||||
}
|
||||
|
||||
.gdButton {
|
||||
cursor: pointer;
|
||||
z-index: 1;
|
||||
user-select: none;
|
||||
transition-duration: 0.07s;
|
||||
transition-timing-function: ease-in-out;
|
||||
}
|
||||
|
||||
.gdButton:hover {
|
||||
transform: scale(1.03);
|
||||
}
|
||||
|
||||
.gdButton:active {
|
||||
transform: scale(1.08);
|
||||
}
|
||||
|
||||
.inline {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
56
classes/Level.js
Normal file
56
classes/Level.js
Normal file
|
@ -0,0 +1,56 @@
|
|||
const XOR = require(__dirname + "/../classes/XOR");
|
||||
|
||||
let orbs = [0, 0, 50, 75, 125, 175, 225, 275, 350, 425, 500]
|
||||
let length = ['Tiny', 'Short', 'Medium', 'Long', 'XL']
|
||||
let difficulty = { 0: 'Unrated', 10: 'Easy', 20: 'Normal', 30: 'Hard', 40: 'Harder', 50: 'Insane' }
|
||||
|
||||
class Level {
|
||||
constructor(levelInfo, author = []) {
|
||||
this.name = levelInfo[2];
|
||||
this.id = levelInfo[1];
|
||||
this.description = Buffer.from(levelInfo[3], "base64").toString() || "(No description provided)";
|
||||
this.author = author[1] || "-"
|
||||
this.authorID = levelInfo[6]
|
||||
this.accountID = author[2] || 0
|
||||
this.difficulty = difficulty[levelInfo[9]]
|
||||
this.downloads = levelInfo[10]
|
||||
this.likes = levelInfo[14]
|
||||
this.disliked = levelInfo[14] < 0
|
||||
this.length = length[levelInfo[15]] || "?"
|
||||
this.stars = levelInfo[18]
|
||||
this.orbs = orbs[levelInfo[18]]
|
||||
this.diamonds = levelInfo[18] < 2 ? 0 : parseInt(levelInfo[18]) + 2
|
||||
this.featured = levelInfo[19] > 0
|
||||
this.epic = levelInfo[42] == 1
|
||||
if (levelInfo[28]) this.uploaded = levelInfo[28] + ' ago'
|
||||
if (levelInfo[29]) this.updated = levelInfo[29] + ' ago'
|
||||
this.version = levelInfo[5];
|
||||
if (levelInfo[27]) this.password = levelInfo[27];
|
||||
this.copiedID = levelInfo[30]
|
||||
this.officialSong = levelInfo[12] != 0 ? parseInt(levelInfo[12]) + 1 : 0
|
||||
this.customSong = levelInfo[35]
|
||||
this.coins = levelInfo[37]
|
||||
this.verifiedCoins = levelInfo[38] == 1
|
||||
this.starsRequested = levelInfo[39]
|
||||
this.ldm = levelInfo[40] == 1
|
||||
this.objects = levelInfo[45] == "65535" ? "65535+" : levelInfo[45]
|
||||
this.large = levelInfo[45] > 40000;
|
||||
this.cp = (this.stars > 0) + this.featured + this.epic
|
||||
|
||||
if (levelInfo[17] == 1) this.difficulty += ' Demon'
|
||||
if (this.difficulty == "Insane Demon") this.difficulty = "Extreme Demon"
|
||||
else if (this.difficulty == "Harder Demon") this.difficulty = "Insane Demon"
|
||||
else if (this.difficulty == "Normal Demon") this.difficulty = "Medium Demon"
|
||||
else if (levelInfo[25] == 1) this.difficulty = 'Auto';
|
||||
this.difficultyFace = `${levelInfo[17] != 1 ? this.difficulty.toLowerCase() : `demon-${this.difficulty.toLowerCase().split(' ')[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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Level;
|
|
@ -41,14 +41,15 @@
|
|||
<div class="header-category">
|
||||
<div class="category-name">Account</div>
|
||||
<div class="category-content">
|
||||
<a class="header-link" href="#commenting">Commenting</a>
|
||||
<a class="header-link" href="#commenting">Commenting</a>
|
||||
<a class="header-link" href="#profileposting">Profile Posting</a>
|
||||
<a class="header-link" href="#liking">Liking</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="header-category">
|
||||
<div class="category-name">Misc</div>
|
||||
<div class="category-content">
|
||||
<a class="header-link" href="#analyze">Level Analysis</a>
|
||||
<a class="header-link" href="#analyze">Level Analysis</a>
|
||||
<a class="header-link" href="#icons">Icons</a>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -56,7 +57,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div id="intro" class="anchor"></div>
|
||||
<main class="grey">
|
||||
<main>
|
||||
<div class="main-block">
|
||||
<h1>Hi there!</h1>
|
||||
<p>This is the documentation for the
|
||||
|
@ -79,6 +80,7 @@
|
|||
<p><a href="#comments"><u>Comments & Posts</u></a> <i>/api/comments/level-or-user-ID</i></p>
|
||||
<p><a href="#analyze"><u>Level Analysis</u></a> <i>/api/analyze/levelID</i></p>
|
||||
<p><a href="#commenting"><u>Commenting</u></a> <i>/api/postComment (POST)</i></p>
|
||||
<p><a href="#profileposting"><u>Profile Posting</u></a> <i>/api/postProfileComment (POST)</i></p>
|
||||
<p><a href="#liking"><u>Liking</u></a> <i>/api/like (POST)</i></p>
|
||||
<p><a href="#icons"><u>Icons</u></a> <i>/icon/username</i></p>
|
||||
</quote>
|
||||
|
@ -90,7 +92,7 @@
|
|||
|
||||
<div class="seperator"></div>
|
||||
</main>
|
||||
<main class="alt">
|
||||
<main>
|
||||
<div id="level" class="anchor"></div>
|
||||
<div class="main-block">
|
||||
<h1>Levels</h1>
|
||||
|
@ -168,7 +170,7 @@
|
|||
|
||||
|
||||
</main>
|
||||
<main class="grey">
|
||||
<main>
|
||||
|
||||
<div id="profile" class="anchor"></div>
|
||||
|
||||
|
@ -225,7 +227,7 @@
|
|||
|
||||
|
||||
</main>
|
||||
<main class="alt">
|
||||
<main>
|
||||
|
||||
<div id="search" class="anchor"></div>
|
||||
|
||||
|
@ -309,7 +311,7 @@
|
|||
|
||||
|
||||
</main>
|
||||
<main class="grey">
|
||||
<main>
|
||||
<div id="leaderboard" class="anchor"></div>
|
||||
<div class="main-block">
|
||||
<h1>Leaderboards</h1>
|
||||
|
@ -360,7 +362,7 @@
|
|||
<div class="seperator"></div>
|
||||
|
||||
</main>
|
||||
<main class="alt">
|
||||
<main>
|
||||
<div id="levelleaderboard" class="anchor"></div>
|
||||
<div class="main-block">
|
||||
<h1>Level Leaderboards</h1>
|
||||
|
@ -405,7 +407,7 @@
|
|||
<div class="seperator"></div>
|
||||
|
||||
</main>
|
||||
<main class="grey">
|
||||
<main>
|
||||
<div id="comments" class="anchor"></div>
|
||||
<div class="main-block">
|
||||
<h1>Comments and Profile Posts</h1>
|
||||
|
@ -432,12 +434,12 @@
|
|||
<p>likes: The number of likes the comment has</p>
|
||||
<p>date: Time since the comment was posted (sent as "x days/weeks/months" ago, since it's all the API sends)</p>
|
||||
<p>levelID: The ID of the level</p>
|
||||
<p>browserColor: If the comment was posted through GDBrowser</p>
|
||||
<p class="red">username: The commenter's username</p>
|
||||
<p class="red">playerID: The commenter's ID</p>
|
||||
<p class="red">accountID: The commenter's account ID</p>
|
||||
<p class="red">form: The form of the commenter's icon</p>
|
||||
<p class="red">percent: The commenter's percent on the level, if provided</p>
|
||||
<p class="red">browserColor: If the comment was posted through GDBrowser</p>
|
||||
<p class="red">modColor: If the commenter is an elder mod and gets fancy green text</p>
|
||||
</div>
|
||||
|
||||
|
@ -459,7 +461,7 @@
|
|||
<div class="seperator"></div>
|
||||
</main>
|
||||
|
||||
<main class="alt">
|
||||
<main>
|
||||
<div id="analyze" class="anchor"></div>
|
||||
<div class="main-block">
|
||||
<h1>Level Analysis</h1>
|
||||
|
@ -500,7 +502,7 @@
|
|||
|
||||
</main>
|
||||
|
||||
<main class="grey">
|
||||
<main>
|
||||
<div id="commenting" class="anchor"></div>
|
||||
<div class="main-block">
|
||||
<h1>Commenting</h1>
|
||||
|
@ -518,7 +520,7 @@
|
|||
<p>password: Your password (as plain text)</p>
|
||||
<p>levelID: The ID of the level to comment on</p>
|
||||
<p>percent: The percent shown on the comment (optional)</p>
|
||||
<p>color: If the comment should have a special color on GDBrowser (optional)</p>
|
||||
<p>color: If the comment should have a special pink color on GDBrowser (optional)</p>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
@ -541,8 +543,44 @@
|
|||
<div class="seperator"></div>
|
||||
</main>
|
||||
|
||||
<main>
|
||||
<div id="profileposting" class="anchor"></div>
|
||||
<div class="main-block">
|
||||
<h1>Profile Posting</h1>
|
||||
<p>POST: /api/postProfileComment</p>
|
||||
|
||||
<p>Leaves a profile post. This one is a POST request!</p>
|
||||
|
||||
<br>
|
||||
<p class="reveal" onclick="$('#params-profileposting').slideToggle(100)"><b>Parameters (5)</b></p>
|
||||
<div class="subdiv" id="params-profileposting">
|
||||
<p>comment: The content of the profile post</p>
|
||||
<p>username: Your username</p>
|
||||
<p>accountID: Your account ID</p>
|
||||
<p>password: Your password (as plain text)</p>
|
||||
<p>color: If the comment should have a special pink color on GDBrowser (optional)</p>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
<main class="alt">
|
||||
<p class="reveal" onclick="$('#request-profileposting').slideToggle(100)"><b>Example</b></p>
|
||||
<div class="subdiv" id="request-profileposting">
|
||||
<p><b>Example Requests</b></p>
|
||||
<p>POST /api/postProfileComment<br>
|
||||
?comment=Update 2.0 is revolution!<br>
|
||||
&username=viprin<br>
|
||||
&accountID=2795<br>
|
||||
&password=CopyAndPasteTurnsMeOn<br>
|
||||
<br>
|
||||
<p>If a status of 200 is returned, then the profile post was successfully posted. Otherwise, a 400 will return with an error message.</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="seperator"></div>
|
||||
</main>
|
||||
|
||||
|
||||
<main>
|
||||
<div id="liking" class="anchor"></div>
|
||||
<div class="main-block">
|
||||
<h1>Liking</h1>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<head>
|
||||
<title>Coming Soon...</title>
|
||||
<meta charset="utf-8">
|
||||
<link href="../css/soon.css" type="text/css" rel="stylesheet">
|
||||
<link href="../css/browser.css" type="text/css" rel="stylesheet">
|
||||
<link rel="icon" href="../assets/coin.png">
|
||||
</head>
|
||||
|
||||
|
|
|
@ -330,7 +330,7 @@ $('#submitComment').click(function() {
|
|||
allowEsc = false
|
||||
|
||||
fetch(`../api/profile/${username}`).then(res => res.json()).then(res => {
|
||||
if (!res || res == "-1") {$('.postbutton').show(); return $('#message').text("The username you provided doesn't exist!")}
|
||||
if (!res || res == "-1") {allowEsc = true; $('.postbutton').show(); return $('#message').text("The username you provided doesn't exist!")}
|
||||
else accountID = res.accountID
|
||||
|
||||
$.post("../postComment", {comment, username, password, levelID, accountID, color: true})
|
||||
|
@ -346,7 +346,7 @@ $('#submitComment').click(function() {
|
|||
page = 0
|
||||
appendComments()
|
||||
})
|
||||
.fail(e => {$('.postbutton').show();$('#message').text(e.responseText.includes("DOCTYPE") ? "Something went wrong..." : e.responseText)})
|
||||
.fail(e => {allowEsc = true; $('.postbutton').show();$('#message').text(e.responseText.includes("DOCTYPE") ? "Something went wrong..." : e.responseText)})
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -397,7 +397,7 @@ $('#submitVote').click(function() {
|
|||
allowEsc = false
|
||||
|
||||
fetch(`../api/profile/${username}`).then(res => res.json()).then(res => {
|
||||
if (!res || res == "-1") {$('.postbutton').show(); return $('#likeMessage').text("The username you provided doesn't exist!")}
|
||||
if (!res || res == "-1") {allowEsc = true; $('.postbutton').show(); return $('#likeMessage').text("The username you provided doesn't exist!")}
|
||||
else accountID = res.accountID
|
||||
|
||||
$.post("../like", { ID, accountID, password, like: likeType, type: 2, extraID })
|
||||
|
@ -414,7 +414,7 @@ $('#submitVote').click(function() {
|
|||
likedComments.push(commentID)
|
||||
localStorage.setItem('likedComments', JSON.stringify(likedComments))
|
||||
})
|
||||
.fail(e => {$('.postbutton').show();$('#likeMessage').text(e.responseText.includes("DOCTYPE") ? "Something went wrong..." : e.responseText)})
|
||||
.fail(e => {allowEsc = true; $('.postbutton').show();$('#likeMessage').text(e.responseText.includes("DOCTYPE") ? "Something went wrong..." : e.responseText)})
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
@ -108,8 +108,8 @@ fetch(`./api/credits`).then(res => res.json()).then(res => {
|
|||
|
||||
res.specialThanks.forEach((x, y) => {
|
||||
$('#specialthanks').append(`<div class="specialThanks">
|
||||
<h2 class="gdButton smaller"><a href="./profile/${x[1]}">${x[0]}</h2></a>
|
||||
<img src="./icon/${x[1]}" height=85%><br>
|
||||
<h2 class="gdButton smaller"><a href="./profile/${x}">${x}</h2></a>
|
||||
<img src="./icon/${x}" height=85%><br>
|
||||
</div>`)
|
||||
})
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<head>
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-135255146-3"></script><script>window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);}gtag('js', new Date());gtag('config', 'UA-135255146-3');</script>
|
||||
<title>Online Icon Kit</title>
|
||||
<link href="../css/generate.css" type="text/css" rel="stylesheet">
|
||||
<link href="../css/iconkit.css" type="text/css" rel="stylesheet">
|
||||
<meta name="viewport" content="width=1024">
|
||||
<meta property="og:description" content="Build and save your very own Geometry Dash icons, right from the internet!">
|
||||
<meta property="og:title" content="Geometry Dash Online Icon Kit">
|
||||
|
|
|
@ -12,6 +12,42 @@
|
|||
<body class="levelBG" onbeforeunload="saveUrl()">
|
||||
|
||||
<div id="everything">
|
||||
|
||||
<div class="popup" id="leavePost">
|
||||
<div class="brownbox bounce center supercenter" style="height: 68%; width: 110vh">
|
||||
<p style="position:absolute; right: 1vh; margin-top: 0; text-align: left" id="charcount">180</p>
|
||||
<h1 class="smaller center" style="font-size: 5.5vh">Post Update</h1>
|
||||
<textarea placeholder="Insert comment" id="content" maxlength="180" style="margin: 2% 0%"></textarea><br>
|
||||
<form action="nothing lol">
|
||||
<h3 class="center" style="margin-top: 2%">GD Password</h3>
|
||||
<input type="password" id="password" maxlength="50" style="height: 8vh; width: 90%; text-align: center; margin-top: 0.5%">
|
||||
</form>
|
||||
<div style="min-height: 20%; max-height: 20%">
|
||||
<p id="message" style="padding: 0% 10%; margin-top: 2%"></p>
|
||||
</div>
|
||||
<img src="../assets/btn-cancel.png" height=11%; class="postButton gdButton center" style="margin-right: 1%" onclick="$('#leavePost').hide(); $('textarea').val('')">
|
||||
<img src="../assets/btn-submit.png" type="submit" height=11%; class="postButton gdButton center" style="margin-left: 1%" id="submitComment">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="popup" id="likeComment">
|
||||
<div class="brownbox bounce center supercenter" style="height: 75%; width: 100vh">
|
||||
<h1 class="smaller center" style="font-size: 5.5vh">Vote</h1>
|
||||
<img src="../assets/smashLike.png" id="likebtn" class="inline gdButton likeButton"><!--
|
||||
--><img src="../assets/smashDislike.png" id="dislikebtn" class="inline gdButton likeButton youAreNotTheOne">
|
||||
<form action="nothing lol">
|
||||
<h3 class="center">GD Username</h3>
|
||||
<input type="text" name="gdbrowser" id="like-username" maxlength="50" style="height: 8vh; width: 90%; text-align: center; margin-top: 0.5%">
|
||||
<h3 class="center" style="margin-top: 2%">GD Password</h3>
|
||||
<input type="password" id="like-password" maxlength="50" style="height: 8vh; width: 90%; text-align: center; margin-top: 0.5%">
|
||||
</form>
|
||||
<div style="min-height: 18%; max-height: 18%">
|
||||
<p id="likeMessage" style="padding: 0% 10%; margin-top: 2.5%"></p>
|
||||
</div>
|
||||
<img src="../assets/btn-cancel.png" height=10%; class="postButton gdButton center" style="margin-right: 1%" onclick="$('#likeComment').hide(); $('#likebtn').trigger('click');">
|
||||
<img src="../assets/btn-submit.png" type="submit" height=10%; class="postButton gdButton center" style="margin-left: 1%" id="submitVote">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="popup" id="settingsDiv">
|
||||
<div class="fancybox bounce center supercenter">
|
||||
|
@ -68,13 +104,14 @@
|
|||
<div class="lightBox center dragscroll" id="statusDiv" style="margin: 2% auto; width: 105vh; height: 35vh; background-color: #BE6F3F">
|
||||
</div>
|
||||
|
||||
<div class="center" style="margin: 2.5% auto;">
|
||||
<div class="center" style="margin: 1.5% auto 2.5% auto;">
|
||||
<img src="../assets/messages.png" height="10%" id="msgButton" class="sideSpace gdButton" onclick="$('#settingsDiv').show()">
|
||||
<img src="../assets/friends.png" height="10%" id="friendButton" class="sideSpace gdButton" onclick="$('#settingsDiv').show()">
|
||||
<a href="../search/[[USERNAME]]?user"><img height="10%" src="../assets/levels.png" class="sideSpace gdButton"></a>
|
||||
</div>
|
||||
|
||||
<!-- <img src="../assets/follow-off.png" class="gdButton" style="position: absolute; left: 0.5%; bottom: 1%; width: 6%"> -->
|
||||
<a href="../search/[[USERNAME]]?user"><img src="../assets/levels.png" class="gdButton" style="position: absolute; right: 0.5%; bottom: 1%; width: 6%"></a>
|
||||
|
||||
<a id="commentA"><img src="../assets/comments.png" class="gdButton" id="commentButton" style="position: absolute; right: 0.5%; bottom: 50%; width: 6%" onclick="$('#settingsDiv').show()"></a>
|
||||
|
||||
<div style="position: absolute; right: 0.5%; top: 0%; width: 6%">
|
||||
|
@ -95,6 +132,10 @@
|
|||
<img class="gdButton" src="../assets/arrow-right.png" height="95%" onclick="page += 1; appendComments()">
|
||||
</div>
|
||||
|
||||
<div id="postButton" style="position:absolute;bottom: 0%;right: 0%;width: 14%;text-align: right;transform: translate(30%, 40%);">
|
||||
<img class="gdButton" src="../assets/comment.png" width="60%" onclick="$('#content').trigger('input'); $('#leavePost').show();">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
@ -106,6 +147,10 @@
|
|||
|
||||
<script>
|
||||
|
||||
let messageText = 'Your <span style="color: yellow">Geometry Dash password</span> will <span style="color: lime">not be stored</span> anywhere on the site, both <span style="color:rgb(113, 234, 255)">locally and server-side.</span> You can view the code used for profile posts <a class="menuLink" target="_blank" href="https://github.com/GDColon/GDBrowser/blob/master/api/postProfileComment.js">here</a>.'
|
||||
$('#message').html(messageText)
|
||||
$('#likeMessage').html(messageText.replace("profile posts", "liking posts").replace("postProfileComment", "like"))
|
||||
|
||||
$('#collectibles').html($('#collectibles').html().split(' 0 <img class="valign" src="../assets/cp')[0])
|
||||
$('#modBadge[[MODERATOR]]').show()
|
||||
$('#globalrank[[RANK]]').hide()
|
||||
|
@ -161,12 +206,12 @@ fetch(`../api/comments/[[ACCOUNTID]]?type=profile&page=${page}`).then(res => res
|
|||
<div class="comment">
|
||||
<h2>[[USERNAME]]</h2>
|
||||
<div class="commentAlign">
|
||||
<p class="commentText" style="color: ${"[[USERNAME]]" == "RobTop" ? "rgb(50, 255, 255)" : "[[MODERATOR]]" == "2" ? "rgb(75, 255, 75)" : "white"}">${x.content}</p>
|
||||
<p class="commentText" style="color: rgb(${"[[USERNAME]]" == "RobTop" ? "50, 255, 255" : "[[MODERATOR]]" == "2" ? "75, 255, 75" : x.browserColor ? "255, 180, 255" : "255, 255, 255"})">${x.content}</p>
|
||||
</div>
|
||||
</div>
|
||||
<p class="commentDate">${x.date}</p>
|
||||
<div class="commentLikes">
|
||||
<img id="likeImg" class="inline" ${x.likes < 0 ? "style='transform: translateY(25%)'" : ""} src="../assets/${x.likes < 0 ? "dis" : ""}like.png" height=20% style="margin-right: 0.4%">
|
||||
<img id="likeImg" class="likeComment gdButton inline" commentID="${x.ID}" ${x.likes < 0 ? "style='transform: translateY(25%)'" : ""} src="../assets/${x.likes < 0 ? "dis" : ""}like.png" height=20% style="margin-right: 0.4%">
|
||||
<h3 class="inline">${x.likes}</h3><br>
|
||||
</div>
|
||||
</div>`)
|
||||
|
@ -187,5 +232,100 @@ let page = 0
|
|||
let loadingComments = false
|
||||
appendComments()
|
||||
|
||||
$('#content').on('input', function() {
|
||||
let remaining = 180-$('#content').val().length
|
||||
$('#charcount').text(remaining)
|
||||
})
|
||||
|
||||
$('#submitComment').click(function () {
|
||||
let comment = $('#content').val()
|
||||
let username = '[[USERNAME]]'
|
||||
let password = $('#password').val()
|
||||
let accountID = '[[ACCOUNTID]]'
|
||||
if (!comment || !password || loadingComments) return $('#leavePost').hide()
|
||||
$('#message').text("Posting...")
|
||||
$('.postbutton').hide()
|
||||
allowEsc = false
|
||||
$.post("../postProfileComment", { comment, username, password, accountID, color: true })
|
||||
.done(x => {
|
||||
$('#content').val("")
|
||||
$('#leavePost').hide()
|
||||
$('.postbutton').show()
|
||||
$('#message').html(messageText)
|
||||
allowEsc = true
|
||||
page = 0
|
||||
appendComments()
|
||||
})
|
||||
.fail(e => { allowEsc = true; $('.postbutton').show(); $('#message').text(e.responseText.includes("DOCTYPE") ? "Something went wrong..." : e.responseText) })
|
||||
})
|
||||
|
||||
let commentID = 0;
|
||||
let likeCount, likeImg;
|
||||
let likedComments;
|
||||
let like = true;
|
||||
|
||||
$('#likebtn').click(function() {
|
||||
$('#likebtn').removeClass('youAreNotTheOne')
|
||||
$('#dislikebtn').addClass('youAreNotTheOne')
|
||||
like = true
|
||||
})
|
||||
|
||||
$('#dislikebtn').click(function() {
|
||||
$('#likebtn').addClass('youAreNotTheOne')
|
||||
$('#dislikebtn').removeClass('youAreNotTheOne')
|
||||
like = false
|
||||
})
|
||||
|
||||
$(document).on('click', '.likeComment', function(cmnt) {
|
||||
commentID = $(this).attr('commentID')
|
||||
|
||||
likedComments = localStorage.likedComments ? JSON.parse(localStorage.likedComments) : []
|
||||
if (likedComments.includes(commentID)) return;
|
||||
|
||||
lvID = $(this).attr('levelID') || 0
|
||||
likeImg = $(this).find('img')
|
||||
likeCount = $(this).parent().find('h3:not(.gold)')
|
||||
$('#likeComment').show()
|
||||
})
|
||||
|
||||
$('#submitVote').click(function() {
|
||||
|
||||
if (likedComments.includes(commentID)) return $('#likeMessage').text("You've already liked/disliked this comment!");
|
||||
|
||||
let ID = commentID
|
||||
let username = $('#like-username').val()
|
||||
let password = $('#like-password').val()
|
||||
let extraID = lvID || window.location.pathname.split('/')[2]
|
||||
let accountID = 0
|
||||
let likeType = like ? "1" : "0"
|
||||
|
||||
if (!ID || !username || !password || loadingComments) return $('#postComment').hide()
|
||||
|
||||
$('#likeMessage').text(like ? "Liking..." : "Disliking... :(")
|
||||
$('.postbutton').hide()
|
||||
allowEsc = false
|
||||
|
||||
fetch(`../api/profile/${username}`).then(res => res.json()).then(res => {
|
||||
if (!res || res == "-1") {allowEsc = true; $('.postbutton').show(); return $('#likeMessage').text("The username you provided doesn't exist!")}
|
||||
else accountID = res.accountID
|
||||
|
||||
$.post("../like", { ID, accountID, password, like: likeType, type: 3, extraID: "[[ACCOUNTID]]" })
|
||||
.done(x => {
|
||||
let newCount = parseInt(likeCount.text()) + (like ? 1 : -1)
|
||||
likeCount.text(newCount)
|
||||
if (newCount < 0) likeImg.attr('src', '../assets/dislike.png').css('transform', compact ? 'translateY(15%)' : 'translateY(25%)')
|
||||
else likeImg.attr('src', '../assets/like.png').removeAttr('style')
|
||||
$('#likeComment').hide()
|
||||
$('#likebtn').trigger('click')
|
||||
$('.postbutton').show()
|
||||
$('#likeMessage').html(messageText.replace("profile posts", "liking posts").replace("postProfileComment", "like"))
|
||||
allowEsc = true
|
||||
likedComments.push(commentID)
|
||||
localStorage.setItem('likedComments', JSON.stringify(likedComments))
|
||||
})
|
||||
.fail(e => {allowEsc = true; $('.postbutton').show();$('#likeMessage').text(e.responseText.includes("DOCTYPE") ? "Something went wrong..." : e.responseText)})
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
</script>
|
|
@ -129,7 +129,7 @@ let results = 0
|
|||
let legalPages = true
|
||||
let superSearch = ['*', '*?type=mostliked', '*?type=mostdownloaded', '*?type=recent'].includes(window.location.href.split('/')[4].toLowerCase())
|
||||
|
||||
let searchFilters = `../api/search/${type == 'saved' ? JSON.parse(localStorage.getItem('saved') || '[]').reverse().toString() : accID || path}?page=${page}${window.location.search.replace("?", "&").replace("page", "nope")}`
|
||||
let searchFilters = `../api/search/${type == 'saved' ? JSON.parse(localStorage.getItem('saved') || '[]').reverse().toString() : accID || path}?page=[PAGE]${window.location.search.replace("?", "&").replace("page", "nope")}`
|
||||
|
||||
function Append(firstLoad) {
|
||||
|
||||
|
@ -142,7 +142,7 @@ function Append(firstLoad) {
|
|||
if (page == 0) $('#pageDown').hide()
|
||||
else $('#pageDown').show()
|
||||
|
||||
fetch(searchFilters).then(res => res.json()).then(res => {
|
||||
fetch(searchFilters.replace("[PAGE]", page)).then(res => res.json()).then(res => {
|
||||
|
||||
if (res == '-1' || (res.length < 9 && type != "recent")) $('#pageUp').hide()
|
||||
else $('#pageUp').show()
|
||||
|
@ -285,7 +285,7 @@ $('#shuffle').click(function() {
|
|||
if (randomIndex == 0) randomIndex = 10
|
||||
$('#searchBox').html('<div style="height: 4.5%"></div>')
|
||||
$('#loading').show()
|
||||
fetch(searchFilters.replace(/(\?page=)\d+/, `$1${randomPage-1}`)).then(res => res.json()).then(res => {
|
||||
fetch(searchFilters.replace('PAGE', randomPage-1)).then(res => res.json()).then(res => {
|
||||
window.location.href = "../" + res[randomIndex-1].id
|
||||
})
|
||||
}
|
||||
|
|
7
index.js
7
index.js
|
@ -56,6 +56,10 @@ app.post("/postComment", function(req, res) {
|
|||
app.modules.postComment(app, req, res)
|
||||
})
|
||||
|
||||
app.post("/postProfileComment", function(req, res) {
|
||||
app.modules.postProfileComment(app, req, res)
|
||||
})
|
||||
|
||||
app.post("/like", function(req, res) {
|
||||
app.modules.like(app, req, res)
|
||||
})
|
||||
|
@ -174,7 +178,8 @@ app.get("/", function(req, res) {
|
|||
})
|
||||
|
||||
app.get('*', function(req, res) {
|
||||
res.redirect('/search/404%20')
|
||||
if (req.path.startsWith('/assets')) res.send("Looks like this file doesn't exist ¯\\_(ツ)_/¯<br>You can check out all of the assets <a href='https://github.com/GDColon/GDBrowser/tree/master/assets'>here</a>")
|
||||
else res.redirect('/search/404%20')
|
||||
});
|
||||
|
||||
app.listen(2000);
|
||||
|
|
|
@ -68,11 +68,11 @@
|
|||
],
|
||||
|
||||
"specialThanks": [
|
||||
["Qufy", "Qufy"],
|
||||
["AlFas", "AlFas"],
|
||||
["ViPriN", "ViPriN"],
|
||||
["NeKitDS", "NeKitDS"],
|
||||
["cos8o", "cos8o"],
|
||||
["zmxmx", "zmxmx"]
|
||||
"Qufy",
|
||||
"AlFas",
|
||||
"ViPriN",
|
||||
"NeKitDS",
|
||||
"cos8o",
|
||||
"zmxmx"
|
||||
]
|
||||
}
|
Loading…
Reference in a new issue