From ea5aa7bdddf491ba549cd9dc2b97b2951bec3924 Mon Sep 17 00:00:00 2001 From: Lncvrt Date: Fri, 20 Feb 2026 10:33:02 -0700 Subject: [PATCH] Add plat level score and move classic endpoint --- README.md | 4 +- src/index.ts | 49 ++++++++++++++----- src/lib/util.ts | 10 ++++ .../{scores => leaderboards}/level/get.ts | 27 ++++++++-- 4 files changed, 72 insertions(+), 18 deletions(-) rename src/routes/{scores => leaderboards}/level/get.ts (72%) diff --git a/README.md b/README.md index 507996e..2a2b241 100644 --- a/README.md +++ b/README.md @@ -11,14 +11,14 @@ This is very early in development and will not be finished for a long time likel - [x] Get gauntlets (not names yet) - [x] Get map packs - [x] Get leaderboards -- [x] Get level scores +- [x] Get classic level scores +- [x] Get plat level scores - [ ] Get Levels List - [ ] Get save data - [ ] Get level lists (same as map packs?) - [ ] Get top artists (not sure what this is) - [ ] Get user (profile?) - [ ] Get user info -- [ ] Get plat level scores - [ ] Get messages (account?) - [ ] Get comments - [ ] Get comment history diff --git a/src/index.ts b/src/index.ts index 5f9b040..fd1c656 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,11 +5,10 @@ import dotenv from 'dotenv' import { handler as gauntletsGetHandler } from './routes/gauntlets/get' import { handler as leaderboardsGetHandler } from './routes/leaderboards/get' +import { handler as leaderboardsLevelGetHandler } from './routes/leaderboards/level/get' import { handler as mapPacksGetHandler } from './routes/map-packs/get' -import { handler as scoresLevelGetHandler } from './routes/scores/level/get' - dotenv.config() const app = new Elysia() @@ -20,16 +19,42 @@ app.get('/gauntlets', gauntletsGetHandler) app.get('/map-packs', mapPacksGetHandler) app.get('/leaderboards/top', () => leaderboardsGetHandler('top')) app.get('/leaderboards/creators', () => leaderboardsGetHandler('creators')) -app.get('/scores/level/top', handler => scoresLevelGetHandler(handler, '1'), { - query: t.Object({ - id: t.String() - }) -}) -app.get('/scores/level/week', handler => scoresLevelGetHandler(handler, '2'), { - query: t.Object({ - id: t.String() - }) -}) +app.get( + '/leaderboards/level/classic/top', + handler => leaderboardsLevelGetHandler(handler, '1', true), + { + query: t.Object({ + id: t.String() + }) + } +) +app.get( + '/leaderboards/level/platformer/top', + handler => leaderboardsLevelGetHandler(handler, '2', false), + { + query: t.Object({ + id: t.String() + }) + } +) +app.get( + '/leaderboards/level/classic/week', + handler => leaderboardsLevelGetHandler(handler, '2', true), + { + query: t.Object({ + id: t.String() + }) + } +) +app.get( + '/leaderboards/level/platformer/week', + handler => leaderboardsLevelGetHandler(handler, '1', false), + { + query: t.Object({ + id: t.String() + }) + } +) app.all('*', () => jsonResponse( { diff --git a/src/lib/util.ts b/src/lib/util.ts index 7138756..a8d0a6e 100644 --- a/src/lib/util.ts +++ b/src/lib/util.ts @@ -21,3 +21,13 @@ export const encodeGJP2 = async (password = '', salt = 'mI29fmAnxgTs') => { .map(b => b.toString(16).padStart(2, '0')) .join('') } +export const generateTimeLength = (ms: number): string => { + const minutes = Math.floor(ms / 60000) + const seconds = Math.floor((ms % 60000) / 1000) + const milliseconds = ms % 1000 + + const secStr = seconds.toString().padStart(2, '0') + const msStr = milliseconds.toString().padStart(3, '0') + + return `${minutes}:${secStr}.${msStr}` +} diff --git a/src/routes/scores/level/get.ts b/src/routes/leaderboards/level/get.ts similarity index 72% rename from src/routes/scores/level/get.ts rename to src/routes/leaderboards/level/get.ts index 361606d..47c0291 100644 --- a/src/routes/scores/level/get.ts +++ b/src/routes/leaderboards/level/get.ts @@ -1,8 +1,17 @@ import axios from 'axios' -import { encodeGJP2, jsonResponse, splitGd } from '../../../lib/util' +import { + encodeGJP2, + generateTimeLength, + jsonResponse, + splitGd +} from '../../../lib/util' import { Context } from 'elysia' -export const handler = async (handler: Context, type: string) => { +export const handler = async ( + handler: Context, + type: string, + classic: boolean +) => { const id = handler.query.id if (!process.env.ACCOUNT_ID || !process.env.ACCOUNT_PASSWORD) @@ -14,7 +23,9 @@ export const handler = async (handler: Context, type: string) => { }) const request = await axios.post( - 'https://www.boomlings.com/database/getGJLevelScores211.php', + classic + ? 'https://www.boomlings.com/database/getGJLevelScores211.php' + : 'https://www.boomlings.com/database/getGJLevelScoresPlat.php', new URLSearchParams({ gameVersion: '22', binaryVersion: '45', @@ -39,6 +50,13 @@ export const handler = async (handler: Context, type: string) => { 'Failed to authenticate (are you sure account ID and password are in .env?)', data: null }) + if (request.data.trim() == '') + return jsonResponse({ + success: false, + message: + 'Empty response (please check that you are using the right classic/platformer type)', + data: null + }) const responseText = request.data as string const responseSplit = responseText.split('|') @@ -49,9 +67,10 @@ export const handler = async (handler: Context, type: string) => { data: responseSplit.map(item => { const split = splitGd(item) return { - split, rank: +split['6'], username: split['1'], + time: +split['3'], + timeFormatted: generateTimeLength(+split['3']), coins: +split['13'], playerId: +split['2'], accountId: +split['16'],