From 02b7ff7a93dec08189ebaa6d81932f6e3749087e Mon Sep 17 00:00:00 2001 From: UCDFiddes <66165184+UCDFiddes@users.noreply.github.com> Date: Sun, 11 Feb 2024 19:02:26 +0000 Subject: [PATCH 01/13] partial statistic endpoints --- .gitignore | 2 + package.json | 4 +- src/format/gameFormat.ts | 158 ---------------- src/helpers/fetchEndpoint.ts | 49 +++++ src/helpers/isGame.ts | 5 + src/index.ts | 26 +-- src/methods/calculateLevel.ts | 49 ----- src/methods/fetchData.ts | 166 ----------------- src/methods/getAllTimeLeaderboard.ts | 65 ++++--- src/methods/getAllTimeStatistics.ts | 107 +++++++++++ src/methods/getAllTimeStats.ts | 135 -------------- src/methods/getGameMetadata.ts | 31 ---- src/methods/getGlobalStatistics.ts | 28 --- src/methods/getMaps.ts | 54 ------ src/methods/getMonthlyLeaderboard.ts | 136 ++++++-------- src/methods/getMonthlyStatistics.ts | 111 +++++++++++ src/methods/getMonthlyStats.ts | 185 ------------------- src/methods/getPlayerInfo.ts | 31 ---- src/processors/common.ts | 13 ++ src/processors/index.ts | 72 ++++++++ src/processors/leaderboard.ts | 14 ++ src/types/API.ts | 263 --------------------------- src/types/ENDPOINTS.ts | 55 ------ src/types/GAMES.ts | 105 ----------- src/types/GAME_INFO.ts | 117 ------------ src/types/METHODS.ts | 8 - src/types/output.ts | 72 ++++++++ src/types/types.ts | 22 +++ tests/leaderboard/alltime.test.ts | 12 -- tests/leaderboard/monthly.test.ts | 45 ----- tests/misc/global.test.ts | 15 -- tests/misc/maps.test.ts | 20 -- tests/misc/metadata.test.ts | 11 -- tests/misc/player.test.ts | 11 -- tests/setup.ts | 2 - tests/stats/alltime.test.ts | 45 ----- tests/stats/monthly.test.ts | 60 ------ 37 files changed, 569 insertions(+), 1735 deletions(-) delete mode 100644 src/format/gameFormat.ts create mode 100644 src/helpers/fetchEndpoint.ts create mode 100644 src/helpers/isGame.ts delete mode 100644 src/methods/calculateLevel.ts delete mode 100644 src/methods/fetchData.ts create mode 100644 src/methods/getAllTimeStatistics.ts delete mode 100644 src/methods/getAllTimeStats.ts delete mode 100644 src/methods/getGameMetadata.ts delete mode 100644 src/methods/getGlobalStatistics.ts delete mode 100644 src/methods/getMaps.ts create mode 100644 src/methods/getMonthlyStatistics.ts delete mode 100644 src/methods/getMonthlyStats.ts delete mode 100644 src/methods/getPlayerInfo.ts create mode 100644 src/processors/common.ts create mode 100644 src/processors/index.ts create mode 100644 src/processors/leaderboard.ts delete mode 100644 src/types/API.ts delete mode 100644 src/types/ENDPOINTS.ts delete mode 100644 src/types/GAMES.ts delete mode 100644 src/types/GAME_INFO.ts delete mode 100644 src/types/METHODS.ts create mode 100644 src/types/output.ts create mode 100644 src/types/types.ts delete mode 100644 tests/leaderboard/alltime.test.ts delete mode 100644 tests/leaderboard/monthly.test.ts delete mode 100644 tests/misc/global.test.ts delete mode 100644 tests/misc/maps.test.ts delete mode 100644 tests/misc/metadata.test.ts delete mode 100644 tests/misc/player.test.ts delete mode 100644 tests/setup.ts delete mode 100644 tests/stats/alltime.test.ts delete mode 100644 tests/stats/monthly.test.ts diff --git a/.gitignore b/.gitignore index 5a1ba46..9718392 100644 --- a/.gitignore +++ b/.gitignore @@ -136,3 +136,5 @@ yarn.lock # Custom /lib index.js +test.ts +test.js \ No newline at end of file diff --git a/package.json b/package.json index 5b78fbb..9b4eee1 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,9 @@ }, "license": "MIT", "author": "Cubeedge Studios", - "dependencies": {}, + "dependencies": { + "hive-bedrock-data": "^1.1.1" + }, "scripts": { "build": "tsc", "test": "jest" diff --git a/src/format/gameFormat.ts b/src/format/gameFormat.ts deleted file mode 100644 index 917bf64..0000000 --- a/src/format/gameFormat.ts +++ /dev/null @@ -1,158 +0,0 @@ -import calculateLevel from "../methods/calculateLevel"; -import { GAME } from "../types/GAME_INFO"; - -interface SingleGameFormat { - [key: string]: any; -} -interface GameFormat { - [key: string]: SingleGameFormat | null; -} - -export const formats = { - main: (player: SingleGameFormat) => { - return player; - }, - default: (gameType: GAME, game: SingleGameFormat | null) => { - if (!game) return null; - if ("index" in game && game.index == 2147483646) return null; - - if (!("index" in game)) { - if ("xp" in game!) { - game.level = calculateLevel(game.xp, gameType); - } else return null; - } - - if ("played" in game && "victories" in game) { - game.losses = (game.played ?? 0) - (game.victories ?? 0); - game.win_percentage = game.victories / game.played; - - if (isNaN(game.win_percentage)) game.win_percentage = 0; - } - - if ("kills" in game && "deaths" in game) - game.kdr = parseFloat( - ((game.kills ?? 0) / (game.deaths ?? 0)).toFixed(2) - ); - - return { id: gameType, ...game }; - }, - [GAME.BlockDrop]: (game: SingleGameFormat | null) => { - return game; - }, - [GAME.BlockParty]: (game: SingleGameFormat | null) => { - return game; - }, - [GAME.CaptureTheFlag]: (game: SingleGameFormat | null) => { - return game; - }, - [GAME.DeathRun]: (game: SingleGameFormat | null) => { - return game; - }, - [GAME.GroundWars]: (game: SingleGameFormat | null) => { - return game; - }, - [GAME.HideAndSeek]: (game: SingleGameFormat | null) => { - if (!game) return null; - if ("hider_kills" in game && "deaths" in game) - game.kdr = parseFloat((game.hider_kills / game.deaths).toFixed(2)); - - return game; - }, - [GAME.JustBuild]: (game: SingleGameFormat | null) => { - return game; - }, - [GAME.MurderMystery]: (game: SingleGameFormat | null) => { - if (!game) return null; - if ("murders" in game && "deaths" in game) - game.kdr = parseFloat((game.murders / game.deaths).toFixed(2)); - return game; - }, - [GAME.Skywars]: (game: SingleGameFormat | null) => { - return game; - }, - [GAME.SurvivalGames]: (game: SingleGameFormat | null) => { - return game; - }, - [GAME.TreasureWars]: (game: SingleGameFormat | null) => { - return game; - }, - [GAME.Gravity]: (game: SingleGameFormat | null) => { - return game; - }, - [GAME.TheBridge]: (game: SingleGameFormat | null) => { - if (!game) return null; - - if ("m_solo_deaths" in game) { - game.deaths = game.m_solo_deaths; - delete game.m_solo_deaths; - } - - if ("m_solo_goals" in game) { - game.goals = game.m_solo_goals; - delete game.m_solo_goals; - } - - if ("m_solo_kills" in game) { - game.kills = game.m_solo_kills; - delete game.m_solo_kills; - } - - if ("m_solo_played" in game) { - game.played = game.m_solo_played; - delete game.m_solo_played; - } - - if ("m_solo_victories" in game) { - game.victories = game.m_solo_victories; - delete game.m_solo_victories; - } - - if ("played" in game && "victories" in game) { - game.losses = game.played ?? 0 - game.victories ?? 0; - game.win_percentage = game.victories / game.played; - - if (isNaN(game.win_percentage)) game.win_percentage = 0; - } - - return game; - }, -}; - -export function gameFormatArray( - gameType: GAME, - games: SingleGameFormat[] -): (SingleGameFormat | null)[] { - return games.map((game) => - gameFormat( - { - [gameType]: game, - }, - true - ) - ); -} - -export default function gameFormat( - games: GameFormat, - returnAsSingle: true -): SingleGameFormat | null; -export default function gameFormat(games: GameFormat): GameFormat | null; - -export default function gameFormat( - games: GameFormat, - returnAsSingle?: boolean -): (GameFormat | SingleGameFormat) | null { - const formatted = Object.fromEntries( - Object.entries(games).map(([key, data]) => { - if (!data) return [key, null]; - let defaultFormat = formats.default(key as GAME, data!); - - if (!defaultFormat) return [key, null]; - let gameFormat = formats[key as GAME](defaultFormat); - - return [key, gameFormat]; - }) - ); - - return returnAsSingle ? Object.values(formatted)[0] : formatted; -} diff --git a/src/helpers/fetchEndpoint.ts b/src/helpers/fetchEndpoint.ts new file mode 100644 index 0000000..6413b2a --- /dev/null +++ b/src/helpers/fetchEndpoint.ts @@ -0,0 +1,49 @@ +import { API_SERVER } from ".."; +import { APIResponse } from "../types/types"; +import { Routes } from "hive-bedrock-data"; + +export default async function fetchEndpoint( + endpoint: T, + init?: RequestInit +): Promise>>; + +export default async function fetchEndpoint( + endpoint: T, + init?: RequestInit +): Promise>> { + try { + let time_start = performance.now(); + let request = await fetch(API_SERVER + endpoint, init); + let time_end = performance.now(); + let duration = Math.round(time_end - time_start); + + if (!request.ok) + return { + status: request.status, + data: null, + error: { + code: request.status, + message: request.statusText, + }, + duration, + }; + + let response = await request.json(); + + return { + status: request.status, + data: response, + error: null, + duration, + }; + } catch (err) { + return { + status: 500, + data: null, + error: { + code: 500, + message: "Failed to fetch endpoint data.", + }, + }; + } +} diff --git a/src/helpers/isGame.ts b/src/helpers/isGame.ts new file mode 100644 index 0000000..803e388 --- /dev/null +++ b/src/helpers/isGame.ts @@ -0,0 +1,5 @@ +import { Game } from "hive-bedrock-data"; + +export default function isGame(game_id: Game) { + return Object.values(Game).includes(game_id); +} diff --git a/src/index.ts b/src/index.ts index 3c71d99..d9e5ac1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,26 +1,12 @@ -import getMonthlyStats from "./methods/getMonthlyStats"; -import getMonthlyLeaderboard from "./methods/getMonthlyLeaderboard"; -import getAllTimeStats from "./methods/getAllTimeStats"; import getAllTimeLeaderboard from "./methods/getAllTimeLeaderboard"; -import getGlobalStatistics from "./methods/getGlobalStatistics"; -import getPlayerInfo from "./methods/getPlayerInfo"; -import getMaps from "./methods/getMaps"; -import getGameMetadata from "./methods/getGameMetadata"; -import { AVATAR, RANK, MAP_SEASON, MAP_VARIANT } from "./types/API"; +import getAllTimeStatistics from "./methods/getAllTimeStatistics"; +import getMonthlyLeaderboard from "./methods/getMonthlyLeaderboard"; +import getMonthlyStatistics from "./methods/getMonthlyStatistics"; +export const API_SERVER = "https://api.playhive.com/v0"; export { - getAllTimeStats, getAllTimeLeaderboard, - getMonthlyStats, getMonthlyLeaderboard, - getGlobalStatistics, - getPlayerInfo, - getMaps, - getGameMetadata, - AVATAR, - RANK, - MAP_SEASON, - MAP_VARIANT, + getAllTimeStatistics, + getMonthlyStatistics, }; -export * from "./types/GAME_INFO"; -export * from "./types/GAMES"; diff --git a/src/methods/calculateLevel.ts b/src/methods/calculateLevel.ts deleted file mode 100644 index e518c63..0000000 --- a/src/methods/calculateLevel.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { GAME, GAME_INFO } from "../types/GAME_INFO"; - -export default function calculateLevel(xp: number, game: GAME): number { - if (game === GAME.TheBridge) { - let lastXp = 0; - let currentXp = 300; - let increment = 300; - let additionalIncrement = 300; - - let i = 2; - while (true) { - if (xp === currentXp) return i; - else if (xp < currentXp) - return i + (xp - lastXp) / (currentXp - lastXp) - 1; - - additionalIncrement = Math.floor(additionalIncrement * 1.08); - increment += additionalIncrement; - lastXp = currentXp; - currentXp += increment; - i++; - } - } - - let gameInfo = GAME_INFO[game]; - - const INCREMENT = gameInfo.increment / 2; - const INCREMENT_CAP = gameInfo.incrementCap; - - let level = - (INCREMENT + Math.sqrt(INCREMENT * (INCREMENT + 4 * xp))) / - (2 * INCREMENT); - - if (!INCREMENT_CAP || level <= INCREMENT_CAP) - return boundLevel(level, gameInfo.maxLevel); - level = - INCREMENT_CAP + - (xp - - (INCREMENT * Math.pow(INCREMENT_CAP - 1, 2) + - (INCREMENT_CAP - 1) * INCREMENT)) / - ((INCREMENT_CAP - 1) * INCREMENT * 2); - - return boundLevel(level, gameInfo.maxLevel); -} - -function boundLevel(level: number, maxLevel: number) { - if (level < 0) return 0; - if (level > maxLevel) return maxLevel; - return parseFloat(level.toFixed(2)); -} diff --git a/src/methods/fetchData.ts b/src/methods/fetchData.ts deleted file mode 100644 index 18cafca..0000000 --- a/src/methods/fetchData.ts +++ /dev/null @@ -1,166 +0,0 @@ -import { - API_BASE_GAME_ALL, - API_BASE_GAME_MONTHLY, - API_GAME_STATS, - API_GLOBAL_STATISTICS, - API_MAP, - API_METADATA, - API_REQUEST_ALL, - API_REQUEST_LB, -} from "../types/API"; -import { - ENDPOINTS, - GAME_LB_ALLTIME_ENDPOINT, - GAME_LB_MONTHLY_ENDPOINT, - GAME_LB_SPECIFIC_MONTHLY_ENDPOINT, - GAME_MAPS_ENDPOINT, - GAME_METADATA_ENDPOINT, - GLOBAL_STATISTICS_ENDPOINT, - PLAYER_ALLTIME_ENDPOINT, - PLAYER_LB_SPECIFIC_MONTHLY_ENDPOINT, - PLAYER_MONTHLY_ENDPOINT, -} from "../types/ENDPOINTS"; -import { USER_MAIN } from "../types/GAMES"; -import { GAME } from "../types/GAME_INFO"; -import { MethodResponse } from "../types/METHODS"; - -export interface FetchOptions { - headers: { - [key: string]: string; - }; -} - -export const API_ROOT = "https://api.playhive.com/v0"; - -export default async function fetchData( - endpoint: PLAYER_ALLTIME_ENDPOINT<"all", string>, - options?: FetchOptions -): Promise>; - -export default async function fetchData( - endpoint: PLAYER_ALLTIME_ENDPOINT<"main", string>, - options?: FetchOptions -): Promise< - MethodResponse<{ - main: USER_MAIN; - }> ->; - -export default async function fetchData( - endpoint: PLAYER_ALLTIME_ENDPOINT, - options?: FetchOptions -): Promise[G]>>; - -export default async function fetchData( - endpoint: PLAYER_MONTHLY_ENDPOINT<"all", string>, - options?: FetchOptions -): Promise>>; - -export default async function fetchData( - endpoint: PLAYER_MONTHLY_ENDPOINT, - options?: FetchOptions -): Promise[G]>>; - -export default async function fetchData( - endpoint: GAME_LB_ALLTIME_ENDPOINT, - options?: FetchOptions -): Promise>>; - -export default async function fetchData( - endpoint: GAME_LB_MONTHLY_ENDPOINT, - options?: FetchOptions -): Promise>>; - -export default async function fetchData( - endpoint: PLAYER_LB_SPECIFIC_MONTHLY_ENDPOINT< - G, - string, - number, - number, - number, - number - >, - options?: FetchOptions -): Promise>>; - -export default async function fetchData( - endpoint: GAME_LB_SPECIFIC_MONTHLY_ENDPOINT< - G, - number, - number, - number, - number - >, - options?: FetchOptions -): Promise>>; - -export default async function fetchData( - endpoint: GLOBAL_STATISTICS_ENDPOINT, - options?: FetchOptions -): Promise>; - -export default async function fetchData( - endpoint: GAME_MAPS_ENDPOINT, - options?: FetchOptions -): Promise>; - -export default async function fetchData( - endpoint: GAME_METADATA_ENDPOINT, - options?: FetchOptions -): Promise>; - -export default async function fetchData( - endpoint: ENDPOINTS, - options?: FetchOptions -): Promise< - MethodResponse< - | API_GLOBAL_STATISTICS - | API_GAME_STATS - | API_GAME_STATS[G] - | API_GAME_STATS - | API_GAME_STATS[G] - | API_REQUEST_LB - | PLAYER_LB_SPECIFIC_MONTHLY_ENDPOINT< - G, - string, - number, - number, - number, - number - > - | GAME_LB_SPECIFIC_MONTHLY_ENDPOINT - | API_MAP[] - | API_METADATA - > -> { - try { - const request = await fetch(API_ROOT + endpoint, { - headers: options?.headers - ? new Headers(options?.headers) - : undefined, - }); - - if (!request.ok) - return { - data: null, - error: { - message: request.statusText, - status: request.status, - endpoint: API_ROOT + endpoint, - }, - }; - - const response = await request.json(); - - return { - data: response, - error: null, - }; - } catch (err) { - console.error(err); - return { - data: null, - error: { message: "Failed to fetch data." }, - }; - } -} diff --git a/src/methods/getAllTimeLeaderboard.ts b/src/methods/getAllTimeLeaderboard.ts index 4fd5de8..6550838 100644 --- a/src/methods/getAllTimeLeaderboard.ts +++ b/src/methods/getAllTimeLeaderboard.ts @@ -1,35 +1,44 @@ -import { gameFormatArray } from "../format/gameFormat"; -import { Options } from "../types/API"; -import { LB_STATS, REQUEST_LB } from "../types/GAMES"; -import { GAME } from "../types/GAME_INFO"; -import { MethodResponse } from "../types/METHODS"; -import fetchData from "./fetchData"; +import { Game, Timeframe } from "hive-bedrock-data"; +import { APIResponse, Options } from "../types/types"; +import fetchEndpoint from "../helpers/fetchEndpoint"; +import isGame from "../helpers/isGame"; +import { LeaderboardResponse } from "../types/output"; +import processors from "../processors"; -export default async function getAllTimeLeaderboard( - game: G, +export default function getAllTimeLeaderboard( + game_id: G, options?: Options -): Promise[]>>; +): Promise>>; -export default async function getAllTimeLeaderboard( - game: G, +export default async function getAllTimeLeaderboard( + game_id: G, options?: Options -): Promise>> { - try { - const { data, error } = await fetchData( - `/game/all/${game}`, - options?.fetch - ); - if (error || !data) - return { - data: null, - error: { message: "Failed to fetch data.", ...error }, - }; +): Promise>> { + if (!isGame(game_id)) + return { + status: 404, + error: { + code: 404, + message: "Game not found.", + }, + data: null, + }; - const gameData = gameFormatArray(game, data) as REQUEST_LB; + let response = await fetchEndpoint(`/game/all/${game_id}`, options?.init); + if (response.error) return response; - return { data: gameData, error: null }; - } catch (err) { - console.error(err); - return { data: null, error: { message: "Failed to fetch data." } }; - } + processors.leaderboard[Timeframe.AllTime][game_id].forEach((processor) => + processor(response.data) + ); + + let data = response.data as unknown as LeaderboardResponse< + G, + Timeframe.AllTime + >; + + return { + ...response, + data, + error: null, + }; } diff --git a/src/methods/getAllTimeStatistics.ts b/src/methods/getAllTimeStatistics.ts new file mode 100644 index 0000000..1d8c6c0 --- /dev/null +++ b/src/methods/getAllTimeStatistics.ts @@ -0,0 +1,107 @@ +import { Game, PlayerMetadata, Statistics, Timeframe } from "hive-bedrock-data"; +import { APIResponse, Options } from "../types/types"; +import { + AllGameStatistics, + AllStatistics, + StatisticsResponse, +} from "../types/output"; +import isGame from "../helpers/isGame"; +import fetchEndpoint from "../helpers/fetchEndpoint"; +import processors from "../processors"; + +type AllGameStatisticsPlayer = AllGameStatistics & { + player: PlayerMetadata; +}; + +export default async function getAllTimeStatistics( + identifier: string, + options?: Options +): Promise>; + +export default async function getAllTimeStatistics( + identifier: string, + game_id: G, + options?: Options +): Promise>>; + +export default async function getAllTimeStatistics( + identifier: string, + game_or_options?: G | Options, + options?: Options +) { + let game_id: G | "all" = "all"; + let method_options: Options | undefined = game_or_options as Options; + + if (!isGame(game_id as G) && game_id !== "all") + return { + status: 404, + error: { + code: 404, + message: "Game not found.", + }, + data: null, + }; + + if (isGame(game_or_options as G)) { + game_id = game_or_options as G; + method_options = options; + } + + let response = await fetchEndpoint( + `/game/all/${game_id}/${identifier}`, + method_options?.init + ); + if (response.error) return response; + + if (game_id === "all") { + let games = Object.entries(response.data); + let output: Partial = {}; + + for (let [g, stats] of games) { + if (isGame(g as Game)) { + if (Array.isArray(stats)) { + output[g as Game] = null; + continue; + } + + processors.statistics.all[g as Game].forEach((processor) => + processor( + stats as StatisticsResponse + ) + ); + output[g as Game] = stats; + } + } + + return { + ...response, + data: { + ...output, + player: games.find(([g]) => g === "main")?.at(1), + }, + }; + } + + let response_data = response.data as unknown as Statistics< + G, + Timeframe.AllTime + >; + + if (Array.isArray(response_data)) + return { + ...response, + status: 404, + data: null, + error: { code: 404, message: "Not Found" }, + }; + + processors.statistics.all[game_id].forEach((processor) => + processor(response_data as StatisticsResponse) + ); + + return { + ...response, + data: response_data, + error: null, + }; +} diff --git a/src/methods/getAllTimeStats.ts b/src/methods/getAllTimeStats.ts deleted file mode 100644 index 23392b1..0000000 --- a/src/methods/getAllTimeStats.ts +++ /dev/null @@ -1,135 +0,0 @@ -import gameFormat, { formats } from "../format/gameFormat"; -import { Options } from "../types/API"; -import { - BASE_GAME_ALL, - GAME_STATS, - GAME_STATS_ALL, - REQUEST_ALL, - USER_MAIN, -} from "../types/GAMES"; -import { GAME } from "../types/GAME_INFO"; -import { MethodResponse } from "../types/METHODS"; -import fetchData from "./fetchData"; - -export default async function getAllTimeStats( - playerIdentifier: string, - options?: Options -): Promise> & { player: USER_MAIN }>; - -export default async function getAllTimeStats( - playerIdentifier: string, - game: G, - options?: Options -): Promise[G]>>; - -export default async function getAllTimeStats( - playerIdentifier: string, - game: G[], - options?: Options -): Promise< - MethodResponse<{ [M in G]: GAME_STATS_ALL }> & { player: USER_MAIN } ->; - -export default async function getAllTimeStats( - playerIdentifier: string, - game?: G | G[] | Options, - options?: Options -): Promise< - | MethodResponse[G]> - | (MethodResponse< - GAME_STATS | { [M in G]: GAME_STATS_ALL } - > & { player: USER_MAIN }) -> { - if (typeof game === "object" && !(game instanceof Array)) { - options = game; - game = undefined; - } - - if (!game) { - try { - const { data, error } = await fetchData( - `/game/all/all/${playerIdentifier}`, - options?.fetch - ); - if (error || !data) - return { - data: null, - error: { message: "Failed to fetch data.", ...error }, - }; - - const gameData = gameFormat(data) as REQUEST_ALL; - - let filteredGamesEntries = Object.entries(gameData).filter( - ([key]) => Object.values(GAME).includes(key as GAME) - ); - - let filteredGames = Object.fromEntries( - filteredGamesEntries - ) as GAME_STATS; - - return { - data: filteredGames, - error: null, - player: formats.main(data.main) as USER_MAIN, - }; - } catch (err) { - console.error(err); - return { data: null, error: { message: "Failed to fetch data." } }; - } - } - - if (typeof game === "string") { - try { - const { data, error } = await fetchData( - `/game/all/${game}/${playerIdentifier}`, - options?.fetch - ); - if (error || !data) - return { - data: null, - error: { message: "Failed to fetch data.", ...error }, - }; - const gameData = gameFormat({ [game]: data }) as REQUEST_ALL; - return { data: gameData[game], error: null }; - } catch (err) { - console.error(err); - return { data: null, error: { message: "Failed to fetch data." } }; - } - } - - if (Array.isArray(game)) { - try { - const { data, error } = await fetchData( - `/game/all/all/${playerIdentifier}`, - options?.fetch - ); - if (error || !data) - return { - data: null, - error: { message: "Failed to fetch data.", ...error }, - }; - - const gameData = gameFormat(data) as REQUEST_ALL; - const FILTER_GAMES = game as GAME[]; - - let filteredGamesEntries = Object.entries(gameData).filter( - ([key]) => FILTER_GAMES.includes(key as GAME) - ); - - let filteredGames = Object.fromEntries(filteredGamesEntries) as { - [M in G]: GAME_STATS_ALL; - }; - - return { - data: filteredGames, - error: null, - player: formats.main(data.main) as USER_MAIN, - }; - } catch (err) { - console.error(err); - return { data: null, error: { message: "Failed to fetch data." } }; - } - } - - return { data: null, error: { message: "Failed to detect game type." } }; -} diff --git a/src/methods/getGameMetadata.ts b/src/methods/getGameMetadata.ts deleted file mode 100644 index e210801..0000000 --- a/src/methods/getGameMetadata.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { API_METADATA, Options } from "../types/API"; -import { GAME } from "../types/GAME_INFO"; -import { MethodResponse } from "../types/METHODS"; -import fetchData from "./fetchData"; - -export default async function getGameMetadata( - game: GAME, - options?: Options -): Promise>; - -export default async function getGameMetadata( - game: GAME, - options?: Options -): Promise> { - try { - const { data, error } = await fetchData( - `/game/meta/${game}`, - options?.fetch - ); - if (error || !data) - return { - data: null, - error: { message: "Failed to fetch data.", ...error }, - }; - - return { data: data, error: null }; - } catch (err) { - console.error(err); - return { data: null, error: { message: "Failed to fetch data." } }; - } -} diff --git a/src/methods/getGlobalStatistics.ts b/src/methods/getGlobalStatistics.ts deleted file mode 100644 index 55bb487..0000000 --- a/src/methods/getGlobalStatistics.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { API_GLOBAL_STATISTICS, Options } from "../types/API"; -import { MethodResponse } from "../types/METHODS"; -import fetchData from "./fetchData"; - -export default async function getGlobalStatistics( - options?: Options -): Promise>; - -export default async function getGlobalStatistics( - options?: Options -): Promise> { - try { - const { data, error } = await fetchData( - `/global/statistics`, - options?.fetch - ); - if (error || !data) - return { - data: null, - error: { message: "Failed to fetch data.", ...error }, - }; - - return { data, error: null }; - } catch (err) { - console.error(err); - return { data: null, error: { message: "Failed to fetch data." } }; - } -} diff --git a/src/methods/getMaps.ts b/src/methods/getMaps.ts deleted file mode 100644 index f28edbb..0000000 --- a/src/methods/getMaps.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { API_MAP, Options } from "../types/API"; -import { GAME } from "../types/GAME_INFO"; -import { MethodResponse } from "../types/METHODS"; -import fetchData from "./fetchData"; - -const VALID_GAMES = [ - GAME.BlockDrop, - GAME.CaptureTheFlag, - GAME.DeathRun, - GAME.Gravity, - GAME.GroundWars, - GAME.HideAndSeek, - GAME.MurderMystery, - GAME.Skywars, - GAME.SurvivalGames, - GAME.TreasureWars, - GAME.TheBridge, -]; - -export default async function getMaps( - game: GAME, - options?: Options -): Promise>; - -export default async function getMaps( - game: GAME, - options?: Options -): Promise> { - if (!VALID_GAMES.includes(game)) - return { - data: null, - error: { - message: - "This game does not support this endpoint as it only has one map.", - }, - }; - - try { - const { data, error } = await fetchData( - `/game/map/${game}`, - options?.fetch - ); - if (error || !data) - return { - data: null, - error: { message: "Failed to fetch data.", ...error }, - }; - - return { data: data, error: null }; - } catch (err) { - console.error(err); - return { data: null, error: { message: "Failed to fetch data." } }; - } -} diff --git a/src/methods/getMonthlyLeaderboard.ts b/src/methods/getMonthlyLeaderboard.ts index ffd9431..4845e00 100644 --- a/src/methods/getMonthlyLeaderboard.ts +++ b/src/methods/getMonthlyLeaderboard.ts @@ -1,82 +1,58 @@ -import { gameFormatArray } from "../format/gameFormat"; -import { Options } from "../types/API"; -import { LB_STATS, REQUEST_LB } from "../types/GAMES"; -import { GAME } from "../types/GAME_INFO"; -import { MethodResponse } from "../types/METHODS"; -import fetchData from "./fetchData"; - -interface OptionsType extends Options { - year?: number; - month?: number; - date?: Date; - skip?: number; - amount?: number; +import { Game, Routes, Timeframe } from "hive-bedrock-data"; +import { APIResponse, Options } from "../types/types"; +import fetchEndpoint from "../helpers/fetchEndpoint"; +import isGame from "../helpers/isGame"; +import processors from "../processors"; +import { LeaderboardResponse } from "../types/output"; + +interface MonthlyOptions extends Options { + month: number; + year: number; + amount: number; + skip: number; } - -export default async function getMonthlyLeaderboard( - game: G, - options?: OptionsType -): Promise[]>>; - -export default async function getMonthlyLeaderboard( - game: G, - options?: OptionsType -): Promise>> { - if ( - !options?.year && - !options?.amount && - !options?.month && - !options?.skip && - !options?.date - ) { - try { - const { data, error } = await fetchData( - `/game/monthly/${game}`, - options?.fetch - ); - if (error || !data) - return { - data: null, - error: { message: "Failed to fetch data.", ...error }, - }; - - const gameData = gameFormatArray(game, data) as REQUEST_LB; - - return { data: gameData, error: null }; - } catch (err) { - console.error(err); - return { data: null, error: { message: "Failed to fetch data." } }; - } - } else { - let year = options.year ?? new Date().getFullYear(); - let month = options.month ?? new Date().getMonth() + 1; - let amount = options.amount ?? 100; - let skip = options.skip ?? 0; - - if (options.date) { - year = options.date.getFullYear(); - month = options.date.getMonth() + 1; - } - - try { - let { data, error } = await fetchData( - `/game/monthly/${game}/${year}/${month}/${amount}/${skip}`, - options?.fetch - ); - if (error || !data) - return { - data: null, - error: { message: "Failed to fetch data.", ...error }, - }; - - if (!Array.isArray(data)) data = Object.values(data); - - const gameData = gameFormatArray(game, data) as REQUEST_LB; - - return { data: gameData, error: null }; - } catch (err) { - console.error(err); - return { data: null, error: { message: "Failed to fetch data." } }; - } - } +export default function getMonthlyLeaderboard( + game_id: G, + options?: Partial +): Promise>>; + +export default async function getMonthlyLeaderboard( + game_id: G, + options?: Partial +): Promise>> { + if (!isGame(game_id)) + return { + status: 404, + error: { + code: 404, + message: "Game not found.", + }, + data: null, + }; + + let current_date = new Date(); + let endpoint = `/game/monthly/${game_id}/${ + options?.year ?? current_date.getFullYear() + }/${options?.month ?? current_date.getMonth() + 1}/${ + options?.amount ?? 100 + }/${options?.skip ?? 0}` as const; + + let response = await fetchEndpoint(endpoint, options?.init); + if (response.error) return response; + response.data = Object.values(response.data) as Routes; + + processors.leaderboard[Timeframe.Monthly][game_id].forEach((processor) => + processor(response.data) + ); + + let data = response.data as unknown as LeaderboardResponse< + G, + Timeframe.Monthly + >; + + return { + ...response, + data, + error: null, + }; } diff --git a/src/methods/getMonthlyStatistics.ts b/src/methods/getMonthlyStatistics.ts new file mode 100644 index 0000000..05ebc14 --- /dev/null +++ b/src/methods/getMonthlyStatistics.ts @@ -0,0 +1,111 @@ +import { Game, PlayerMetadata, Statistics, Timeframe } from "hive-bedrock-data"; +import { APIResponse, Options } from "../types/types"; +import { + AllGameStatistics, + AllStatistics, + StatisticsResponse, +} from "../types/output"; +import isGame from "../helpers/isGame"; +import fetchEndpoint from "../helpers/fetchEndpoint"; +import processors from "../processors"; + +interface MonthlyOptions extends Options { + month: number; + year: number; +} + +export default async function getMonthlyStatistics( + identifier: string, + options?: MonthlyOptions +): Promise>>; + +export default async function getMonthlyStatistics( + identifier: string, + game_id: G, + options?: MonthlyOptions +): Promise>>; + +export default async function getMonthlyStatistics( + identifier: string, + game_or_options?: G | MonthlyOptions, + options?: MonthlyOptions +) { + let game_id: G | "all" = "all"; + let method_options: MonthlyOptions | undefined = + game_or_options as MonthlyOptions; + + if (!isGame(game_id as G) && game_id !== "all") + return { + status: 404, + error: { + code: 404, + message: "Game not found.", + }, + data: null, + }; + + if (isGame(game_or_options as G)) { + game_id = game_or_options as G; + method_options = options; + } + + let current_date = new Date(); + let endpoint = `/game/monthly/player/${game_id}/${identifier}/${ + options?.year ?? current_date.getFullYear() + }/${options?.month ?? current_date.getMonth() + 1}` as const; + + let response = await fetchEndpoint(endpoint, method_options?.init); + if (response.error) return response; + + if (game_id === "all") { + let games = Object.entries(response.data); + let output: Partial> = {}; + + for (let [g, stats] of games) { + if (isGame(g as Game)) { + if ( + Array.isArray(stats) || + typeof stats.played === "undefined" + ) { + output[g as Game] = null; + continue; + } + + processors.statistics.all[g as Game].forEach((processor) => + processor( + stats as StatisticsResponse + ) + ); + output[g as Game] = stats; + } + } + + return { + ...response, + data: output, + }; + } + + let response_data = response.data as unknown as Statistics< + G, + Timeframe.Monthly + >; + + if (Array.isArray(response_data)) + return { + ...response, + status: 404, + data: null, + error: { code: 404, message: "Not Found" }, + }; + + processors.statistics.all[game_id].forEach((processor) => + processor(response_data as StatisticsResponse) + ); + + return { + ...response, + data: response_data, + error: null, + }; +} diff --git a/src/methods/getMonthlyStats.ts b/src/methods/getMonthlyStats.ts deleted file mode 100644 index 23bdb4f..0000000 --- a/src/methods/getMonthlyStats.ts +++ /dev/null @@ -1,185 +0,0 @@ -import gameFormat from "../format/gameFormat"; -import { Options } from "../types/API"; -import { - BASE_GAME_MONTHLY, - GAME_STATS, - GAME_STATS_ALL, - GAME_STATS_MONTHLY, - REQUEST_MONTHLY, -} from "../types/GAMES"; -import { GAME } from "../types/GAME_INFO"; -import { MethodResponse } from "../types/METHODS"; -import fetchData from "./fetchData"; - -interface OptionsType extends Options { - year?: number; - month?: number; - date?: Date; -} - -export default async function getMonthlyStats( - playerIdentifier: string, - options?: Options -): Promise>>; - -export default async function getMonthlyStats( - playerIdentifier: string, - game: G, - options?: OptionsType -): Promise[G]>>; - -export default async function getMonthlyStats( - playerIdentifier: string, - game: G[], - options?: Options -): Promise }>>; - -export default async function getMonthlyStats( - playerIdentifier: string, - game?: G | G[] | OptionsType, - options?: OptionsType -): Promise< - MethodResponse< - | GAME_STATS - | GAME_STATS[G] - | { [M in G]: GAME_STATS_MONTHLY } - > -> { - if (typeof game === "object" && !(game instanceof Array)) { - options = game; - game = undefined; - } - - if (!game) { - try { - const { data, error } = await fetchData( - `/game/monthly/player/all/${playerIdentifier}`, - options?.fetch - ); - if (error || !data) - return { - data: null, - error: { message: "Failed to fetch data.", ...error }, - }; - - const gameData = gameFormat(data) as REQUEST_MONTHLY; - - let filteredGamesEntries = Object.entries(gameData).filter( - ([key]) => Object.values(GAME).includes(key as GAME) - ); - - let filteredGames = Object.fromEntries( - filteredGamesEntries - ) as GAME_STATS; - - return { data: filteredGames, error: null }; - } catch (err) { - console.error(err); - return { data: null, error: { message: "Failed to fetch data." } }; - } - } - - if ( - (options?.year || options?.month || options?.date) && - typeof game === "string" - ) { - let year = options.year ?? new Date().getFullYear(); - let month = options.month ?? new Date().getMonth() + 1; - - if (options.date) { - year = options.date.getFullYear(); - month = options.date.getMonth() + 1; - } - - try { - const { data, error } = await fetchData( - `/game/monthly/player/${game}/${playerIdentifier}/${year}/${month}`, - options?.fetch - ); - if (error || !data) - return { - data: null, - error: { message: "Failed to fetch data.", ...error }, - }; - - const gameData = gameFormat( - { - [game]: data, - }, - true - ) as GAME_STATS[G]; - - return { - data: gameData as GAME_STATS[G], - error: null, - }; - } catch (err) { - console.error(err); - return { data: null, error: { message: "Failed to fetch data." } }; - } - } - - if (typeof game === "string") { - try { - const { data, error } = await fetchData( - `/game/monthly/player/${game}/${playerIdentifier}`, - options?.fetch - ); - if (error || !data) - return { - data: null, - error: { message: "Failed to fetch data.", ...error }, - }; - - const gameData = gameFormat( - { - [game]: data, - }, - true - ) as GAME_STATS[G]; - - return { - data: gameData as GAME_STATS[G], - error: null, - }; - } catch (err) { - console.error(err); - return { data: null, error: { message: "Failed to fetch data." } }; - } - } - - if (Array.isArray(game)) { - try { - const { data, error } = await fetchData( - `/game/monthly/player/all/${playerIdentifier}`, - options?.fetch - ); - if (error || !data) - return { - data: null, - error: { message: "Failed to fetch data.", ...error }, - }; - - const FILTER_GAMES = game as GAME[]; - - let filteredGamesEntries = Object.entries(data).filter(([key]) => - FILTER_GAMES.includes(key as GAME) - ); - - let filteredGames = Object.fromEntries(filteredGamesEntries) as { - [M in G]: GAME_STATS_MONTHLY; - }; - - const gameData = gameFormat(filteredGames) as { - [M in G]: GAME_STATS_MONTHLY; - }; - - return { data: gameData, error: null }; - } catch (err) { - console.error(err); - return { data: null, error: { message: "Failed to fetch data." } }; - } - } - - return { data: null, error: { message: "Failed to detect game type." } }; -} diff --git a/src/methods/getPlayerInfo.ts b/src/methods/getPlayerInfo.ts deleted file mode 100644 index 7253325..0000000 --- a/src/methods/getPlayerInfo.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Options } from "../types/API"; -import { USER_MAIN } from "../types/GAMES"; -import { MethodResponse } from "../types/METHODS"; -import fetchData from "./fetchData"; - -export default async function getPlayerInfo( - playerIdentifier: string, - options?: Options -): Promise>; - -export default async function getPlayerInfo( - playerIdentifier: string, - options?: Options -): Promise> { - try { - const { data, error } = await fetchData( - `/game/all/main/${playerIdentifier}`, - options?.fetch - ); - if (error || !data) - return { - data: null, - error: { message: "Failed to fetch data.", ...error }, - }; - - return { data: data.main, error: null }; - } catch (err) { - console.error(err); - return { data: null, error: { message: "Failed to fetch data." } }; - } -} diff --git a/src/processors/common.ts b/src/processors/common.ts new file mode 100644 index 0000000..b984394 --- /dev/null +++ b/src/processors/common.ts @@ -0,0 +1,13 @@ +import { Game, Timeframe } from "hive-bedrock-data"; +import { StatisticsResponse } from "../types/output"; + +const commonProcessors: (( + stats: StatisticsResponse +) => void)[] = [ + (stats) => (stats.victories = stats.victories ?? 0), + (stats) => (stats.losses = stats.played - stats.victories), + (stats) => + (stats.win_percentage = + stats.played > 0 ? stats.victories / stats.played : 0), +]; +export default commonProcessors; diff --git a/src/processors/index.ts b/src/processors/index.ts new file mode 100644 index 0000000..532466f --- /dev/null +++ b/src/processors/index.ts @@ -0,0 +1,72 @@ +import { Game, Leaderboards, Timeframe } from "hive-bedrock-data"; +import commonProcessors from "./common"; +import leaderboardModifier from "./leaderboard"; +import { GameProcessors } from "../types/output"; + +const processors = { + statistics: { + [Timeframe.AllTime]: { + [Game.BlockDrop]: [...commonProcessors], + [Game.BlockParty]: [...commonProcessors], + [Game.CaptureTheFlag]: [...commonProcessors], + [Game.DeathRun]: [...commonProcessors], + [Game.Gravity]: [...commonProcessors], + [Game.GroundWars]: [...commonProcessors], + [Game.HideAndSeek]: [...commonProcessors], + [Game.JustBuild]: [...commonProcessors], + [Game.MurderMystery]: [...commonProcessors], + [Game.Skywars]: [...commonProcessors], + [Game.SurvivalGames]: [...commonProcessors], + [Game.TheBridge]: [...commonProcessors], + [Game.TreasureWars]: [...commonProcessors], + }, + [Timeframe.Monthly]: { + [Game.BlockDrop]: [...commonProcessors], + [Game.BlockParty]: [...commonProcessors], + [Game.CaptureTheFlag]: [...commonProcessors], + [Game.DeathRun]: [...commonProcessors], + [Game.Gravity]: [...commonProcessors], + [Game.GroundWars]: [...commonProcessors], + [Game.HideAndSeek]: [...commonProcessors], + [Game.JustBuild]: [...commonProcessors], + [Game.MurderMystery]: [...commonProcessors], + [Game.Skywars]: [...commonProcessors], + [Game.SurvivalGames]: [...commonProcessors], + [Game.TheBridge]: [...commonProcessors], + [Game.TreasureWars]: [...commonProcessors], + }, + }, + leaderboard: { + [Timeframe.AllTime]: { + [Game.BlockDrop]: [leaderboardModifier(...commonProcessors)], + [Game.BlockParty]: [leaderboardModifier(...commonProcessors)], + [Game.CaptureTheFlag]: [leaderboardModifier(...commonProcessors)], + [Game.DeathRun]: [leaderboardModifier(...commonProcessors)], + [Game.Gravity]: [leaderboardModifier(...commonProcessors)], + [Game.GroundWars]: [leaderboardModifier(...commonProcessors)], + [Game.HideAndSeek]: [leaderboardModifier(...commonProcessors)], + [Game.JustBuild]: [leaderboardModifier(...commonProcessors)], + [Game.MurderMystery]: [leaderboardModifier(...commonProcessors)], + [Game.Skywars]: [leaderboardModifier(...commonProcessors)], + [Game.SurvivalGames]: [leaderboardModifier(...commonProcessors)], + [Game.TheBridge]: [leaderboardModifier(...commonProcessors)], + [Game.TreasureWars]: [leaderboardModifier(...commonProcessors)], + } as GameProcessors, + [Timeframe.Monthly]: { + [Game.BlockDrop]: [leaderboardModifier(...commonProcessors)], + [Game.BlockParty]: [leaderboardModifier(...commonProcessors)], + [Game.CaptureTheFlag]: [leaderboardModifier(...commonProcessors)], + [Game.DeathRun]: [leaderboardModifier(...commonProcessors)], + [Game.Gravity]: [leaderboardModifier(...commonProcessors)], + [Game.GroundWars]: [leaderboardModifier(...commonProcessors)], + [Game.HideAndSeek]: [leaderboardModifier(...commonProcessors)], + [Game.JustBuild]: [leaderboardModifier(...commonProcessors)], + [Game.MurderMystery]: [leaderboardModifier(...commonProcessors)], + [Game.Skywars]: [leaderboardModifier(...commonProcessors)], + [Game.SurvivalGames]: [leaderboardModifier(...commonProcessors)], + [Game.TheBridge]: [leaderboardModifier(...commonProcessors)], + [Game.TreasureWars]: [leaderboardModifier(...commonProcessors)], + } as GameProcessors, + }, +}; +export default processors; diff --git a/src/processors/leaderboard.ts b/src/processors/leaderboard.ts new file mode 100644 index 0000000..75eb927 --- /dev/null +++ b/src/processors/leaderboard.ts @@ -0,0 +1,14 @@ +import { Game, Leaderboards, Timeframe } from "hive-bedrock-data"; + +export default function leaderboardModifier( + ...processors: ((stats: any) => void)[] +) { + return (stats: any[]) => { + return stats.map((record) => { + for (let processor of processors) { + processor(record); + } + return record; + }); + }; +} diff --git a/src/types/API.ts b/src/types/API.ts deleted file mode 100644 index f0465c1..0000000 --- a/src/types/API.ts +++ /dev/null @@ -1,263 +0,0 @@ -import { FetchOptions } from "../methods/fetchData"; -import { GAME } from "./GAME_INFO"; - -export interface API_ERROR { - request?: { - status: number; - statusText: string; - }; - ratelimit?: { - limit: string | null; - remaining: string | null; - }; - error?: any; -} - -export interface Options { - fetch?: FetchOptions; -} - -export enum MAP_SEASON { - None = "NO_SEASON", - Winter = "WINTERFEST", - Spring = "SPRING", - Summer = "SUMMER", - Halloween = "HALLOWEEN", - Autumn = "AUTUMN", -} - -export enum MAP_VARIANT { - Regular = "REGULAR", - Duos = "DUOS", - Trios = "TRIOS", - Squads = "SQUADS", - Mega = "MEGA", - Royale = "ROYALE", -} - -export enum RANK { - Regular = "REGULAR", - Plus = "PLUS", - Youtuber = "YOUTUBER", - Streamer = "STREAMER", - Tiktok = "TIKTOK", - VIP = "VIP", - Helper = "HELPER", - Moderator = "MODERATOR", - Hive = "HIVE_TEAM", - StaffManager = "STAFF_MANAGER", - CommunityManager = "COMMUNITY_MANAGER", - Owner = "OWNER", -} - -export type API_REQUEST_ALL = { - [G in GAME]: API_GAME_STATS_ALL; -} & { - main: API_USER_MAIN; -}; - -export type API_REQUEST_MONTHLY = { - [G in GAME]: API_GAME_STATS_MONTHLY; -}; - -export type API_REQUEST_LB = API_LB_STATS[]; - -export type API_GAME_STATS< - M extends API_BASE_GAME_ALL | API_BASE_GAME_MONTHLY | API_BASE_LB -> = { - [GAME.HideAndSeek]: M & API_GAME_HIDE; - [GAME.DeathRun]: M & API_GAME_DR; - [GAME.TreasureWars]: M & API_GAME_WARS; - [GAME.MurderMystery]: M & API_GAME_MURDER; - [GAME.SurvivalGames]: M & API_GAME_SG; - [GAME.Skywars]: M & API_GAME_SKY; - [GAME.CaptureTheFlag]: M & API_GAME_CTF; - [GAME.BlockDrop]: M & API_GAME_DROP; - [GAME.GroundWars]: M & API_GAME_GROUND; - [GAME.JustBuild]: M & API_GAME_BUILD; - [GAME.BlockParty]: M & API_GAME_PARTY; - [GAME.TheBridge]: M & API_GAME_BRIDGE; - [GAME.Gravity]: M & API_GAME_GRAV; -}; - -export type API_LB_STATS = API_GAME_STATS[G]; -export type API_GAME_STATS_ALL = - API_GAME_STATS[G]; -export type API_GAME_STATS_MONTHLY = - API_GAME_STATS[G]; - -export interface API_BASE_LB extends API_BASE_GAME_MONTHLY { - UUID: string; -} - -export interface API_GLOBAL_STATISTICS { - unique_players: { - [key in GAME]: number; - } & { - global: number; - main: number; - }; -} - -export interface API_MAP { - name: string; - season: MAP_SEASON; - variant: MAP_VARIANT; - image: string; -} - -export interface API_METADATA { - name: string; - shortName: string; - maxLevel: number; - allowPrestiging: boolean; - maxPrestige: number; - experienceToLevel: { - [xp: string]: number; - }; -} - -export interface API_BASE_GAME_ALL { - UUID: string; - xp: number; - played: number; - victories: number; - first_played: number; -} - -export interface API_BASE_GAME_MONTHLY { - index: number; - human_index: number; - username: string; - xp: number; - played: number; - victories: number; -} - -export type AVATAR = { url: string; name: string }; - -export interface API_USER_MAIN { - UUID: string; - xuid: number; - - username: string; - username_cc: string; - - rank: RANK; - first_played: number; - - daily_login_streak: number; - longest_daily_login_streak: number; - - hub_title_count: number; - hub_title_unlocked: string[]; - costume_count: number; - costume_unlocked: string[]; - avatar_count: number; - avatar_unlocked: AVATAR[]; - friend_count: number; - - equipped_hub_title: string | null; - equipped_costume: string | null; - equipped_avatar: AVATAR | null; - - quest_count: number; - paid_ranks: RANK[]; - - pets: string[]; - mounts: string[]; - hats: string[]; -} - -export interface API_GAME_HIDE { - deaths: number; - hider_kills: number; - seeker_kills: number; -} - -export interface API_GAME_DR { - deaths: number; - checkpoints: number; - activated: number; - kills: number; -} - -export interface API_GAME_WARS { - final_kills: number; - kills: number; - treasure_destroyed: number; - deaths: number; - prestige: number; -} - -export interface API_GAME_MURDER { - deaths: number; - coins: number; - murders: number; - murderer_eliminations: number; - prestige: number; -} - -export interface API_GAME_SG { - crates: number; - deathmatches: number; - cows: number; - kills: number; - deaths: number; -} - -export interface API_GAME_SKY { - kills: number; - mystery_chests_destroyed: number; - ores_mined: number; - spells_used: number; - deaths: number; -} - -export interface API_GAME_CTF { - assists: number; - deaths: number; - flags_captured: number; - kills: number; - flags_returned: number; -} - -export interface API_GAME_DROP { - blocks_destroyed: number; - powerups_collected: number; - vaults_used: number; - deaths: number; -} - -export interface API_GAME_GROUND { - blocks_destroyed: number; - blocks_placed: number; - deaths: number; - projectiles_fired: number; - kills: number; -} - -export interface API_GAME_BUILD { - rating_good_received: number; - rating_love_received: number; - rating_meh_received: number; - rating_okay_received: number; - rating_great_received: number; -} - -export interface API_GAME_PARTY { - powerups_collected: number; - rounds_survived: number; -} - -export interface API_GAME_BRIDGE { - deaths: number; - goals: number; - kills: number; -} - -export interface API_GAME_GRAV { - deaths: number; - maps_completed: number; - maps_completed_without_dying: number; -} diff --git a/src/types/ENDPOINTS.ts b/src/types/ENDPOINTS.ts deleted file mode 100644 index bc80a57..0000000 --- a/src/types/ENDPOINTS.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { GAME } from "./GAME_INFO"; - -export type PLAYER_ALLTIME_ENDPOINT< - G extends GAME | "all" | "main", - ID extends string -> = `/game/all/${G}/${ID}`; - -export type PLAYER_MONTHLY_ENDPOINT< - G extends GAME | "all", - ID extends string -> = `/game/monthly/player/${G}/${ID}`; - -export type GAME_LB_ALLTIME_ENDPOINT = `/game/all/${G}`; -export type GAME_LB_MONTHLY_ENDPOINT = `/game/monthly/${G}`; - -export type PLAYER_LB_SPECIFIC_MONTHLY_ENDPOINT< - G extends GAME, - ID extends string, - Y extends number, - M extends number, - A extends number, - S extends number -> = `/game/monthly/player/${G}/${ID}/${Y}/${M}/${A}/${S}`; - -export type GAME_LB_SPECIFIC_MONTHLY_ENDPOINT< - G extends GAME, - Y extends number, - M extends number, - A extends number, - S extends number -> = `/game/monthly/${G}/${Y}/${M}/${A}/${S}`; - -export type GLOBAL_STATISTICS_ENDPOINT = `/global/statistics`; -export type GAME_MAPS_ENDPOINT = `/game/map/${G}`; -export type GAME_METADATA_ENDPOINT = `/game/meta/${G}`; - -export type ENDPOINTS = - | PLAYER_ALLTIME_ENDPOINT<"all", string> - | PLAYER_ALLTIME_ENDPOINT - | PLAYER_MONTHLY_ENDPOINT<"all", string> - | PLAYER_MONTHLY_ENDPOINT - | GAME_LB_ALLTIME_ENDPOINT - | GAME_LB_MONTHLY_ENDPOINT - | PLAYER_LB_SPECIFIC_MONTHLY_ENDPOINT< - G, - string, - number, - number, - number, - number - > - | GAME_LB_SPECIFIC_MONTHLY_ENDPOINT - | GLOBAL_STATISTICS_ENDPOINT - | GAME_MAPS_ENDPOINT - | GAME_METADATA_ENDPOINT; diff --git a/src/types/GAMES.ts b/src/types/GAMES.ts deleted file mode 100644 index 53f58c1..0000000 --- a/src/types/GAMES.ts +++ /dev/null @@ -1,105 +0,0 @@ -import { - API_BASE_GAME_ALL, - API_BASE_GAME_MONTHLY, - API_BASE_LB, - API_GAME_BRIDGE, - API_GAME_BUILD, - API_GAME_CTF, - API_GAME_DR, - API_GAME_DROP, - API_GAME_GRAV, - API_GAME_GROUND, - API_GAME_HIDE, - API_GAME_MURDER, - API_GAME_PARTY, - API_GAME_SG, - API_GAME_SKY, - API_GAME_WARS, - API_USER_MAIN, -} from "./API"; -import { GAME } from "./GAME_INFO"; - -export type REQUEST_ALL = { - [G in GAME]: GAME_STATS_ALL; -} & { - main: USER_MAIN; -}; -export type REQUEST_MONTHLY = { - [G in GAME]: GAME_STATS_MONTHLY; -}; -export type REQUEST_LB = LB_STATS[]; - -export type GAME_STATS = - { - [GAME.HideAndSeek]: M & GAME_HIDE; - [GAME.DeathRun]: M & GAME_DR; - [GAME.TreasureWars]: M & GAME_WARS; - [GAME.MurderMystery]: M & GAME_MURDER; - [GAME.SurvivalGames]: M & GAME_SG; - [GAME.Skywars]: M & GAME_SKY; - [GAME.CaptureTheFlag]: M & GAME_CTF; - [GAME.BlockDrop]: M & GAME_DROP; - [GAME.GroundWars]: M & GAME_GROUND; - [GAME.JustBuild]: M & GAME_BUILD; - [GAME.BlockParty]: M & GAME_PARTY; - [GAME.TheBridge]: M & GAME_BRIDGE; - [GAME.Gravity]: M & GAME_GRAV; - }; - -export type LB_STATS = GAME_STATS[G]; -export type GAME_STATS_ALL = GAME_STATS[G]; -export type GAME_STATS_MONTHLY = - GAME_STATS[G]; - -export interface BASE_LB extends API_BASE_LB { - id: GAME; - level: number; - losses: number; - win_percentage: number; -} -export interface BASE_GAME_ALL extends Omit { - id: GAME; - level: number; - losses: number; - first_played: number; - win_percentage: number; -} -export interface BASE_GAME_MONTHLY extends API_BASE_GAME_MONTHLY { - id: GAME; - level: number; - losses: number; - win_percentage: number; -} -export interface USER_MAIN extends Omit { - first_played: number; -} - -export interface GAME_HIDE extends API_GAME_HIDE {} -export interface GAME_DR extends API_GAME_DR { - kdr: number; -} -export interface GAME_WARS extends API_GAME_WARS { - kdr: number; -} -export interface GAME_MURDER extends API_GAME_MURDER { - kdr: number; -} -export interface GAME_SG extends API_GAME_SG { - kdr: number; -} -export interface GAME_SKY extends API_GAME_SKY { - kdr: number; -} -export interface GAME_CTF extends API_GAME_CTF { - kdr: number; -} -export interface GAME_DROP extends API_GAME_DROP {} -export interface GAME_GROUND extends API_GAME_GROUND { - kdr: number; -} -export interface GAME_BUILD extends API_GAME_BUILD {} -export interface GAME_PARTY extends API_GAME_PARTY {} -export interface GAME_BRIDGE extends API_GAME_BRIDGE { - kdr: number; -} -export interface GAME_GRAV extends API_GAME_GRAV {} diff --git a/src/types/GAME_INFO.ts b/src/types/GAME_INFO.ts deleted file mode 100644 index 233cc52..0000000 --- a/src/types/GAME_INFO.ts +++ /dev/null @@ -1,117 +0,0 @@ -export enum GAME { - HideAndSeek = "hide", - DeathRun = "dr", - TreasureWars = "wars", - MurderMystery = "murder", - SurvivalGames = "sg", - Skywars = "sky", - CaptureTheFlag = "ctf", - BlockDrop = "drop", - GroundWars = "ground", - JustBuild = "build", - BlockParty = "party", - TheBridge = "bridge", - Gravity = "grav", -} - -interface GAME_INFO_TYPE { - id: string; - maxLevel: number; - increment: number; - incrementCap: number | null; - prestige: boolean; -} - -export const GAME_INFO: { [key in GAME]: GAME_INFO_TYPE } = { - [GAME.TreasureWars]: { - id: GAME.TreasureWars, - maxLevel: 100, - increment: 150, - incrementCap: 52, - prestige: true, - }, - [GAME.DeathRun]: { - id: GAME.DeathRun, - maxLevel: 75, - increment: 200, - incrementCap: 42, - prestige: false, - }, - [GAME.HideAndSeek]: { - id: GAME.HideAndSeek, - maxLevel: 75, - increment: 100, - incrementCap: null, - prestige: false, - }, - [GAME.MurderMystery]: { - id: GAME.MurderMystery, - maxLevel: 100, - increment: 100, - incrementCap: 82, - prestige: true, - }, - [GAME.SurvivalGames]: { - id: GAME.SurvivalGames, - maxLevel: 30, - increment: 150, - incrementCap: null, - prestige: false, - }, - [GAME.Skywars]: { - id: GAME.Skywars, - maxLevel: 75, - increment: 150, - incrementCap: 52, - prestige: false, - }, - [GAME.JustBuild]: { - id: GAME.JustBuild, - maxLevel: 20, - increment: 100, - incrementCap: null, - prestige: false, - }, - [GAME.GroundWars]: { - id: GAME.GroundWars, - maxLevel: 20, - increment: 150, - incrementCap: null, - prestige: false, - }, - [GAME.BlockDrop]: { - id: GAME.BlockDrop, - maxLevel: 25, - increment: 150, - incrementCap: 22, - prestige: false, - }, - [GAME.CaptureTheFlag]: { - id: GAME.CaptureTheFlag, - maxLevel: 50, - increment: 150, - incrementCap: null, - prestige: false, - }, - [GAME.BlockParty]: { - id: GAME.BlockParty, - maxLevel: 25, - increment: 150, - incrementCap: null, - prestige: false, - }, - [GAME.TheBridge]: { - id: GAME.TheBridge, - maxLevel: 20, - increment: 0, - incrementCap: null, - prestige: false, - }, - [GAME.Gravity]: { - id: GAME.Gravity, - maxLevel: 25, - increment: 150, - incrementCap: null, - prestige: false, - }, -}; diff --git a/src/types/METHODS.ts b/src/types/METHODS.ts deleted file mode 100644 index 3f878da..0000000 --- a/src/types/METHODS.ts +++ /dev/null @@ -1,8 +0,0 @@ -export type MethodResponse = { - data: R | null; - error: { - message: string; - status?: number; - endpoint?: string; - } | null; -}; diff --git a/src/types/output.ts b/src/types/output.ts new file mode 100644 index 0000000..c34174f --- /dev/null +++ b/src/types/output.ts @@ -0,0 +1,72 @@ +import { + Game, + Leaderboards, + PlayerMetadata, + Statistics, + Timeframe, +} from "hive-bedrock-data"; + +type Processor = (stats: any) => void; +export type GameProcessors = { + [key in Game]: Processor[]; +}; + +interface CommonStatistics { + played: number; + victories: number; + losses: number; + win_percentage: number; +} +type BaseStatistics = Statistics & + CommonStatistics; +export type StatisticsResponse< + G extends Game, + T extends Timeframe +> = AllStatistics[G]; +export interface AllStatistics { + [Game.BlockDrop]: BaseStatistics; + [Game.BlockParty]: BaseStatistics; + [Game.CaptureTheFlag]: BaseStatistics; + [Game.DeathRun]: BaseStatistics; + [Game.Gravity]: BaseStatistics; + [Game.GroundWars]: BaseStatistics; + [Game.HideAndSeek]: BaseStatistics; + [Game.JustBuild]: BaseStatistics; + [Game.MurderMystery]: BaseStatistics; + [Game.Skywars]: BaseStatistics; + [Game.SurvivalGames]: BaseStatistics; + [Game.TheBridge]: BaseStatistics; + [Game.TreasureWars]: BaseStatistics; +} + +interface CommonLeaderboard { + played: number; + victories: number; + losses: number; + win_percentage: number; +} +type BaseLeaderboard = Leaderboards & + CommonLeaderboard; +export type LeaderboardResponse< + G extends Game, + T extends Timeframe +> = AllLeaderboards[G]; +export interface AllLeaderboards { + [Game.BlockDrop]: BaseLeaderboard[]; + [Game.BlockParty]: BaseLeaderboard[]; + [Game.CaptureTheFlag]: BaseLeaderboard[]; + [Game.DeathRun]: BaseLeaderboard[]; + [Game.Gravity]: BaseLeaderboard[]; + [Game.GroundWars]: BaseLeaderboard[]; + [Game.HideAndSeek]: BaseLeaderboard[]; + [Game.JustBuild]: BaseLeaderboard[]; + [Game.MurderMystery]: BaseLeaderboard[]; + [Game.Skywars]: BaseLeaderboard[]; + [Game.SurvivalGames]: BaseLeaderboard[]; + [Game.TheBridge]: BaseLeaderboard[]; + [Game.TreasureWars]: BaseLeaderboard[]; +} + +export type AllGameStatistics = { + [key in Game]: BaseStatistics | null; +}; diff --git a/src/types/types.ts b/src/types/types.ts new file mode 100644 index 0000000..4eab085 --- /dev/null +++ b/src/types/types.ts @@ -0,0 +1,22 @@ +interface APIResponse_Base { + duration?: number; + status: number; +} +interface APIResponse_Error { + code: number; + message: string; +} + +export interface APIResponse_Success extends APIResponse_Base { + error: null; + data: T; +} +export interface APIResponse_Failure extends APIResponse_Base { + error: APIResponse_Error; + data: null; +} +export type APIResponse = APIResponse_Success | APIResponse_Failure; + +export interface Options { + init: RequestInit; +} diff --git a/tests/leaderboard/alltime.test.ts b/tests/leaderboard/alltime.test.ts deleted file mode 100644 index ac8a78a..0000000 --- a/tests/leaderboard/alltime.test.ts +++ /dev/null @@ -1,12 +0,0 @@ -import "../setup"; -import { GAME, getAllTimeLeaderboard } from "../../src"; - -describe("all-time leaderboard", () => { - test.concurrent("fetch single game", async () => { - const { data, error } = await getAllTimeLeaderboard(GAME.BlockDrop); - - expect(data![0].id).toBe(GAME.BlockDrop); - expect(data?.length).toBe(50); - expect(error).toBe(null); - }); -}); diff --git a/tests/leaderboard/monthly.test.ts b/tests/leaderboard/monthly.test.ts deleted file mode 100644 index 11c5d98..0000000 --- a/tests/leaderboard/monthly.test.ts +++ /dev/null @@ -1,45 +0,0 @@ -import "../setup"; -import { GAME, getMonthlyLeaderboard } from "../../src"; - -describe("monthly leaderboard", () => { - test.concurrent("fetch single game", async () => { - const { data, error } = await getMonthlyLeaderboard(GAME.BlockDrop); - - expect(data![0].id).toBe(GAME.BlockDrop); - expect(data?.length).toBe(100); - expect(error).toBe(null); - }); - - test.concurrent("fetch previous leaderboard", async () => { - const { data, error } = await getMonthlyLeaderboard(GAME.BlockDrop, { - year: 2023, - month: 1, - }); - - expect(data![0].id).toBe(GAME.BlockDrop); - expect(data?.length).toBe(100); - expect(error).toBe(null); - }); - - test.concurrent("fetch single game with skip", async () => { - const { data, error } = await getMonthlyLeaderboard(GAME.BlockDrop, { - skip: 50, - amount: 50, - }); - - expect(data![0].id).toBe(GAME.BlockDrop); - expect(data![0].index).toBe(50); - expect(data?.length).toBe(50); - expect(error).toBe(null); - }); - - test.concurrent("fetch single game with date", async () => { - const { data, error } = await getMonthlyLeaderboard(GAME.BlockDrop, { - date: new Date(2023, 0), - }); - - expect(data![0].id).toBe(GAME.BlockDrop); - expect(data?.length).toBe(100); - expect(error).toBe(null); - }); -}); diff --git a/tests/misc/global.test.ts b/tests/misc/global.test.ts deleted file mode 100644 index 46a16ed..0000000 --- a/tests/misc/global.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import "../setup"; -import { GAME, getGlobalStatistics } from "../../src"; - -describe("global statistics", () => { - test.concurrent("fetch unique player counts", async () => { - const { data, error } = await getGlobalStatistics(); - - expect(Object.keys(data?.unique_players!)).toIncludeAllMembers([ - ...Object.values(GAME), - "global", - "main", - ]); - expect(error).toBe(null); - }); -}); diff --git a/tests/misc/maps.test.ts b/tests/misc/maps.test.ts deleted file mode 100644 index 62b03f7..0000000 --- a/tests/misc/maps.test.ts +++ /dev/null @@ -1,20 +0,0 @@ -import "../setup"; -import { GAME, getMaps } from "../../src"; - -describe("game maps", () => { - test.concurrent("fetch valid game maps", async () => { - const { data, error } = await getMaps(GAME.TreasureWars); - - expect(data?.length).toBeGreaterThan(3); - expect(error).toBe(null); - }); - - test.concurrent("fetch invalid game maps", async () => { - const { data, error } = await getMaps(GAME.BlockParty); - - expect(data).toBe(null); - expect(error?.message).toBe( - "This game does not support this endpoint as it only has one map." - ); - }); -}); diff --git a/tests/misc/metadata.test.ts b/tests/misc/metadata.test.ts deleted file mode 100644 index cdd2957..0000000 --- a/tests/misc/metadata.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import "../setup"; -import { GAME, getGameMetadata } from "../../src"; - -describe("game maps", () => { - test.concurrent("fetch game metadata", async () => { - const { data, error } = await getGameMetadata(GAME.TreasureWars); - - expect(data?.shortName).toBe(GAME.TreasureWars.toUpperCase()); - expect(error).toBe(null); - }); -}); diff --git a/tests/misc/player.test.ts b/tests/misc/player.test.ts deleted file mode 100644 index 5664014..0000000 --- a/tests/misc/player.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import "../setup"; -import { getPlayerInfo } from "../../src"; - -describe("player infomation", () => { - test.concurrent("fetch player infomation", async () => { - const { data, error } = await getPlayerInfo("ucdfiddes"); - - expect(data?.username).toBe("ucdfiddes"); - expect(error).toBe(null); - }); -}); diff --git a/tests/setup.ts b/tests/setup.ts deleted file mode 100644 index 230125b..0000000 --- a/tests/setup.ts +++ /dev/null @@ -1,2 +0,0 @@ -import * as matchers from "jest-extended"; -expect.extend(matchers); diff --git a/tests/stats/alltime.test.ts b/tests/stats/alltime.test.ts deleted file mode 100644 index 2d45894..0000000 --- a/tests/stats/alltime.test.ts +++ /dev/null @@ -1,45 +0,0 @@ -import "../setup"; -import { GAME, getAllTimeStats } from "../../src"; - -describe("all-time statistics", () => { - test.concurrent("fetch all games", async () => { - const { data, error } = await getAllTimeStats("ucdfiddes"); - - expect(Object.keys(data!)).toIncludeAllMembers(Object.values(GAME)); - expect(error).toBe(null); - }); - - test.concurrent("player is returned", async () => { - const { error, player } = await getAllTimeStats("ucdfiddes"); - - expect(player).toBeObject(); - expect(error).toBe(null); - }); - - test.concurrent("fetch single game", async () => { - const { data, error } = await getAllTimeStats( - "ucdfiddes", - GAME.TreasureWars - ); - - expect(data?.id).toEqual(GAME.TreasureWars); - expect(error).toBe(null); - }); - - test.concurrent("fetch multiple games", async () => { - const { data, error } = await getAllTimeStats("ucdfiddes", [ - GAME.TreasureWars, - GAME.BlockDrop, - GAME.MurderMystery, - GAME.HideAndSeek, - ]); - - expect(Object.keys(data!)).toIncludeAllMembers([ - GAME.TreasureWars, - GAME.BlockDrop, - GAME.MurderMystery, - GAME.HideAndSeek, - ]); - expect(error).toBe(null); - }); -}); diff --git a/tests/stats/monthly.test.ts b/tests/stats/monthly.test.ts deleted file mode 100644 index 6663d06..0000000 --- a/tests/stats/monthly.test.ts +++ /dev/null @@ -1,60 +0,0 @@ -import "../setup"; -import { GAME, getMonthlyStats } from "../../src"; - -describe("monthly statistics", () => { - test.concurrent("fetch all games", async () => { - const { data, error } = await getMonthlyStats("ucdfiddes"); - - expect(Object.keys(data!)).toIncludeAllMembers(Object.values(GAME)); - expect(error).toBe(null); - }); - - test.concurrent("fetch single game", async () => { - const { data, error } = await getMonthlyStats( - "ucdfiddes", - GAME.TreasureWars - ); - - expect(data?.id).toEqual(GAME.TreasureWars); - expect(error).toBe(null); - }); - - test.concurrent("fetch multiple games", async () => { - const { data, error } = await getMonthlyStats("ucdfiddes", [ - GAME.TreasureWars, - GAME.BlockDrop, - GAME.MurderMystery, - GAME.HideAndSeek, - ]); - - expect(Object.keys(data!)).toIncludeAllMembers([ - GAME.TreasureWars, - GAME.BlockDrop, - GAME.MurderMystery, - GAME.HideAndSeek, - ]); - expect(error).toBe(null); - }); - - test.concurrent("fetch single game with month and year", async () => { - const { data, error } = await getMonthlyStats( - "ucdfiddes", - GAME.TreasureWars, - { year: 2023, month: 6 } - ); - - expect(data?.id).toEqual(GAME.TreasureWars); - expect(error).toBe(null); - }); - - test.concurrent("fetch single game with date", async () => { - const { data, error } = await getMonthlyStats( - "ucdfiddes", - GAME.TreasureWars, - { date: new Date(2023, 5) } - ); - - expect(data?.id).toEqual(GAME.TreasureWars); - expect(error).toBe(null); - }); -}); From 357242a1ce7d5bbed389bc2fcb34b1f3eaf86301 Mon Sep 17 00:00:00 2001 From: UCDFiddes <66165184+UCDFiddes@users.noreply.github.com> Date: Sun, 11 Feb 2024 22:17:54 +0000 Subject: [PATCH 02/13] add methods and processors --- src/index.ts | 6 + src/methods/getAllTimeStatistics.ts | 6 +- src/methods/getGlobalStatistics.ts | 14 ++ src/methods/getMaps.ts | 33 +++ src/methods/getMetadata.ts | 33 +++ src/methods/getMonthlyStatistics.ts | 10 +- src/processors/game.ts | 6 + src/processors/index.ts | 343 +++++++++++++++++++++++----- src/processors/level.ts | 12 + src/processors/pvp.ts | 27 +++ src/types/output.ts | 29 ++- 11 files changed, 449 insertions(+), 70 deletions(-) create mode 100644 src/methods/getGlobalStatistics.ts create mode 100644 src/methods/getMaps.ts create mode 100644 src/methods/getMetadata.ts create mode 100644 src/processors/game.ts create mode 100644 src/processors/level.ts create mode 100644 src/processors/pvp.ts diff --git a/src/index.ts b/src/index.ts index d9e5ac1..9e581f2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,8 @@ import getAllTimeLeaderboard from "./methods/getAllTimeLeaderboard"; import getAllTimeStatistics from "./methods/getAllTimeStatistics"; +import getGlobalStatistics from "./methods/getGlobalStatistics"; +import getMaps from "./methods/getMaps"; +import getMetdata from "./methods/getMetadata"; import getMonthlyLeaderboard from "./methods/getMonthlyLeaderboard"; import getMonthlyStatistics from "./methods/getMonthlyStatistics"; @@ -9,4 +12,7 @@ export { getMonthlyLeaderboard, getAllTimeStatistics, getMonthlyStatistics, + getGlobalStatistics, + getMaps, + getMetdata, }; diff --git a/src/methods/getAllTimeStatistics.ts b/src/methods/getAllTimeStatistics.ts index 1d8c6c0..afb15be 100644 --- a/src/methods/getAllTimeStatistics.ts +++ b/src/methods/getAllTimeStatistics.ts @@ -65,9 +65,7 @@ export default async function getAllTimeStatistics( } processors.statistics.all[g as Game].forEach((processor) => - processor( - stats as StatisticsResponse - ) + processor(stats as any) ); output[g as Game] = stats; } @@ -96,7 +94,7 @@ export default async function getAllTimeStatistics( }; processors.statistics.all[game_id].forEach((processor) => - processor(response_data as StatisticsResponse) + processor(response_data as any) ); return { diff --git a/src/methods/getGlobalStatistics.ts b/src/methods/getGlobalStatistics.ts new file mode 100644 index 0000000..62f8cc9 --- /dev/null +++ b/src/methods/getGlobalStatistics.ts @@ -0,0 +1,14 @@ +import { GlobalStatisticsMetadata } from "hive-bedrock-data"; +import { APIResponse, Options } from "../types/types"; +import fetchEndpoint from "../helpers/fetchEndpoint"; + +export default function getGlobalStatistics( + options?: Options +): Promise>; + +export default async function getGlobalStatistics( + options?: Options +): Promise> { + let response = await fetchEndpoint(`/global/statistics`, options?.init); + return response; +} diff --git a/src/methods/getMaps.ts b/src/methods/getMaps.ts new file mode 100644 index 0000000..58f3f8d --- /dev/null +++ b/src/methods/getMaps.ts @@ -0,0 +1,33 @@ +import { Game, MapMetadata } from "hive-bedrock-data"; +import { APIResponse, Options } from "../types/types"; +import fetchEndpoint from "../helpers/fetchEndpoint"; +import isGame from "../helpers/isGame"; + +export default function getMaps( + game_id: G, + options?: Options +): Promise>; + +export default async function getMaps( + game_id: G, + options?: Options +): Promise> { + if (!isGame(game_id)) + return { + status: 404, + error: { + code: 404, + message: "Game not found.", + }, + data: null, + }; + + let response = await fetchEndpoint(`/game/map/${game_id}`, options?.init); + if (response.error) return response; + + let data = response.data as MapMetadata[]; + return { + ...response, + data, + }; +} diff --git a/src/methods/getMetadata.ts b/src/methods/getMetadata.ts new file mode 100644 index 0000000..0d72795 --- /dev/null +++ b/src/methods/getMetadata.ts @@ -0,0 +1,33 @@ +import { Game, GameMetadata } from "hive-bedrock-data"; +import { APIResponse, Options } from "../types/types"; +import fetchEndpoint from "../helpers/fetchEndpoint"; +import isGame from "../helpers/isGame"; + +export default function getMetdata( + game_id: G, + options?: Options +): Promise>; + +export default async function getMetdata( + game_id: G, + options?: Options +): Promise> { + if (!isGame(game_id)) + return { + status: 404, + error: { + code: 404, + message: "Game not found.", + }, + data: null, + }; + + let response = await fetchEndpoint(`/game/meta/${game_id}`, options?.init); + if (response.error) return response; + + let data = response.data as GameMetadata; + return { + ...response, + data, + }; +} diff --git a/src/methods/getMonthlyStatistics.ts b/src/methods/getMonthlyStatistics.ts index 05ebc14..c7329a9 100644 --- a/src/methods/getMonthlyStatistics.ts +++ b/src/methods/getMonthlyStatistics.ts @@ -71,10 +71,8 @@ export default async function getMonthlyStatistics( continue; } - processors.statistics.all[g as Game].forEach((processor) => - processor( - stats as StatisticsResponse - ) + processors.statistics[Timeframe.Monthly][g as Game].forEach( + (processor) => processor(stats as any) ); output[g as Game] = stats; } @@ -99,8 +97,8 @@ export default async function getMonthlyStatistics( error: { code: 404, message: "Not Found" }, }; - processors.statistics.all[game_id].forEach((processor) => - processor(response_data as StatisticsResponse) + processors.statistics[Timeframe.Monthly][game_id].forEach((processor) => + processor(response_data as any) ); return { diff --git a/src/processors/game.ts b/src/processors/game.ts new file mode 100644 index 0000000..7dc1b37 --- /dev/null +++ b/src/processors/game.ts @@ -0,0 +1,6 @@ +import { Game, Timeframe } from "hive-bedrock-data"; +import { StatisticsResponse } from "../types/output"; + +export default function game(game_id: Game) { + return (stats: StatisticsResponse) => (stats.id = game_id); +} diff --git a/src/processors/index.ts b/src/processors/index.ts index 532466f..65a8d3a 100644 --- a/src/processors/index.ts +++ b/src/processors/index.ts @@ -2,70 +2,309 @@ import { Game, Leaderboards, Timeframe } from "hive-bedrock-data"; import commonProcessors from "./common"; import leaderboardModifier from "./leaderboard"; import { GameProcessors } from "../types/output"; +import game from "./game"; +import levelProcessors from "./level"; +import pvpProcessors from "./pvp"; const processors = { statistics: { [Timeframe.AllTime]: { - [Game.BlockDrop]: [...commonProcessors], - [Game.BlockParty]: [...commonProcessors], - [Game.CaptureTheFlag]: [...commonProcessors], - [Game.DeathRun]: [...commonProcessors], - [Game.Gravity]: [...commonProcessors], - [Game.GroundWars]: [...commonProcessors], - [Game.HideAndSeek]: [...commonProcessors], - [Game.JustBuild]: [...commonProcessors], - [Game.MurderMystery]: [...commonProcessors], - [Game.Skywars]: [...commonProcessors], - [Game.SurvivalGames]: [...commonProcessors], - [Game.TheBridge]: [...commonProcessors], - [Game.TreasureWars]: [...commonProcessors], + [Game.BlockDrop]: [ + game(Game.BlockDrop), + ...levelProcessors, + ...commonProcessors, + ], + [Game.BlockParty]: [ + game(Game.BlockParty), + ...levelProcessors, + ...commonProcessors, + ], + [Game.CaptureTheFlag]: [ + game(Game.CaptureTheFlag), + ...levelProcessors, + ...pvpProcessors, + ...commonProcessors, + ], + [Game.DeathRun]: [ + game(Game.DeathRun), + ...levelProcessors, + ...commonProcessors, + ], + [Game.Gravity]: [ + game(Game.Gravity), + ...levelProcessors, + ...commonProcessors, + ], + [Game.GroundWars]: [ + game(Game.GroundWars), + ...levelProcessors, + ...pvpProcessors, + ...commonProcessors, + ], + [Game.HideAndSeek]: [ + game(Game.HideAndSeek), + ...levelProcessors, + ...pvpProcessors, + ...commonProcessors, + ], + [Game.JustBuild]: [ + game(Game.JustBuild), + ...levelProcessors, + ...commonProcessors, + ], + [Game.MurderMystery]: [ + game(Game.MurderMystery), + ...levelProcessors, + ...pvpProcessors, + ...commonProcessors, + ], + [Game.Skywars]: [ + game(Game.Skywars), + ...levelProcessors, + ...pvpProcessors, + ...commonProcessors, + ], + [Game.SurvivalGames]: [ + game(Game.SurvivalGames), + ...levelProcessors, + ...pvpProcessors, + ...commonProcessors, + ], + [Game.TheBridge]: [ + game(Game.TheBridge), + ...levelProcessors, + ...pvpProcessors, + ...commonProcessors, + ], + [Game.TreasureWars]: [ + game(Game.TreasureWars), + ...levelProcessors, + ...pvpProcessors, + ...commonProcessors, + ], }, [Timeframe.Monthly]: { - [Game.BlockDrop]: [...commonProcessors], - [Game.BlockParty]: [...commonProcessors], - [Game.CaptureTheFlag]: [...commonProcessors], - [Game.DeathRun]: [...commonProcessors], - [Game.Gravity]: [...commonProcessors], - [Game.GroundWars]: [...commonProcessors], - [Game.HideAndSeek]: [...commonProcessors], - [Game.JustBuild]: [...commonProcessors], - [Game.MurderMystery]: [...commonProcessors], - [Game.Skywars]: [...commonProcessors], - [Game.SurvivalGames]: [...commonProcessors], - [Game.TheBridge]: [...commonProcessors], - [Game.TreasureWars]: [...commonProcessors], + [Game.BlockDrop]: [game(Game.BlockDrop), ...commonProcessors], + [Game.BlockParty]: [game(Game.BlockParty), ...commonProcessors], + [Game.CaptureTheFlag]: [ + game(Game.CaptureTheFlag), + ...pvpProcessors, + ...commonProcessors, + ], + [Game.DeathRun]: [game(Game.DeathRun), ...commonProcessors], + [Game.Gravity]: [game(Game.Gravity), ...commonProcessors], + [Game.GroundWars]: [ + game(Game.GroundWars), + ...pvpProcessors, + ...commonProcessors, + ], + [Game.HideAndSeek]: [ + game(Game.HideAndSeek), + ...pvpProcessors, + ...commonProcessors, + ], + [Game.JustBuild]: [game(Game.JustBuild), ...commonProcessors], + [Game.MurderMystery]: [ + game(Game.MurderMystery), + ...pvpProcessors, + ...commonProcessors, + ], + [Game.Skywars]: [ + game(Game.Skywars), + ...pvpProcessors, + ...commonProcessors, + ], + [Game.SurvivalGames]: [ + game(Game.SurvivalGames), + ...pvpProcessors, + ...commonProcessors, + ], + [Game.TheBridge]: [ + game(Game.TheBridge), + ...pvpProcessors, + ...commonProcessors, + ], + [Game.TreasureWars]: [ + game(Game.TreasureWars), + ...pvpProcessors, + ...commonProcessors, + ], }, }, leaderboard: { [Timeframe.AllTime]: { - [Game.BlockDrop]: [leaderboardModifier(...commonProcessors)], - [Game.BlockParty]: [leaderboardModifier(...commonProcessors)], - [Game.CaptureTheFlag]: [leaderboardModifier(...commonProcessors)], - [Game.DeathRun]: [leaderboardModifier(...commonProcessors)], - [Game.Gravity]: [leaderboardModifier(...commonProcessors)], - [Game.GroundWars]: [leaderboardModifier(...commonProcessors)], - [Game.HideAndSeek]: [leaderboardModifier(...commonProcessors)], - [Game.JustBuild]: [leaderboardModifier(...commonProcessors)], - [Game.MurderMystery]: [leaderboardModifier(...commonProcessors)], - [Game.Skywars]: [leaderboardModifier(...commonProcessors)], - [Game.SurvivalGames]: [leaderboardModifier(...commonProcessors)], - [Game.TheBridge]: [leaderboardModifier(...commonProcessors)], - [Game.TreasureWars]: [leaderboardModifier(...commonProcessors)], + [Game.BlockDrop]: [ + leaderboardModifier( + game(Game.BlockDrop), + ...levelProcessors, + ...commonProcessors + ), + ], + [Game.BlockParty]: [ + leaderboardModifier( + game(Game.BlockParty), + ...levelProcessors, + ...commonProcessors + ), + ], + [Game.CaptureTheFlag]: [ + leaderboardModifier( + game(Game.CaptureTheFlag), + ...levelProcessors, + ...pvpProcessors, + ...commonProcessors + ), + ], + [Game.DeathRun]: [ + leaderboardModifier( + game(Game.DeathRun), + ...levelProcessors, + ...commonProcessors + ), + ], + [Game.Gravity]: [ + leaderboardModifier( + game(Game.Gravity), + ...levelProcessors, + ...commonProcessors + ), + ], + [Game.GroundWars]: [ + leaderboardModifier( + game(Game.GroundWars), + ...levelProcessors, + ...pvpProcessors, + ...commonProcessors + ), + ], + [Game.HideAndSeek]: [ + leaderboardModifier( + game(Game.HideAndSeek), + ...levelProcessors, + ...pvpProcessors, + ...commonProcessors + ), + ], + [Game.JustBuild]: [ + leaderboardModifier( + game(Game.JustBuild), + ...levelProcessors, + ...commonProcessors + ), + ], + [Game.MurderMystery]: [ + leaderboardModifier( + game(Game.MurderMystery), + ...levelProcessors, + ...pvpProcessors, + ...commonProcessors + ), + ], + [Game.Skywars]: [ + leaderboardModifier( + game(Game.Skywars), + ...levelProcessors, + ...pvpProcessors, + ...commonProcessors + ), + ], + [Game.SurvivalGames]: [ + leaderboardModifier( + game(Game.SurvivalGames), + ...levelProcessors, + ...pvpProcessors, + ...commonProcessors + ), + ], + [Game.TheBridge]: [ + leaderboardModifier( + game(Game.TheBridge), + ...levelProcessors, + ...pvpProcessors, + ...commonProcessors + ), + ], + [Game.TreasureWars]: [ + leaderboardModifier( + game(Game.TreasureWars), + ...levelProcessors, + ...pvpProcessors, + ...commonProcessors + ), + ], } as GameProcessors, [Timeframe.Monthly]: { - [Game.BlockDrop]: [leaderboardModifier(...commonProcessors)], - [Game.BlockParty]: [leaderboardModifier(...commonProcessors)], - [Game.CaptureTheFlag]: [leaderboardModifier(...commonProcessors)], - [Game.DeathRun]: [leaderboardModifier(...commonProcessors)], - [Game.Gravity]: [leaderboardModifier(...commonProcessors)], - [Game.GroundWars]: [leaderboardModifier(...commonProcessors)], - [Game.HideAndSeek]: [leaderboardModifier(...commonProcessors)], - [Game.JustBuild]: [leaderboardModifier(...commonProcessors)], - [Game.MurderMystery]: [leaderboardModifier(...commonProcessors)], - [Game.Skywars]: [leaderboardModifier(...commonProcessors)], - [Game.SurvivalGames]: [leaderboardModifier(...commonProcessors)], - [Game.TheBridge]: [leaderboardModifier(...commonProcessors)], - [Game.TreasureWars]: [leaderboardModifier(...commonProcessors)], + [Game.BlockDrop]: [ + leaderboardModifier(game(Game.BlockDrop), ...commonProcessors), + ], + [Game.BlockParty]: [ + leaderboardModifier(game(Game.BlockParty), ...commonProcessors), + ], + [Game.CaptureTheFlag]: [ + leaderboardModifier( + game(Game.CaptureTheFlag), + ...pvpProcessors, + ...commonProcessors + ), + ], + [Game.DeathRun]: [ + leaderboardModifier(game(Game.DeathRun), ...commonProcessors), + ], + [Game.Gravity]: [ + leaderboardModifier(game(Game.Gravity), ...commonProcessors), + ], + [Game.GroundWars]: [ + leaderboardModifier( + game(Game.GroundWars), + ...pvpProcessors, + ...commonProcessors + ), + ], + [Game.HideAndSeek]: [ + leaderboardModifier( + game(Game.HideAndSeek), + ...pvpProcessors, + ...commonProcessors + ), + ], + [Game.JustBuild]: [ + leaderboardModifier(game(Game.JustBuild), ...commonProcessors), + ], + [Game.MurderMystery]: [ + leaderboardModifier( + game(Game.MurderMystery), + ...pvpProcessors, + ...commonProcessors + ), + ], + [Game.Skywars]: [ + leaderboardModifier( + game(Game.Skywars), + ...pvpProcessors, + ...commonProcessors + ), + ], + [Game.SurvivalGames]: [ + leaderboardModifier( + game(Game.SurvivalGames), + ...pvpProcessors, + ...commonProcessors + ), + ], + [Game.TheBridge]: [ + leaderboardModifier( + game(Game.TheBridge), + ...pvpProcessors, + ...commonProcessors + ), + ], + [Game.TreasureWars]: [ + leaderboardModifier( + game(Game.TreasureWars), + ...pvpProcessors, + ...commonProcessors + ), + ], } as GameProcessors, }, }; diff --git a/src/processors/level.ts b/src/processors/level.ts new file mode 100644 index 0000000..7cce50d --- /dev/null +++ b/src/processors/level.ts @@ -0,0 +1,12 @@ +import { Game, Timeframe, calculateLevelFromXP } from "hive-bedrock-data"; +import { StatisticsResponse } from "../types/output"; + +const levelProcessors: (( + stats: StatisticsResponse +) => void)[] = [ + (stats) => { + if (stats.xp) + stats.level = calculateLevelFromXP(stats.xp, stats.id) ?? 0; + }, +]; +export default levelProcessors; diff --git a/src/processors/pvp.ts b/src/processors/pvp.ts new file mode 100644 index 0000000..f4e93dd --- /dev/null +++ b/src/processors/pvp.ts @@ -0,0 +1,27 @@ +const pvpProcessors: ((stats: { + kdr: number; + kills?: number; + deaths?: number; + hider_kills?: number; + murders?: number; +}) => void)[] = [ + (stats) => { + if ("kills" in stats && "deaths" in stats) + stats.kdr = parseFloat( + ((stats.kills ?? 0) / (stats.deaths ?? 0)).toFixed(2) + ); + }, + (stats) => { + if ("hider_kills" in stats && "deaths" in stats) + stats.kdr = parseFloat( + ((stats.hider_kills ?? 0) / (stats.deaths ?? 0)).toFixed(2) + ); + }, + (stats) => { + if ("murders" in stats && "deaths" in stats) + stats.kdr = parseFloat( + ((stats.murders ?? 0) / (stats.deaths ?? 0)).toFixed(2) + ); + }, +]; +export default pvpProcessors; diff --git a/src/types/output.ts b/src/types/output.ts index c34174f..1680c11 100644 --- a/src/types/output.ts +++ b/src/types/output.ts @@ -11,14 +11,24 @@ export type GameProcessors = { [key in Game]: Processor[]; }; +interface AllTimeAdditions { + level: number; +} +interface PvPAdditions { + kdr: number; +} + interface CommonStatistics { + id: Game; played: number; victories: number; losses: number; win_percentage: number; } + type BaseStatistics = Statistics & - CommonStatistics; + CommonStatistics & + (T extends Timeframe.AllTime ? AllTimeAdditions : unknown); export type StatisticsResponse< G extends Game, T extends Timeframe @@ -26,27 +36,30 @@ export type StatisticsResponse< export interface AllStatistics { [Game.BlockDrop]: BaseStatistics; [Game.BlockParty]: BaseStatistics; - [Game.CaptureTheFlag]: BaseStatistics; + [Game.CaptureTheFlag]: BaseStatistics & + PvPAdditions; [Game.DeathRun]: BaseStatistics; [Game.Gravity]: BaseStatistics; - [Game.GroundWars]: BaseStatistics; + [Game.GroundWars]: BaseStatistics & PvPAdditions; [Game.HideAndSeek]: BaseStatistics; [Game.JustBuild]: BaseStatistics; [Game.MurderMystery]: BaseStatistics; - [Game.Skywars]: BaseStatistics; - [Game.SurvivalGames]: BaseStatistics; - [Game.TheBridge]: BaseStatistics; - [Game.TreasureWars]: BaseStatistics; + [Game.Skywars]: BaseStatistics & PvPAdditions; + [Game.SurvivalGames]: BaseStatistics & PvPAdditions; + [Game.TheBridge]: BaseStatistics & PvPAdditions; + [Game.TreasureWars]: BaseStatistics & PvPAdditions; } interface CommonLeaderboard { + id: Game; played: number; victories: number; losses: number; win_percentage: number; } type BaseLeaderboard = Leaderboards & - CommonLeaderboard; + CommonLeaderboard & + (T extends Timeframe.AllTime ? AllTimeAdditions : unknown); export type LeaderboardResponse< G extends Game, T extends Timeframe From d53d2fd33de942aa0e18d291e833c3d23b606431 Mon Sep 17 00:00:00 2001 From: David Fiddes <66165184+UCDFiddes@users.noreply.github.com> Date: Tue, 13 Feb 2024 19:54:40 +0100 Subject: [PATCH 03/13] Update processors --- src/helpers/toPoint.ts | 3 + src/index.ts | 2 + src/methods/getAllTimeLeaderboard.ts | 23 +- src/methods/getAllTimeStatistics.ts | 18 +- src/methods/getMonthlyLeaderboard.ts | 15 +- src/methods/getMonthlyStatistics.ts | 11 +- src/methods/getPlayerInfomation.ts | 24 ++ src/processors/common.ts | 13 - src/processors/game.ts | 6 - src/processors/index.ts | 386 ++++++--------------------- src/processors/leaderboard.ts | 14 - src/processors/level.ts | 12 - src/processors/pvp.ts | 27 -- 13 files changed, 135 insertions(+), 419 deletions(-) create mode 100644 src/helpers/toPoint.ts create mode 100644 src/methods/getPlayerInfomation.ts delete mode 100644 src/processors/common.ts delete mode 100644 src/processors/game.ts delete mode 100644 src/processors/leaderboard.ts delete mode 100644 src/processors/level.ts delete mode 100644 src/processors/pvp.ts diff --git a/src/helpers/toPoint.ts b/src/helpers/toPoint.ts new file mode 100644 index 0000000..ed5b757 --- /dev/null +++ b/src/helpers/toPoint.ts @@ -0,0 +1,3 @@ +export default function toPoint(num: number, point: number): number { + return (Math.round((num * 10) ^ point) / 10) ^ point; +} diff --git a/src/index.ts b/src/index.ts index 9e581f2..a0c53e3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,6 +5,7 @@ import getMaps from "./methods/getMaps"; import getMetdata from "./methods/getMetadata"; import getMonthlyLeaderboard from "./methods/getMonthlyLeaderboard"; import getMonthlyStatistics from "./methods/getMonthlyStatistics"; +import getPlayerInfomation from "./methods/getPlayerInfomation"; export const API_SERVER = "https://api.playhive.com/v0"; export { @@ -15,4 +16,5 @@ export { getGlobalStatistics, getMaps, getMetdata, + getPlayerInfomation, }; diff --git a/src/methods/getAllTimeLeaderboard.ts b/src/methods/getAllTimeLeaderboard.ts index 6550838..8e36b2c 100644 --- a/src/methods/getAllTimeLeaderboard.ts +++ b/src/methods/getAllTimeLeaderboard.ts @@ -1,9 +1,9 @@ -import { Game, Timeframe } from "hive-bedrock-data"; +import { Game, Leaderboards, Timeframe } from "hive-bedrock-data"; import { APIResponse, Options } from "../types/types"; import fetchEndpoint from "../helpers/fetchEndpoint"; import isGame from "../helpers/isGame"; import { LeaderboardResponse } from "../types/output"; -import processors from "../processors"; +import getProcessors from "../processors"; export default function getAllTimeLeaderboard( game_id: G, @@ -26,19 +26,22 @@ export default async function getAllTimeLeaderboard( let response = await fetchEndpoint(`/game/all/${game_id}`, options?.init); if (response.error) return response; - - processors.leaderboard[Timeframe.AllTime][game_id].forEach((processor) => - processor(response.data) - ); - - let data = response.data as unknown as LeaderboardResponse< + let response_data = response.data as unknown as Leaderboards< G, Timeframe.AllTime - >; + >[]; + + let processors = getProcessors(game_id, Timeframe.AllTime); + response_data.forEach((statistics) => + processors.forEach((processor) => processor(statistics)) + ); return { ...response, - data, + data: response_data as unknown as LeaderboardResponse< + G, + Timeframe.AllTime + >, error: null, }; } diff --git a/src/methods/getAllTimeStatistics.ts b/src/methods/getAllTimeStatistics.ts index afb15be..54e7969 100644 --- a/src/methods/getAllTimeStatistics.ts +++ b/src/methods/getAllTimeStatistics.ts @@ -1,13 +1,9 @@ import { Game, PlayerMetadata, Statistics, Timeframe } from "hive-bedrock-data"; import { APIResponse, Options } from "../types/types"; -import { - AllGameStatistics, - AllStatistics, - StatisticsResponse, -} from "../types/output"; +import { AllGameStatistics, StatisticsResponse } from "../types/output"; import isGame from "../helpers/isGame"; import fetchEndpoint from "../helpers/fetchEndpoint"; -import processors from "../processors"; +import getProcessors from "../processors"; type AllGameStatisticsPlayer = AllGameStatistics & { player: PlayerMetadata; @@ -64,9 +60,8 @@ export default async function getAllTimeStatistics( continue; } - processors.statistics.all[g as Game].forEach((processor) => - processor(stats as any) - ); + let processors = getProcessors(g as Game, Timeframe.AllTime); + processors.forEach((processor) => processor(stats)); output[g as Game] = stats; } } @@ -93,9 +88,8 @@ export default async function getAllTimeStatistics( error: { code: 404, message: "Not Found" }, }; - processors.statistics.all[game_id].forEach((processor) => - processor(response_data as any) - ); + let processors = getProcessors(game_id, Timeframe.AllTime); + processors.forEach((processor) => processor(response_data)); return { ...response, diff --git a/src/methods/getMonthlyLeaderboard.ts b/src/methods/getMonthlyLeaderboard.ts index 4845e00..7475702 100644 --- a/src/methods/getMonthlyLeaderboard.ts +++ b/src/methods/getMonthlyLeaderboard.ts @@ -4,6 +4,7 @@ import fetchEndpoint from "../helpers/fetchEndpoint"; import isGame from "../helpers/isGame"; import processors from "../processors"; import { LeaderboardResponse } from "../types/output"; +import getProcessors from "../processors"; interface MonthlyOptions extends Options { month: number; @@ -39,20 +40,16 @@ export default async function getMonthlyLeaderboard( let response = await fetchEndpoint(endpoint, options?.init); if (response.error) return response; - response.data = Object.values(response.data) as Routes; + let response_data = Object.values(response.data); - processors.leaderboard[Timeframe.Monthly][game_id].forEach((processor) => - processor(response.data) + let processors = getProcessors(game_id, Timeframe.Monthly); + response_data.forEach((statistics) => + processors.forEach((processor) => processor(statistics)) ); - let data = response.data as unknown as LeaderboardResponse< - G, - Timeframe.Monthly - >; - return { ...response, - data, + data: response_data, error: null, }; } diff --git a/src/methods/getMonthlyStatistics.ts b/src/methods/getMonthlyStatistics.ts index c7329a9..d1266ec 100644 --- a/src/methods/getMonthlyStatistics.ts +++ b/src/methods/getMonthlyStatistics.ts @@ -8,6 +8,7 @@ import { import isGame from "../helpers/isGame"; import fetchEndpoint from "../helpers/fetchEndpoint"; import processors from "../processors"; +import getProcessors from "../processors"; interface MonthlyOptions extends Options { month: number; @@ -71,9 +72,8 @@ export default async function getMonthlyStatistics( continue; } - processors.statistics[Timeframe.Monthly][g as Game].forEach( - (processor) => processor(stats as any) - ); + const processors = getProcessors(g as Game, Timeframe.Monthly); + processors.forEach((processor) => processor(stats)); output[g as Game] = stats; } } @@ -97,9 +97,8 @@ export default async function getMonthlyStatistics( error: { code: 404, message: "Not Found" }, }; - processors.statistics[Timeframe.Monthly][game_id].forEach((processor) => - processor(response_data as any) - ); + let processors = getProcessors(game_id, Timeframe.Monthly); + processors.forEach((processor) => processor(response_data)); return { ...response, diff --git a/src/methods/getPlayerInfomation.ts b/src/methods/getPlayerInfomation.ts new file mode 100644 index 0000000..f492c5b --- /dev/null +++ b/src/methods/getPlayerInfomation.ts @@ -0,0 +1,24 @@ +import { MapMetadata, PlayerMetadata } from "hive-bedrock-data"; +import { APIResponse, Options } from "../types/types"; +import fetchEndpoint from "../helpers/fetchEndpoint"; + +export default function getPlayerInfomation( + identifier: string, + options?: Options +): Promise>; + +export default async function getPlayerInfomation( + identifier: string, + options?: Options +): Promise> { + let response = await fetchEndpoint( + `/game/all/main/${identifier}`, + options?.init + ); + if (response.error) return response; + + return { + ...response, + data: response.data.main, + }; +} diff --git a/src/processors/common.ts b/src/processors/common.ts deleted file mode 100644 index b984394..0000000 --- a/src/processors/common.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Game, Timeframe } from "hive-bedrock-data"; -import { StatisticsResponse } from "../types/output"; - -const commonProcessors: (( - stats: StatisticsResponse -) => void)[] = [ - (stats) => (stats.victories = stats.victories ?? 0), - (stats) => (stats.losses = stats.played - stats.victories), - (stats) => - (stats.win_percentage = - stats.played > 0 ? stats.victories / stats.played : 0), -]; -export default commonProcessors; diff --git a/src/processors/game.ts b/src/processors/game.ts deleted file mode 100644 index 7dc1b37..0000000 --- a/src/processors/game.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { Game, Timeframe } from "hive-bedrock-data"; -import { StatisticsResponse } from "../types/output"; - -export default function game(game_id: Game) { - return (stats: StatisticsResponse) => (stats.id = game_id); -} diff --git a/src/processors/index.ts b/src/processors/index.ts index 65a8d3a..6e8ca3f 100644 --- a/src/processors/index.ts +++ b/src/processors/index.ts @@ -1,311 +1,77 @@ -import { Game, Leaderboards, Timeframe } from "hive-bedrock-data"; -import commonProcessors from "./common"; -import leaderboardModifier from "./leaderboard"; -import { GameProcessors } from "../types/output"; -import game from "./game"; -import levelProcessors from "./level"; -import pvpProcessors from "./pvp"; +import { Game, Timeframe, calculateLevelFromXP } from "hive-bedrock-data"; +import toPoint from "../helpers/toPoint"; -const processors = { - statistics: { - [Timeframe.AllTime]: { - [Game.BlockDrop]: [ - game(Game.BlockDrop), - ...levelProcessors, - ...commonProcessors, - ], - [Game.BlockParty]: [ - game(Game.BlockParty), - ...levelProcessors, - ...commonProcessors, - ], - [Game.CaptureTheFlag]: [ - game(Game.CaptureTheFlag), - ...levelProcessors, - ...pvpProcessors, - ...commonProcessors, - ], - [Game.DeathRun]: [ - game(Game.DeathRun), - ...levelProcessors, - ...commonProcessors, - ], - [Game.Gravity]: [ - game(Game.Gravity), - ...levelProcessors, - ...commonProcessors, - ], - [Game.GroundWars]: [ - game(Game.GroundWars), - ...levelProcessors, - ...pvpProcessors, - ...commonProcessors, - ], - [Game.HideAndSeek]: [ - game(Game.HideAndSeek), - ...levelProcessors, - ...pvpProcessors, - ...commonProcessors, - ], - [Game.JustBuild]: [ - game(Game.JustBuild), - ...levelProcessors, - ...commonProcessors, - ], - [Game.MurderMystery]: [ - game(Game.MurderMystery), - ...levelProcessors, - ...pvpProcessors, - ...commonProcessors, - ], - [Game.Skywars]: [ - game(Game.Skywars), - ...levelProcessors, - ...pvpProcessors, - ...commonProcessors, - ], - [Game.SurvivalGames]: [ - game(Game.SurvivalGames), - ...levelProcessors, - ...pvpProcessors, - ...commonProcessors, - ], - [Game.TheBridge]: [ - game(Game.TheBridge), - ...levelProcessors, - ...pvpProcessors, - ...commonProcessors, - ], - [Game.TreasureWars]: [ - game(Game.TreasureWars), - ...levelProcessors, - ...pvpProcessors, - ...commonProcessors, - ], - }, - [Timeframe.Monthly]: { - [Game.BlockDrop]: [game(Game.BlockDrop), ...commonProcessors], - [Game.BlockParty]: [game(Game.BlockParty), ...commonProcessors], - [Game.CaptureTheFlag]: [ - game(Game.CaptureTheFlag), - ...pvpProcessors, - ...commonProcessors, - ], - [Game.DeathRun]: [game(Game.DeathRun), ...commonProcessors], - [Game.Gravity]: [game(Game.Gravity), ...commonProcessors], - [Game.GroundWars]: [ - game(Game.GroundWars), - ...pvpProcessors, - ...commonProcessors, - ], - [Game.HideAndSeek]: [ - game(Game.HideAndSeek), - ...pvpProcessors, - ...commonProcessors, - ], - [Game.JustBuild]: [game(Game.JustBuild), ...commonProcessors], - [Game.MurderMystery]: [ - game(Game.MurderMystery), - ...pvpProcessors, - ...commonProcessors, - ], - [Game.Skywars]: [ - game(Game.Skywars), - ...pvpProcessors, - ...commonProcessors, - ], - [Game.SurvivalGames]: [ - game(Game.SurvivalGames), - ...pvpProcessors, - ...commonProcessors, - ], - [Game.TheBridge]: [ - game(Game.TheBridge), - ...pvpProcessors, - ...commonProcessors, - ], - [Game.TreasureWars]: [ - game(Game.TreasureWars), - ...pvpProcessors, - ...commonProcessors, - ], - }, - }, - leaderboard: { - [Timeframe.AllTime]: { - [Game.BlockDrop]: [ - leaderboardModifier( - game(Game.BlockDrop), - ...levelProcessors, - ...commonProcessors - ), - ], - [Game.BlockParty]: [ - leaderboardModifier( - game(Game.BlockParty), - ...levelProcessors, - ...commonProcessors - ), - ], - [Game.CaptureTheFlag]: [ - leaderboardModifier( - game(Game.CaptureTheFlag), - ...levelProcessors, - ...pvpProcessors, - ...commonProcessors - ), - ], - [Game.DeathRun]: [ - leaderboardModifier( - game(Game.DeathRun), - ...levelProcessors, - ...commonProcessors - ), - ], - [Game.Gravity]: [ - leaderboardModifier( - game(Game.Gravity), - ...levelProcessors, - ...commonProcessors - ), - ], - [Game.GroundWars]: [ - leaderboardModifier( - game(Game.GroundWars), - ...levelProcessors, - ...pvpProcessors, - ...commonProcessors - ), - ], - [Game.HideAndSeek]: [ - leaderboardModifier( - game(Game.HideAndSeek), - ...levelProcessors, - ...pvpProcessors, - ...commonProcessors - ), - ], - [Game.JustBuild]: [ - leaderboardModifier( - game(Game.JustBuild), - ...levelProcessors, - ...commonProcessors - ), - ], - [Game.MurderMystery]: [ - leaderboardModifier( - game(Game.MurderMystery), - ...levelProcessors, - ...pvpProcessors, - ...commonProcessors - ), - ], - [Game.Skywars]: [ - leaderboardModifier( - game(Game.Skywars), - ...levelProcessors, - ...pvpProcessors, - ...commonProcessors - ), - ], - [Game.SurvivalGames]: [ - leaderboardModifier( - game(Game.SurvivalGames), - ...levelProcessors, - ...pvpProcessors, - ...commonProcessors - ), - ], - [Game.TheBridge]: [ - leaderboardModifier( - game(Game.TheBridge), - ...levelProcessors, - ...pvpProcessors, - ...commonProcessors - ), - ], - [Game.TreasureWars]: [ - leaderboardModifier( - game(Game.TreasureWars), - ...levelProcessors, - ...pvpProcessors, - ...commonProcessors - ), - ], - } as GameProcessors, - [Timeframe.Monthly]: { - [Game.BlockDrop]: [ - leaderboardModifier(game(Game.BlockDrop), ...commonProcessors), - ], - [Game.BlockParty]: [ - leaderboardModifier(game(Game.BlockParty), ...commonProcessors), - ], - [Game.CaptureTheFlag]: [ - leaderboardModifier( - game(Game.CaptureTheFlag), - ...pvpProcessors, - ...commonProcessors - ), - ], - [Game.DeathRun]: [ - leaderboardModifier(game(Game.DeathRun), ...commonProcessors), - ], - [Game.Gravity]: [ - leaderboardModifier(game(Game.Gravity), ...commonProcessors), - ], - [Game.GroundWars]: [ - leaderboardModifier( - game(Game.GroundWars), - ...pvpProcessors, - ...commonProcessors - ), - ], - [Game.HideAndSeek]: [ - leaderboardModifier( - game(Game.HideAndSeek), - ...pvpProcessors, - ...commonProcessors - ), - ], - [Game.JustBuild]: [ - leaderboardModifier(game(Game.JustBuild), ...commonProcessors), - ], - [Game.MurderMystery]: [ - leaderboardModifier( - game(Game.MurderMystery), - ...pvpProcessors, - ...commonProcessors - ), - ], - [Game.Skywars]: [ - leaderboardModifier( - game(Game.Skywars), - ...pvpProcessors, - ...commonProcessors - ), - ], - [Game.SurvivalGames]: [ - leaderboardModifier( - game(Game.SurvivalGames), - ...pvpProcessors, - ...commonProcessors - ), - ], - [Game.TheBridge]: [ - leaderboardModifier( - game(Game.TheBridge), - ...pvpProcessors, - ...commonProcessors - ), - ], - [Game.TreasureWars]: [ - leaderboardModifier( - game(Game.TreasureWars), - ...pvpProcessors, - ...commonProcessors - ), - ], - } as GameProcessors, - }, -}; -export default processors; +export default function getProcessors( + game: Game, + timeframe: Timeframe +): ((stats: { [key: string]: any }) => void)[] { + return [ + // Append game id + (s) => (s.id = game), + + // Fix inconsistant bridge keys + (s) => + game === Game.TheBridge && timeframe === Timeframe.Monthly + ? Object.keys(s) + .filter((key) => key.startsWith("m_solo_")) + .forEach((key) => { + s[key.replace(/m_solo_/, "")] = s[key]; + delete s[key]; + }) + : null, + + // Calculate current level + (s) => + "xp" in s && timeframe === Timeframe.AllTime + ? (s.level = calculateLevelFromXP(s.xp, game) ?? 0) + : null, + + // Calculate kill/death ratio + (s) => + "kills" in s && "deaths" in s + ? (s.kdr = toPoint((s.kills ?? 0) / (s.deaths ?? 0), 2)) + : null, + + // Calculate kill/death ratio - hide + (s) => + "hider_kills" in s && "deaths" in s + ? (s.kdr = toPoint((s.hider_kills ?? 0) / (s.deaths ?? 0), 2)) + : null, + + // Calculate kill/death ratio - murder + (s) => + "murders" in s && "deaths" in s + ? (s.kdr = toPoint((s.murders ?? 0) / (s.deaths ?? 0), 2)) + : null, + + // Total ratings for build + (s) => + game === Game.JustBuild + ? Object.keys(s) + .filter((key) => key.startsWith("rating_")) + .forEach( + (key) => + (s.total_ratings = + (s.total_ratings ?? 0) + s[key]) + ) + : null, + + // Append losses + (s) => + "played" in s && "victories" in s + ? (s.losses = s.played - s.victories) + : null, + + // Calculate win percentage + (s) => + "played" in s && "victories" in s + ? (s.win_percentage = s.played > 0 ? s.victories / s.played : 0) + : null, + + // Remove NaN + (s) => + Object.entries(s).forEach(([key, value]) => + isNaN(value) && typeof value === "number" ? delete s[key] : null + ), + ]; +} diff --git a/src/processors/leaderboard.ts b/src/processors/leaderboard.ts deleted file mode 100644 index 75eb927..0000000 --- a/src/processors/leaderboard.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Game, Leaderboards, Timeframe } from "hive-bedrock-data"; - -export default function leaderboardModifier( - ...processors: ((stats: any) => void)[] -) { - return (stats: any[]) => { - return stats.map((record) => { - for (let processor of processors) { - processor(record); - } - return record; - }); - }; -} diff --git a/src/processors/level.ts b/src/processors/level.ts deleted file mode 100644 index 7cce50d..0000000 --- a/src/processors/level.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Game, Timeframe, calculateLevelFromXP } from "hive-bedrock-data"; -import { StatisticsResponse } from "../types/output"; - -const levelProcessors: (( - stats: StatisticsResponse -) => void)[] = [ - (stats) => { - if (stats.xp) - stats.level = calculateLevelFromXP(stats.xp, stats.id) ?? 0; - }, -]; -export default levelProcessors; diff --git a/src/processors/pvp.ts b/src/processors/pvp.ts deleted file mode 100644 index f4e93dd..0000000 --- a/src/processors/pvp.ts +++ /dev/null @@ -1,27 +0,0 @@ -const pvpProcessors: ((stats: { - kdr: number; - kills?: number; - deaths?: number; - hider_kills?: number; - murders?: number; -}) => void)[] = [ - (stats) => { - if ("kills" in stats && "deaths" in stats) - stats.kdr = parseFloat( - ((stats.kills ?? 0) / (stats.deaths ?? 0)).toFixed(2) - ); - }, - (stats) => { - if ("hider_kills" in stats && "deaths" in stats) - stats.kdr = parseFloat( - ((stats.hider_kills ?? 0) / (stats.deaths ?? 0)).toFixed(2) - ); - }, - (stats) => { - if ("murders" in stats && "deaths" in stats) - stats.kdr = parseFloat( - ((stats.murders ?? 0) / (stats.deaths ?? 0)).toFixed(2) - ); - }, -]; -export default pvpProcessors; From 53a87ab0368c2ff74239ba860c1db2dd7ba6bff0 Mon Sep 17 00:00:00 2001 From: David Fiddes <66165184+UCDFiddes@users.noreply.github.com> Date: Tue, 13 Feb 2024 20:02:08 +0100 Subject: [PATCH 04/13] Update README --- README.md | 78 +++++++++++++++++++++++++--------------------------- src/index.ts | 2 ++ 2 files changed, 39 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index 79c3a0a..2f4cc25 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Hive Bedrock API -An API wrapper for the Hive Minecraft Bedrock Edition server. Which allows you to get stats for leaderboards, players, cosmetics and total unique player counts. +An API wrapper for the Hive Minecraft Bedrock Edition server. Which allows you to get stats for leaderboards, players, cosmetics, unique player counts, maps and metadata. ## Getting started @@ -16,78 +16,65 @@ See [API.md](docs/API.md) for detailed documentation. ### Fetch Player Infomation ```ts -import { getPlayerInfo } from "hive-bedrock-api"; +import { getPlayerInformation } from "hive-bedrock-api"; // Returns player, cosmetics, server statistics and profile infomation -const { data, error } = await getPlayerInfo("ucdfiddes"); +const { data, error } = await getPlayerInformation("player"); ``` ### Fetch All-Time Player Statistics ```ts -import { getAllTimeStats, GAME } from "hive-bedrock-api"; +import { getAllTimeStatistics, Game } from "hive-bedrock-api"; // Returns all games -const { data, player, error } = await getAllTimeStats("ucdfiddes"); - -// Returns multiple specific games -const { data, player, error } = await getAllTimeStats("ucdfiddes", [GAME.Deathrun, GAME.TheBridge]); +const { data, error } = await getAllTimeStats("player"); // Returns a single game -const { data, error } = await getAllTimeStats("ucdfiddes", GAME.HideAndSeek); +const { data, error } = await getAllTimeStats("player", Game.HideAndSeek); ``` ### Fetch Monthly Player Statistics ```ts -import { getMonthlyStats, GAME } from "hive-bedrock-api"; +import { getMonthlyStatistics, Game } from "hive-bedrock-api"; // Returns all games -const { data, error } = await getMonthlyStats("ucdfiddes"); - -// Returns multiple specific games -const { data, error } = await getMonthlyStats("ucdfiddes", [GAME.Deathrun, GAME.TheBridge]); +const { data, error } = await getMonthlyStats("player"); // Returns a single game -const { data, error } = await getMonthlyStats("ucdfiddes", GAME.BlockDrop); +const { data, error } = await getMonthlyStats("player", Game.BlockDrop); // Returns a single game in a previous month (can return muliple games) -const { data, error } = await getMonthlyStats("ucdfiddes", GAME.BlockDrop, { - year: 2023, - month: 1, // January -}); - -// Same as before, but with a Date object -const previousMonth = new Date(); -previousMonth.setMonth(0); // January -const { data, error } = await getMonthlyStats("ucdfiddes", GAME.BlockDrop, { - date: previousMonth, +const { data, error } = await getMonthlyStats("player", Game.BlockDrop, { + year: 2023, + month: 1, // January }); ``` ### Fetch All-Time Leaderboard ```ts -import { getAllTimeLeaderboard, GAME } from "hive-bedrock-api"; +import { getAllTimeLeaderboard, Game } from "hive-bedrock-api"; // Returns a single game -const { data, error } = await getAllTimeLeaderboard(GAME.TreasureWars); +const { data, error } = await getAllTimeLeaderboard(Game.TreasureWars); ``` ### Fetch Monthly Leaderboard ```ts -import { getMonthlyLeaderboard, GAME } from "hive-bedrock-api"; +import { getMonthlyLeaderboard, Game } from "hive-bedrock-api"; // Returns a single game -const { data, error } = await getMonthlyLeaderboard(GAME.TreasureWars); +const { data, error } = await getMonthlyLeaderboard(Game.TreasureWars); // Returns a single game from a previous month -const { data, error } = await getMonthlyLeaderboard(GAME.BlockParty, { - year: 2023, - month: 11, // November - amount: 50, - skip: 20, // Sum of skip and amount must be <=100 +const { data, error } = await getMonthlyLeaderboard(Game.BlockParty, { + year: 2023, + month: 11, // November + amount: 50, + skip: 20, // Sum of skip and amount must be <=100 }); ``` @@ -103,20 +90,29 @@ const { data, error } = await getGlobalStatistics(); ### Fetch Maps ```ts -import { getMaps, GAME } from "hive-bedrock-api"; +import { getMaps, Game } from "hive-bedrock-api"; + +// Returns data for a specific game's currently active maps +const { data, error } = await getMaps(Game.TreasureWars); +``` + +### Fetch Metadata + +```ts +import { getMaps, Game } from "hive-bedrock-api"; // Returns data for a specific game's currently active maps -const { data, error } = await getMaps(GAME.TreasureWars); +const { data, error } = await getMetadata(Game.TreasureWars); ``` ## API Response Changes Different API responses are edited by the wrapper to provide more data: -- Game responses have a new value "id" showing the parent game -- "first_played" values are converted into a Date object -- A new value for "losses" is provided -- A new value for "kdr" is provided -- "xp" is converted and a "level" is provided +- Game responses have a new value "id" showing the parent game +- A new value for "losses" is provided +- A new value for "kdr" is provided +- "xp" is converted and a "level" is provided +- "total_ratings" for just build is provided See [API.md](docs/API.md#game-statistics-types) for specific fields and their corresponding types per game. diff --git a/src/index.ts b/src/index.ts index a0c53e3..cda9b31 100644 --- a/src/index.ts +++ b/src/index.ts @@ -18,3 +18,5 @@ export { getMetdata, getPlayerInfomation, }; + +export { Game } from "hive-bedrock-data"; From ea8c12c7a234d5711c8a3b67eb4aeb0f288edb05 Mon Sep 17 00:00:00 2001 From: David Fiddes <66165184+UCDFiddes@users.noreply.github.com> Date: Tue, 13 Feb 2024 20:07:36 +0100 Subject: [PATCH 05/13] fix output processor types --- src/types/output.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/types/output.ts b/src/types/output.ts index 1680c11..ec01cc5 100644 --- a/src/types/output.ts +++ b/src/types/output.ts @@ -42,7 +42,9 @@ export interface AllStatistics { [Game.Gravity]: BaseStatistics; [Game.GroundWars]: BaseStatistics & PvPAdditions; [Game.HideAndSeek]: BaseStatistics; - [Game.JustBuild]: BaseStatistics; + [Game.JustBuild]: BaseStatistics & { + total_ratings: number; + }; [Game.MurderMystery]: BaseStatistics; [Game.Skywars]: BaseStatistics & PvPAdditions; [Game.SurvivalGames]: BaseStatistics & PvPAdditions; @@ -72,7 +74,9 @@ export interface AllLeaderboards { [Game.Gravity]: BaseLeaderboard[]; [Game.GroundWars]: BaseLeaderboard[]; [Game.HideAndSeek]: BaseLeaderboard[]; - [Game.JustBuild]: BaseLeaderboard[]; + [Game.JustBuild]: (BaseLeaderboard & { + total_ratings: number; + })[]; [Game.MurderMystery]: BaseLeaderboard[]; [Game.Skywars]: BaseLeaderboard[]; [Game.SurvivalGames]: BaseLeaderboard[]; From 7a23adb300c5566c9301073a62b3fb9366f163c5 Mon Sep 17 00:00:00 2001 From: David Fiddes <66165184+UCDFiddes@users.noreply.github.com> Date: Tue, 13 Feb 2024 20:53:28 +0100 Subject: [PATCH 06/13] Add documentaion and player processors --- docs/API.md | 299 ++++++++++++++-------------- docs/GAMES.md | 28 +-- package.json | 11 +- src/methods/getAllTimeStatistics.ts | 9 +- src/methods/getPlayerInfomation.ts | 8 +- src/processors/index.ts | 17 ++ 6 files changed, 198 insertions(+), 174 deletions(-) diff --git a/docs/API.md b/docs/API.md index 8fc5ee1..63614d8 100644 --- a/docs/API.md +++ b/docs/API.md @@ -1,147 +1,154 @@ # Documentation -## getMonthlyStats(playerIdentifier[, game, options]) +## getMonthlyStatistics(identifier[, game, options]) Get a player's monthly statistics. -| Parameter | Type | Required | Description | -| --------------------- | --------------------------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| playerIdentifier | `string` | **Required** | The player's gamertag or UUID of which to get stats for | -| game | `GAME \| GAME[]` | _optional_ | The game identifier - see [GAME](API.md#games). When specifying multiple games in an array, will return array of results for each game. When not specified, will return all games | -| options | `object` | _optional_ | Options is an object with the fields below: | -| options.year | `number` | _optional_ | The year from which to get stats | -| options.month | `number` | _optional_ | The month from which to get stats | -| options.date | `Date` | _optional_ | An instance of `Date` from which to get stats (can be used interchangably with `year` + `month`) | -| options.fetch | `object` | _optional_ | Fetch options: | -| options.fetch.headers | `{ [key: string]: string }` | _optional_ | Any custom headers to apply to the fetch method | +| Parameter | Type | Required | Description | +| ------------- | ------------- | ------------ | ----------------------------------------------------------------------------------------- | +| identifier | `string` | **Required** | The player's gamertag or UUID of which to get stats for | +| game | `Game` | _optional_ | The game identifier - see [Game](API.md#games). When not specified, will return all games | +| options | `object` | _optional_ | Options is an object with the fields below: | +| options.year | `number` | _optional_ | The year from which to get stats | +| options.month | `number` | _optional_ | The month from which to get stats | +| options.init | `RequestInit` | _optional_ | Used in the api request to add custom headers and more | Returns a Promise which resolves to the following object: -| Field | Type | Description | -| ----- | ------------------------------ | ----------------- | -| data | [Response](GAMES.md) `\| null` | The response data | -| error | `{ message: string } \| null` | Error data | +| Field | Type | Description | +| -------- | ------------------------------------------- | -------------------------------- | +| data | [Response](GAMES.md) `\| null` | The response data | +| error | `{ code: number, message: string } \| null` | Error data | +| status | `number` | The http status returned | +| duration | `number \| undefined` | The duration of the http request | ### Usage ```ts -import { getMonthlyStats, GAME } from "hive-bedrock-api"; +import { getMonthlyStatsistics, Game } from "hive-bedrock-api"; // Get GAMERTAG's Treasure Wars stats from June 2023 -const { data, error } = await getMonthlyStats("GAMERTAG", GAME.TreasureWars, { - year: 2023, - month: 6, -}); +const { data, error } = await getMonthlyStatsistics( + "GAMERTAG", + Game.TreasureWars, + { + year: 2023, + month: 6, + } +); ``` ## getMonthlyLeaderboard(game[, options]) Get the monthly leaderboard for a specific month. -| Parameter | Type | Required | Description | -| --------------------- | --------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| game | `GAME \| GAME[]` | **Required** | The game identifier - see [GAME](API.md#games). When specifying multiple games in an array, will return array of results for each game | -| options | `object` | _optional_ | Options is an object with the fields below: | -| options.year | `number` | _optional_ | The year from which to get the leaderboard | -| options.month | `number` | _optional_ | The month from which to get the leaderboard | -| options.date | `Date` | _optional_ | An instance of `Date` from which to get the leaderboard (can be used interchangably with `year` + `month`; if both are present, `date` will override `year` and `month`) | -| options.skip | `number` | _optional_ | How many players to skip in the leaderboard | -| options.amount | `number` | _optional_ | How many players to return in the leaderboard | -| options.fetch | `object` | _optional_ | Fetch options: | -| options.fetch.headers | `{ [key: string]: string }` | _optional_ | Any custom headers to apply to the fetch method | +| Parameter | Type | Required | Description | +| -------------- | ------------- | ------------ | ------------------------------------------------------ | +| game | `Game` | **Required** | The game identifier - see [Game](API.md#games). | +| options | `object` | _optional_ | Options is an object with the fields below: | +| options.year | `number` | _optional_ | The year from which to get the leaderboard | +| options.month | `number` | _optional_ | The month from which to get the leaderboard | +| options.skip | `number` | _optional_ | How many players to skip in the leaderboard | +| options.amount | `number` | _optional_ | How many players to return in the leaderboard | +| options.init | `RequestInit` | _optional_ | Used in the api request to add custom headers and more | Returns a Promise which resolves to the following object: -| Field | Type | Description | -| ----- | -------------------------------- | ----------------- | -| data | [Response](GAMES.md)[] `\| null` | The response data | -| error | `{ message: string } \| null` | Error data | +| Field | Type | Description | +| -------- | ------------------------------------------- | -------------------------------- | +| data | [Response](GAMES.md) `\| null` | The response data | +| error | `{ code: number, message: string } \| null` | Error data | +| status | `number` | The http status returned | +| duration | `number \| undefined` | The duration of the http request | ### Usage ```ts -import { getMonthlyLeaderboard, GAME } from "hive-bedrock-api"; +import { getMonthlyLeaderboard, Game } from "hive-bedrock-api"; // Get the top three Skywars players from this month -const now = new Date(); -const { data, error } = await getMonthlyLeaderboard(GAME.SkyWars, { +const { data, error } = await getMonthlyLeaderboard(Game.SkyWars, { amount: 3, - date: now, }); ``` -## getAllTimeStats(playerIdentifier[, game, options]) +## getAllTimeStatistics(identifier[, game, options]) Get a player's all-time statistics. -| Parameter | Type | Required | Description | -| --------------------- | --------------------------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| playerIdentifier | `string` | **Required** | The player's gamertag or UUID of which to get stats for | -| game | `GAME \| GAME[]` | _optional_ | The game identifier - see [GAME](API.md#games). When specifying multiple games in an array, will return array of results for each game. When not specified, will return all games | -| options | `object` | _optional_ | Options is an object with the fields below: | -| options.fetch | `object` | _optional_ | Fetch options: | -| options.fetch.headers | `{ [key: string]: string }` | _optional_ | Any custom headers to apply to the fetch method | +| Parameter | Type | Required | Description | +| ------------ | ------------- | ------------ | ----------------------------------------------------------------------------------------- | +| identifier | `string` | **Required** | The player's gamertag or UUID of which to get stats for | +| game | `Game` | _optional_ | The game identifier - see [GAME](API.md#games). When not specified, will return all games | +| options | `object` | _optional_ | Options is an object with the fields below: | +| options.init | `RequestInit` | _optional_ | Used in the api request to add custom headers and more | Returns a Promise which resolves to the following object: -| Field | Type | Description | -| ------ | ------------------------------- | ---------------------- | -| data | [Response](GAMES.md) `\| null` | The response data | -| error | `{ message: string } \| null` | Error data | -| player | [PlayerInfo](API.md#playerinfo) | Additional player info | +| Field | Type | Description | +| -------- | ------------------------------------------- | --------------------------------------------------------------------- | +| data | [Response](GAMES.md) `\| null` | The response data including player infomation if requesting all games | +| error | `{ code: number, message: string } \| null` | Error data | +| status | `number` | The http status returned | +| duration | `number \| undefined` | The duration of the http request | ### Usage ```ts -import { getAllTimeStats, GAME } from "hive-bedrock-api"; +import { getAllTimeStatistics, Game } from "hive-bedrock-api"; // Get GAMERTAG's all-time Hide and Seek stats -const { data, error } = await getAllTimeStats("GAMERTAG", GAME.HideAndSeek); +const { data, error } = await getAllTimeStatistics( + "GAMERTAG", + Game.HideAndSeek +); ``` ## getAllTimeLeaderboard(game[, options]) Get the all-time leaderboard for a game or games. -| Parameter | Type | Required | Description | -| --------------------- | --------------------------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------- | -| game | `GAME \| GAME[]` | **Required** | The game identifier - see [GAME](API.md#games). When specifying multiple games in an array, will return array of results for each game | -| options | `object` | _optional_ | Options is an object with the fields below: | -| options.fetch | `object` | _optional_ | Fetch options: | -| options.fetch.headers | `{ [key: string]: string }` | _optional_ | Any custom headers to apply to the fetch method | +| Parameter | Type | Required | Description | +| ------------ | ------------- | ------------ | ------------------------------------------------------ | +| game | `Game` | **Required** | The game identifier - see [GAME](API.md#games). | +| options | `object` | _optional_ | Options is an object with the fields below: | +| options.init | `RequestInit` | _optional_ | Used in the api request to add custom headers and more | Returns a Promise which resolves to the following object: -| Field | Type | Description | -| ----- | -------------------------------- | ----------------- | -| data | [Response](GAMES.md)[] `\| null` | The response data | -| error | `{ message: string } \| null` | Error data | +| Field | Type | Description | +| -------- | ------------------------------------------- | -------------------------------- | +| data | [Response](GAMES.md) `\| null` | The response data | +| error | `{ code: number, message: string } \| null` | Error data | +| status | `number` | The http status returned | +| duration | `number \| undefined` | The duration of the http request | ### Usage ```ts -import { getAllTimeLeaderboard, GAME } from "hive-bedrock-api"; +import { getAllTimeLeaderboard, Game } from "hive-bedrock-api"; // Get the all-time leaderboard for Survival Games -const { data, error } = await getAllTimeLeaderboard(GAME.SurvivalGames); +const { data, error } = await getAllTimeLeaderboard(Game.SurvivalGames); ``` ## getGlobalStatistics([options]) Get special data such as each game's all-time player count. -| Parameter | Type | Required | Description | -| --------------------- | --------------------------- | ---------- | ----------------------------------------------- | -| options | `object` | _optional_ | Options is an object with the fields below: | -| options.fetch | `object` | _optional_ | Fetch options: | -| options.fetch.headers | `{ [key: string]: string }` | _optional_ | Any custom headers to apply to the fetch method | +| Parameter | Type | Required | Description | +| ------------ | ------------- | ---------- | ------------------------------------------------------ | +| options | `object` | _optional_ | Options is an object with the fields below: | +| options.init | `RequestInit` | _optional_ | Used in the api request to add custom headers and more | Returns a Promise which resolves to the following object: -| Field | Type | Description | -| ----- | ------------------------------------------------ | ----------------- | -| data | [Response](API.md#global-statistics)[] `\| null` | The response data | -| error | `{ message: string } \| null` | Error data | +| Field | Type | Description | +| -------- | ---------------------------------------------- | -------------------------------- | +| data | [Response](API.md#global-statistics) `\| null` | The response data | +| error | `{ code: number, message: string } \| null` | Error data | +| status | `number` | The http status returned | +| duration | `number \| undefined` | The duration of the http request | ### Usage @@ -157,19 +164,20 @@ const { data, error } = await getGlobalStatistics(); Fetch data about a specific game's currently active maps. If the game has got only one map, returned data will be null and the error message will say so. -| Parameter | Type | Required | Description | -| --------------------- | --------------------------- | ------------ | ----------------------------------------------- | -| game | `GAME` | **Required** | The game identifier - see [GAME](API.md#games) | -| options | `object` | _optional_ | Options is an object with the fields below: | -| options.fetch | `object` | _optional_ | Fetch options: | -| options.fetch.headers | `{ [key: string]: string }` | _optional_ | Any custom headers to apply to the fetch method | +| Parameter | Type | Required | Description | +| ------------ | ------------- | ------------ | ------------------------------------------------------ | +| game | `GAME` | **Required** | The game identifier - see [GAME](API.md#games) | +| options | `object` | _optional_ | Options is an object with the fields below: | +| options.init | `RequestInit` | _optional_ | Used in the api request to add custom headers and more | Returns a Promise which resolves to the following object: -| Field | Type | Description | -| ----- | -------------------------------------- | ----------------- | -| data | [MapData](API.md#map-data)[] `\| null` | The response data | -| error | `{ message: string } \| null` | Error data | +| Field | Type | Description | +| -------- | ------------------------------------------- | -------------------------------- | +| data | [Response](API.md#map-data) `\| null` | The response data | +| error | `{ code: number, message: string } \| null` | Error data | +| status | `number` | The http status returned | +| duration | `number \| undefined` | The duration of the http request | Allowed games (games with only one map don't work): @@ -188,98 +196,101 @@ Allowed games (games with only one map don't work): ### Usage ```ts -import { getMaps, GAME } from "hive-bedrock-api"; +import { getMaps, Game } from "hive-bedrock-api"; // Get all currently active Skywars maps' data. -const { data, error } = await getMaps(GAME.Skywars); +const { data, error } = await getMaps(Game.Skywars); ``` -## getGameMetadata(game[, options]) +## getMetadata(game[, options]) **Currently Ground Wars does not work with this endpoint.** Fetch metadata about a specific game. This includes level and prestige infomation. -| Parameter | Type | Required | Description | -| --------------------- | --------------------------- | ------------ | ----------------------------------------------- | -| game | `GAME` | **Required** | The game identifier - see [GAME](API.md#games) | -| options | `object` | _optional_ | Options is an object with the fields below: | -| options.fetch | `object` | _optional_ | Fetch options: | -| options.fetch.headers | `{ [key: string]: string }` | _optional_ | Any custom headers to apply to the fetch method | +| Parameter | Type | Required | Description | +| ------------ | ------------- | ------------ | ------------------------------------------------------ | +| game | `Game` | **Required** | The game identifier - see [GAME](API.md#games) | +| options | `object` | _optional_ | Options is an object with the fields below: | +| options.init | `RequestInit` | _optional_ | Used in the api request to add custom headers and more | Returns a Promise which resolves to the following object: -| Field | Type | Description | -| ----- | ------------------------------------------------ | ----------------- | -| data | [GameMetadata](API.md#game-metadata)[] `\| null` | The response data | -| error | `{ message: string } \| null` | Error data | +| Field | Type | Description | +| -------- | ------------------------------------------- | -------------------------------- | +| data | [Response](API.md#map-data) `\| null` | The response data | +| error | `{ code: number, message: string } \| null` | Error data | +| status | `number` | The http status returned | +| duration | `number \| undefined` | The duration of the http request | ### Usage ```ts -import { getMaps, GAME } from "hive-bedrock-api"; +import { getMetadata, Game } from "hive-bedrock-api"; // Get metadata about skywars levels and prestige -const { data, error } = await getGameMetadata(GAME.Skywars); +const { data, error } = await getMetadata(Game.Skywars); ``` -## getPlayerInfo(playerIdentifier[, options]) +## getPlayerInformation(identifier[, options]) Get a player's information, such as current/longest login streak, currently equipped and all unlocked cosmetics, and number of quests completed. -| Parameter | Type | Required | Description | -| --------------------- | --------------------------- | ------------ | -------------------------------------------------- | -| playerIdentifier | `string` | **Required** | The player's gamertag or UUID of which to get info | -| options | `object` | _optional_ | Options is an object with the fields below: | -| options.fetch | `object` | _optional_ | Fetch options: | -| options.fetch.headers | `{ [key: string]: string }` | _optional_ | Any custom headers to apply to the fetch method | +| Parameter | Type | Required | Description | +| ------------ | ------------- | ------------ | ------------------------------------------------------ | +| identifier | `string` | **Required** | The player's gamertag or UUID of which to get info | +| options | `object` | _optional_ | Options is an object with the fields below: | +| options.init | `RequestInit` | _optional_ | Used in the api request to add custom headers and more | Returns a Promise which resolves to the following object -| Field | Type | Description | -| ----- | ----------------------------------------- | ----------------- | -| data | [PlayerInfo](API.md#playerinfo) `\| null` | The response data | -| error | `{ message: string } \| null` | Error data | +| Field | Type | Description | +| -------- | ------------------------------------------- | -------------------------------- | +| data | [Response](API.md#playerinfo) `\| null` | The response data | +| error | `{ code: number, message: string } \| null` | Error data | +| status | `number` | The http status returned | +| duration | `number \| undefined` | The duration of the http request | ### Usage ```ts -import { getPlayerInfo } from "hive-bedrock-api"; +import { getPlayerInformation } from "hive-bedrock-api"; // Get GAMERTAG's player info. -const { data, error } = await getPlayerInfo("GAMERTAG"); +const { data, error } = await getPlayerInformation("GAMERTAG"); ``` ## PlayerInfo PlayerInfo object structure -| Field | Type | Description | -| -------------------------- | ---------------- | ------------------------------------------------------- | -| UUID | `string` | The player's UUID | -| xuid | `number` | The player's XUID (Xbox User ID) | -| username | `string` | The player's username | -| username_cc | `string` | The player's correctly capitalised username | -| rank | `RANK` | The player's [rank](API.md#player-ranks) | -| first_played | `Date` | The date when the player first played on The Hive | -| daily_login_streak | `number` | The player's current daily login streak | -| longest_daily_login_streak | `number` | The player's longest daily login streak | -| hub_title_count | `number` | How many hub titles the player currently owns | -| hub_title_unlocked | `string[]` | All of the player's owned hub titles | -| costume_count | `number` | How many costumes the player currently owns | -| costume_unlocked | `string[]` | All of the player's owned costumes' names | -| avatar_count | `number` | How many avatars the player currently owns | -| avatar_unlocked | `AVATAR[]` | All of the player's owned [avatars](API.md#avatar) | -| friend_count | `number` | How many friends the player has got on the server | -| equipped_hub_title | `string \| null` | The player's currently equipped hub title | -| equipped_costume | `string \| null` | The player's currently equipped costume's name | -| equipped_avatar | `AVATAR \| null` | The player's currently equipped [avatar](API.md#avatar) | -| quest_count | `number` | How many quests the player has ever completed | -| paid_ranks | `RANK[]` | All of the player's paid [ranks](API.md#player-ranks) | -| pets | `string[]` | All of the player's owned pets' names | -| mounts | `string[]` | All of the player's owned mounts' names | -| hats | `string[]` | All of the player's owned hats' names | +| Field | Type | Description | +| -------------------------- | ---------------- | ----------------------------------------------------------- | +| UUID | `string` | The player's UUID | +| xuid | `number` | The player's XUID (Xbox User ID) | +| username | `string` | The player's username | +| username_cc | `string` | The player's correctly capitalised username | +| rank | `Rank` | The player's [rank](API.md#player-ranks) | +| first_played | `number` | The unix timestamp when the player first played on The Hive | +| daily_login_streak | `number` | The player's current daily login streak | +| longest_daily_login_streak | `number` | The player's longest daily login streak | +| hub_title_count | `number` | How many hub titles the player currently owns | +| hub_title_unlocked | `string[]` | All of the player's owned hub titles | +| costume_count | `number` | How many costumes the player currently owns | +| costume_unlocked | `string[]` | All of the player's owned costumes' names | +| avatar_count | `number` | How many avatars the player currently owns | +| avatar_unlocked | `Avatar[]` | All of the player's owned [avatars](API.md#avatar) | +| friend_count | `number` | How many friends the player has got on the server | +| equipped_hub_title | `string \| null` | The player's currently equipped hub title | +| equipped_costume | `string \| null` | The player's currently equipped costume's name | +| equipped_avatar | `AVATAR \| null` | The player's currently equipped [avatar](API.md#avatar) | +| quest_count | `number` | How many quests the player has ever completed | +| paid_ranks | `Avatar[]` | All of the player's paid [ranks](API.md#player-ranks) | +| pets | `string[]` | All of the player's owned pets' names | +| mounts | `string[]` | All of the player's owned mounts' names | +| hats | `string[]` | All of the player's owned hats' names | +| backblings | `string[]` | All of the player's owned backblings' names | ## Avatar @@ -377,18 +388,6 @@ Each game, and its string ID | GAME.TheBridge | `bridge` | | GAME.Gravity | `grav` | -## Game info - -Constant with [GAME](API.md#games) as keys, and `GAME_INFO_TYPE` as properties: - -| Key | Type | Description | -| ------------ | ---------------- | ------------------------------------------------------------- | -| id | `string` | The ID of the game, as in [GAME](API.md#games). | -| maxLevel | `number` | The maximum level you can reach in this game. | -| increment | `number` | How much the XP requirement increases with each level gained. | -| incrementCap | `number \| null` | The level above which the increment no longer applies. | -| prestige | `boolean` | Wether or not this game supports prestigeing. | - ## Global statistics Total player count for each game diff --git a/docs/GAMES.md b/docs/GAMES.md index ee5a43c..1e0296b 100644 --- a/docs/GAMES.md +++ b/docs/GAMES.md @@ -16,7 +16,7 @@ Fields which are only present in _All-Time statistics_, _Monthly statistics_, or | xp | `number` | Total XP of the player | | level | `number` | Current level (calculated by the wrapper) | | played | `number` | How many times the player has played this game | -| first_played | `Date` | _Only when getting all-time stats_ - The time the player first played this game | +| first_played | `number` | _Only when getting all-time stats_ - The unix timestamp the player first played this game | | victories | `number` | The number of times the player has won this game | | losses | `number` | The number of times the player has lost this game | | win_percentage | `number` | The percentage of wins to total amount of games played | @@ -39,7 +39,7 @@ Fields which are only present in _All-Time statistics_, _Monthly statistics_, or | xp | `number` | Total XP of the player | | level | `number` | Current level (calculated by the wrapper) | | played | `number` | How many times the player has played this game | -| first_played | `Date` | _Only when getting all-time stats_ - The time the player first played this game | +| first_played | `number` | _Only when getting all-time stats_ - The unix timestamp the player first played this game | | victories | `number` | The number of times the player has won this game | | losses | `number` | The number of times the player has lost this game | | win_percentage | `number` | The percentage of wins to total amount of games played | @@ -61,13 +61,14 @@ Fields which are only present in _All-Time statistics_, _Monthly statistics_, or | xp | `number` | Total XP of the player | | level | `number` | Current level (calculated by the wrapper) | | played | `number` | How many times the player has played this game | -| first_played | `Date` | _Only when getting all-time stats_ - The time the player first played this game | +| first_played | `number` | _Only when getting all-time stats_ - The unix timestamp the player first played this game | | victories | `number` | The number of times the player has won this game | | losses | `number` | The number of times the player has lost this game | | win_percentage | `number` | The percentage of wins to total amount of games played | | deaths | `number` | How many times the player has died in this game | | hider_kills | `number` | The number of hiders the player has killed (as a seeker) | | seeker_kills | `number` | The number of seekers the player has killed (as a hider) | +| kdr | `number` | Ratio of kills to deaths | ## Murder Mystery @@ -81,7 +82,7 @@ Fields which are only present in _All-Time statistics_, _Monthly statistics_, or | xp | `number` | Total XP of the player | | level | `number` | Current level (calculated by the wrapper) | | played | `number` | How many times the player has played this game | -| first_played | `Date` | _Only when getting all-time stats_ - The time the player first played this game | +| first_played | `number` | _Only when getting all-time stats_ - The unix timestamp the player first played this game | | victories | `number` | The number of times the player has won this game | | losses | `number` | The number of times the player has lost this game | | win_percentage | `number` | The percentage of wins to total amount of games played | @@ -104,7 +105,7 @@ Fields which are only present in _All-Time statistics_, _Monthly statistics_, or | xp | `number` | Total XP of the player | | level | `number` | Current level (calculated by the wrapper) | | played | `number` | How many times the player has played this game | -| first_played | `Date` | _Only when getting all-time stats_ - The time the player first played this game | +| first_played | `number` | _Only when getting all-time stats_ - The unix timestamp the player first played this game | | victories | `number` | The number of times the player has won this game | | losses | `number` | The number of times the player has lost this game | | win_percentage | `number` | The percentage of wins to total amount of games played | @@ -127,7 +128,7 @@ Fields which are only present in _All-Time statistics_, _Monthly statistics_, or | xp | `number` | Total XP of the player | | level | `number` | Current level (calculated by the wrapper) | | played | `number` | How many times the player has played this game | -| first_played | `Date` | _Only when getting all-time stats_ - The time the player first played this game | +| first_played | `number` | _Only when getting all-time stats_ - The unix timestamp the player first played this game | | victories | `number` | The number of times the player has won this game | | losses | `number` | The number of times the player has lost this game | | win_percentage | `number` | The percentage of wins to total amount of games played | @@ -150,7 +151,7 @@ Fields which are only present in _All-Time statistics_, _Monthly statistics_, or | xp | `number` | Total XP of the player | | level | `number` | Current level (calculated by the wrapper) | | played | `number` | How many times the player has played this game | -| first_played | `Date` | _Only when getting all-time stats_ - The time the player first played this game | +| first_played | `number` | _Only when getting all-time stats_ - The unix timestamp the player first played this game | | victories | `number` | The number of times the player has won this game | | losses | `number` | The number of times the player has lost this game | | win_percentage | `number` | The percentage of wins to total amount of games played | @@ -159,6 +160,7 @@ Fields which are only present in _All-Time statistics_, _Monthly statistics_, or | rating_meh_received | `number` | How many mystery chests the player has opened | | rating_okay_raceived | `number` | How many ores the player has mined | | rating_great_received | `number` | How many spells the player has used | +| total_ratings | `number` | The sum of all the players ratings | ## Ground Wars @@ -172,7 +174,7 @@ Fields which are only present in _All-Time statistics_, _Monthly statistics_, or | xp | `number` | Total XP of the player | | level | `number` | Current level (calculated by the wrapper) | | played | `number` | How many times the player has played this game | -| first_played | `Date` | _Only when getting all-time stats_ - The time the player first played this game | +| first_played | `number` | _Only when getting all-time stats_ - The unix timestamp the player first played this game | | victories | `number` | The number of times the player has won this game | | losses | `number` | The number of times the player has lost this game | | win_percentage | `number` | The percentage of wins to total amount of games played | @@ -195,7 +197,7 @@ Fields which are only present in _All-Time statistics_, _Monthly statistics_, or | xp | `number` | Total XP of the player | | level | `number` | Current level (calculated by the wrapper) | | played | `number` | How many times the player has played this game | -| first_played | `Date` | _Only when getting all-time stats_ - The time the player first played this game | +| first_played | `number` | _Only when getting all-time stats_ - The unix timestamp the player first played this game | | victories | `number` | The number of times the player has won this game | | losses | `number` | The number of times the player has lost this game | | win_percentage | `number` | The percentage of wins to total amount of games played | @@ -216,7 +218,7 @@ Fields which are only present in _All-Time statistics_, _Monthly statistics_, or | xp | `number` | Total XP of the player | | level | `number` | Current level (calculated by the wrapper) | | played | `number` | How many times the player has played this game | -| first_played | `Date` | _Only when getting all-time stats_ - The time the player first played this game | +| first_played | `number` | _Only when getting all-time stats_ - The unix timestamp the player first played this game | | victories | `number` | The number of times the player has won this game | | losses | `number` | The number of times the player has lost this game | | win_percentage | `number` | The percentage of wins to total amount of games played | @@ -239,7 +241,7 @@ Fields which are only present in _All-Time statistics_, _Monthly statistics_, or | xp | `number` | Total XP of the player | | level | `number` | Current level (calculated by the wrapper) | | played | `number` | How many times the player has played this game | -| first_played | `Date` | _Only when getting all-time stats_ - The time the player first played this game | +| first_played | `number` | _Only when getting all-time stats_ - The unix timestamp the player first played this game | | victories | `number` | The number of times the player has won this game | | losses | `number` | The number of times the player has lost this game | | win_percentage | `number` | The percentage of wins to total amount of games played | @@ -258,7 +260,7 @@ Fields which are only present in _All-Time statistics_, _Monthly statistics_, or | xp | `number` | Total XP of the player | | level | `number` | Current level (calculated by the wrapper) | | played | `number` | How many times the player has played this game | -| first_played | `Date` | _Only when getting all-time stats_ - The time the player first played this game | +| first_played | `number` | _Only when getting all-time stats_ - The unix timestamp the player first played this game | | victories | `number` | The number of times the player has won this game | | losses | `number` | The number of times the player has lost this game | | win_percentage | `number` | The percentage of wins to total amount of games played | @@ -279,7 +281,7 @@ Fields which are only present in _All-Time statistics_, _Monthly statistics_, or | xp | `number` | Total XP of the player | | level | `number` | Current level (calculated by the wrapper) | | played | `number` | How many times the player has played this game | -| first_played | `Date` | _Only when getting all-time stats_ - The time the player first played this game | +| first_played | `number` | _Only when getting all-time stats_ - The unix timestamp the player first played this game | | victories | `number` | The number of times the player has won this game | | losses | `number` | The number of times the player has lost this game | | win_percentage | `number` | The percentage of wins to total amount of games played | diff --git a/package.json b/package.json index 9b4eee1..6394273 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hive-bedrock-api", - "version": "1.0.10", + "version": "2.0.0", "description": "An API wrapper for the Hive Minecraft Bedrock Edition server.", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -11,20 +11,15 @@ "license": "MIT", "author": "Cubeedge Studios", "dependencies": { - "hive-bedrock-data": "^1.1.1" + "hive-bedrock-data": "^1.1.2" }, "scripts": { - "build": "tsc", - "test": "jest" + "build": "tsc" }, "files": [ "lib/**/*" ], "devDependencies": { - "@types/jest": "^29.5.2", - "jest": "^29.5.0", - "jest-extended": "^4.0.0", - "ts-jest": "^29.1.0", "ts-node": "^10.9.1", "typescript": "^5.1.3" } diff --git a/src/methods/getAllTimeStatistics.ts b/src/methods/getAllTimeStatistics.ts index 54e7969..c040c6d 100644 --- a/src/methods/getAllTimeStatistics.ts +++ b/src/methods/getAllTimeStatistics.ts @@ -3,7 +3,7 @@ import { APIResponse, Options } from "../types/types"; import { AllGameStatistics, StatisticsResponse } from "../types/output"; import isGame from "../helpers/isGame"; import fetchEndpoint from "../helpers/fetchEndpoint"; -import getProcessors from "../processors"; +import getProcessors, { getPlayerProcessors } from "../processors"; type AllGameStatisticsPlayer = AllGameStatistics & { player: PlayerMetadata; @@ -66,11 +66,16 @@ export default async function getAllTimeStatistics( } } + let player = games.find(([g]) => g === "main")?.at(1) as PlayerMetadata; + + let processors = getPlayerProcessors(); + processors.forEach((processor) => processor(player)); + return { ...response, data: { ...output, - player: games.find(([g]) => g === "main")?.at(1), + player, }, }; } diff --git a/src/methods/getPlayerInfomation.ts b/src/methods/getPlayerInfomation.ts index f492c5b..7b30813 100644 --- a/src/methods/getPlayerInfomation.ts +++ b/src/methods/getPlayerInfomation.ts @@ -1,6 +1,7 @@ import { MapMetadata, PlayerMetadata } from "hive-bedrock-data"; import { APIResponse, Options } from "../types/types"; import fetchEndpoint from "../helpers/fetchEndpoint"; +import { getPlayerProcessors } from "../processors"; export default function getPlayerInfomation( identifier: string, @@ -17,8 +18,13 @@ export default async function getPlayerInfomation( ); if (response.error) return response; + let player = response.data.main; + + let processors = getPlayerProcessors(); + processors.forEach((processor) => processor(player)); + return { ...response, - data: response.data.main, + data: player, }; } diff --git a/src/processors/index.ts b/src/processors/index.ts index 6e8ca3f..c3e7c31 100644 --- a/src/processors/index.ts +++ b/src/processors/index.ts @@ -75,3 +75,20 @@ export default function getProcessors( ), ]; } + +export function getPlayerProcessors(): ((player: { + [key: string]: any; +}) => void)[] { + return [ + (p) => (p.daily_login_streak ??= 0), + (p) => (p.longest_daily_login_streak ??= 0), + (p) => (p.quest_count ??= 0), + (p) => (p.friend_count ??= 0), + (p) => (p.hub_title_unlocked ??= []), + (p) => (p.avatar_unlocked ??= []), + (p) => (p.costume_unlocked ??= []), + (p) => (p.equipped_hub_title ??= null), + (p) => (p.equipped_avatar ??= null), + (p) => (p.equipped_costume ??= null), + ]; +} From bd192f99087106c66b542da66514d55be1b3a170 Mon Sep 17 00:00:00 2001 From: David Fiddes <66165184+UCDFiddes@users.noreply.github.com> Date: Tue, 13 Feb 2024 20:55:07 +0100 Subject: [PATCH 07/13] fix player processor for alltime stats --- src/methods/getAllTimeStatistics.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/methods/getAllTimeStatistics.ts b/src/methods/getAllTimeStatistics.ts index c040c6d..b250b41 100644 --- a/src/methods/getAllTimeStatistics.ts +++ b/src/methods/getAllTimeStatistics.ts @@ -66,7 +66,7 @@ export default async function getAllTimeStatistics( } } - let player = games.find(([g]) => g === "main")?.at(1) as PlayerMetadata; + let player = games.find(([g]) => g === "main")?.[1] as PlayerMetadata; let processors = getPlayerProcessors(); processors.forEach((processor) => processor(player)); From 00a83cc1da351a1ec9dd8707b6e1070a9ed69c7a Mon Sep 17 00:00:00 2001 From: David Fiddes <66165184+UCDFiddes@users.noreply.github.com> Date: Tue, 13 Feb 2024 20:56:31 +0100 Subject: [PATCH 08/13] temp remove tests --- .github/workflows/jest.yml | 26 -------------------------- 1 file changed, 26 deletions(-) delete mode 100644 .github/workflows/jest.yml diff --git a/.github/workflows/jest.yml b/.github/workflows/jest.yml deleted file mode 100644 index b29a855..0000000 --- a/.github/workflows/jest.yml +++ /dev/null @@ -1,26 +0,0 @@ -# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node -# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs - -name: Jest Tests - -on: - push: - branches: ["master"] - pull_request: - branches: ["master"] - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - name: Checkout repo - uses: actions/checkout@v2 - - name: Set node version - uses: actions/setup-node@v3 - with: - node-version: "18.x" - - - run: yarn - - run: yarn build - - run: yarn test From 08e911e98eae040ad5a36f17cdeace3480b833ec Mon Sep 17 00:00:00 2001 From: David Fiddes <66165184+UCDFiddes@users.noreply.github.com> Date: Tue, 13 Feb 2024 21:01:31 +0100 Subject: [PATCH 09/13] Add history entry --- HISTORY.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/HISTORY.md b/HISTORY.md index 0b39477..a381016 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,3 +1,9 @@ +# Release 2.0.0 + +- Rename multiple methods to their full names +- Update the methods the API data is processed +- Move some functions and types to hive-bedrock-data + # Release 1.0.8 - Add the getGameMetadata method From 505700022cc0dd82beff3e4de71fdf30b04b067a Mon Sep 17 00:00:00 2001 From: David Fiddes <66165184+UCDFiddes@users.noreply.github.com> Date: Thu, 15 Feb 2024 21:53:27 +0000 Subject: [PATCH 10/13] Add prepare script --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 6394273..33ffc86 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "hive-bedrock-data": "^1.1.2" }, "scripts": { + "prepare": "npm run tsc", "build": "tsc" }, "files": [ From b3d482d3cfe88a4edb69112adb8033a73abf7d47 Mon Sep 17 00:00:00 2001 From: David Fiddes <66165184+UCDFiddes@users.noreply.github.com> Date: Thu, 15 Feb 2024 21:55:22 +0000 Subject: [PATCH 11/13] Update package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 33ffc86..c453a26 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "hive-bedrock-data": "^1.1.2" }, "scripts": { - "prepare": "npm run tsc", + "prepare": "npm run build", "build": "tsc" }, "files": [ From 8b60be84b3b726b470b7dae50d8ee250bf81c914 Mon Sep 17 00:00:00 2001 From: David Fiddes <66165184+UCDFiddes@users.noreply.github.com> Date: Thu, 15 Feb 2024 21:58:12 +0000 Subject: [PATCH 12/13] Add prepack script --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index c453a26..37d50c5 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "hive-bedrock-data": "^1.1.2" }, "scripts": { + "prepack": "yarn build", "prepare": "npm run build", "build": "tsc" }, From d38946c24b0febd34b7290082bb35c5b4d79215f Mon Sep 17 00:00:00 2001 From: UCDFiddes <66165184+UCDFiddes@users.noreply.github.com> Date: Wed, 27 Mar 2024 18:47:14 +0000 Subject: [PATCH 13/13] Fix review --- README.md | 2 +- docs/API.md | 17 +++++++---------- package.json | 2 +- src/helpers/fetchEndpoint.ts | 5 ++--- src/helpers/toPoint.ts | 2 +- src/index.ts | 4 +++- src/methods/getAllTimeStatistics.ts | 10 +++++----- src/methods/getMonthlyStatistics.ts | 10 +++++----- src/types/output.ts | 8 +------- 9 files changed, 26 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index 2f4cc25..7fdf12b 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,7 @@ const { data, error } = await getMaps(Game.TreasureWars); ### Fetch Metadata ```ts -import { getMaps, Game } from "hive-bedrock-api"; +import { getMetadata, Game } from "hive-bedrock-api"; // Returns data for a specific game's currently active maps const { data, error } = await getMetadata(Game.TreasureWars); diff --git a/docs/API.md b/docs/API.md index 63614d8..152f1b7 100644 --- a/docs/API.md +++ b/docs/API.md @@ -27,9 +27,9 @@ Returns a Promise which resolves to the following object: ```ts import { getMonthlyStatsistics, Game } from "hive-bedrock-api"; -// Get GAMERTAG's Treasure Wars stats from June 2023 +// Get player's Treasure Wars stats from June 2023 const { data, error } = await getMonthlyStatsistics( - "GAMERTAG", + "player", Game.TreasureWars, { year: 2023, @@ -97,11 +97,8 @@ Returns a Promise which resolves to the following object: ```ts import { getAllTimeStatistics, Game } from "hive-bedrock-api"; -// Get GAMERTAG's all-time Hide and Seek stats -const { data, error } = await getAllTimeStatistics( - "GAMERTAG", - Game.HideAndSeek -); +// Get player's all-time Hide and Seek stats +const { data, error } = await getAllTimeStatistics("player", Game.HideAndSeek); ``` ## getAllTimeLeaderboard(game[, options]) @@ -219,7 +216,7 @@ Returns a Promise which resolves to the following object: | Field | Type | Description | | -------- | ------------------------------------------- | -------------------------------- | -| data | [Response](API.md#map-data) `\| null` | The response data | +| data | [Response](API.md#game-metadata) `\| null` | The response data | | error | `{ code: number, message: string } \| null` | Error data | | status | `number` | The http status returned | | duration | `number \| undefined` | The duration of the http request | @@ -257,8 +254,8 @@ Returns a Promise which resolves to the following object ```ts import { getPlayerInformation } from "hive-bedrock-api"; -// Get GAMERTAG's player info. -const { data, error } = await getPlayerInformation("GAMERTAG"); +// Get player's player info. +const { data, error } = await getPlayerInformation("player"); ``` ## PlayerInfo diff --git a/package.json b/package.json index 37d50c5..9e5d7f1 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "license": "MIT", "author": "Cubeedge Studios", "dependencies": { - "hive-bedrock-data": "^1.1.2" + "hive-bedrock-data": "^1.1.7" }, "scripts": { "prepack": "yarn build", diff --git a/src/helpers/fetchEndpoint.ts b/src/helpers/fetchEndpoint.ts index 6413b2a..7ae969f 100644 --- a/src/helpers/fetchEndpoint.ts +++ b/src/helpers/fetchEndpoint.ts @@ -1,6 +1,5 @@ -import { API_SERVER } from ".."; import { APIResponse } from "../types/types"; -import { Routes } from "hive-bedrock-data"; +import { API_BASE_ENDPOINT, Routes } from "hive-bedrock-data"; export default async function fetchEndpoint( endpoint: T, @@ -13,7 +12,7 @@ export default async function fetchEndpoint( ): Promise>> { try { let time_start = performance.now(); - let request = await fetch(API_SERVER + endpoint, init); + let request = await fetch(API_BASE_ENDPOINT + endpoint, init); let time_end = performance.now(); let duration = Math.round(time_end - time_start); diff --git a/src/helpers/toPoint.ts b/src/helpers/toPoint.ts index ed5b757..47d63fa 100644 --- a/src/helpers/toPoint.ts +++ b/src/helpers/toPoint.ts @@ -1,3 +1,3 @@ export default function toPoint(num: number, point: number): number { - return (Math.round((num * 10) ^ point) / 10) ^ point; + return Math.round(num * (10 ^ point)) / (10 ^ point); } diff --git a/src/index.ts b/src/index.ts index cda9b31..65faeba 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,8 +6,8 @@ import getMetdata from "./methods/getMetadata"; import getMonthlyLeaderboard from "./methods/getMonthlyLeaderboard"; import getMonthlyStatistics from "./methods/getMonthlyStatistics"; import getPlayerInfomation from "./methods/getPlayerInfomation"; +import type { AllStatistics, AllLeaderboards } from "./types/output"; -export const API_SERVER = "https://api.playhive.com/v0"; export { getAllTimeLeaderboard, getMonthlyLeaderboard, @@ -19,4 +19,6 @@ export { getPlayerInfomation, }; +export type { AllStatistics, AllLeaderboards }; + export { Game } from "hive-bedrock-data"; diff --git a/src/methods/getAllTimeStatistics.ts b/src/methods/getAllTimeStatistics.ts index b250b41..04bb71f 100644 --- a/src/methods/getAllTimeStatistics.ts +++ b/src/methods/getAllTimeStatistics.ts @@ -28,6 +28,11 @@ export default async function getAllTimeStatistics( let game_id: G | "all" = "all"; let method_options: Options | undefined = game_or_options as Options; + if (typeof game_or_options === "string") { + game_id = game_or_options as G; + method_options = options; + } + if (!isGame(game_id as G) && game_id !== "all") return { status: 404, @@ -38,11 +43,6 @@ export default async function getAllTimeStatistics( data: null, }; - if (isGame(game_or_options as G)) { - game_id = game_or_options as G; - method_options = options; - } - let response = await fetchEndpoint( `/game/all/${game_id}/${identifier}`, method_options?.init diff --git a/src/methods/getMonthlyStatistics.ts b/src/methods/getMonthlyStatistics.ts index d1266ec..5dffb7e 100644 --- a/src/methods/getMonthlyStatistics.ts +++ b/src/methods/getMonthlyStatistics.ts @@ -35,6 +35,11 @@ export default async function getMonthlyStatistics( let method_options: MonthlyOptions | undefined = game_or_options as MonthlyOptions; + if (typeof game_or_options === "string") { + game_id = game_or_options as G; + method_options = options; + } + if (!isGame(game_id as G) && game_id !== "all") return { status: 404, @@ -45,11 +50,6 @@ export default async function getMonthlyStatistics( data: null, }; - if (isGame(game_or_options as G)) { - game_id = game_or_options as G; - method_options = options; - } - let current_date = new Date(); let endpoint = `/game/monthly/player/${game_id}/${identifier}/${ options?.year ?? current_date.getFullYear() diff --git a/src/types/output.ts b/src/types/output.ts index ec01cc5..160f53c 100644 --- a/src/types/output.ts +++ b/src/types/output.ts @@ -1,10 +1,4 @@ -import { - Game, - Leaderboards, - PlayerMetadata, - Statistics, - Timeframe, -} from "hive-bedrock-data"; +import { Game, Leaderboards, Statistics, Timeframe } from "hive-bedrock-data"; type Processor = (stats: any) => void; export type GameProcessors = {