Skip to content

Commit

Permalink
0.2.0
Browse files Browse the repository at this point in the history
  • Loading branch information
NeutronicMC committed Jun 20, 2020
1 parent 47bd06e commit 888853e
Show file tree
Hide file tree
Showing 7 changed files with 225 additions and 116 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Advanced Hive Bedrock API wrapper with caching.
## Getting started
*Work in progress.*
```js
import HiveApi from "hive-tools-wrapper"
import HiveApi from "hive-tools-wrapper/lib/HiveApi"

// Make a new HiveApi instance, with a 300 second cache.
let api = new HiveApi(300)
Expand Down
1 change: 0 additions & 1 deletion index.js

This file was deleted.

2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
{
"name": "hive-tools-wrapper",
"version": "0.1.1",
"version": "0.2.0",
"description": "Advanced Hive Bedrock API wrapper with caching.",
"main": "index.js",
"main": "HiveApi.js",
"types": "HiveApi.d.ts",
"files": [
"lib/**/*"
],
Expand All @@ -21,7 +22,7 @@
"scripts": {
"test": "jest --config jestconfig.json",
"build": "tsc",
"format": "prettier --write \"src/**/*.ts\" \"src/**/*.js\"",
"format": "prettier --write \"src/**/*.ts\"",
"lint": "tslint -p tsconfig.json",
"prepare": "npm run build",
"prepublishOnly": "npm test && npm run lint",
Expand Down
192 changes: 146 additions & 46 deletions src/HiveApi.ts
Original file line number Diff line number Diff line change
@@ -1,62 +1,162 @@
import ApiClient from "./api/ApiClient"
import {AxiosResponse} from "axios";
import ApiClient from './api/ApiClient';

export default class HiveToolsAPI extends ApiClient {
constructor(cacheTimeout: number) {
super(cacheTimeout);
}
public static async getMonthlyLeaderboard(game: string): Promise<any[]> {
const params = {
game,
};
const response = await this.getData('/game/monthly/{game}', params);
return this.calculateExtraLeaderboardStats(response.data);
}

public async getMonthlyLeaderboard(game: string, cache: boolean = true) {
const params = {
"game": game
}
public static async getSpecificMonthlyLeaderboard(
game: string,
year: string,
month: string,
amount?: string,
skip?: string,
): Promise<any[]> {
const params = {
game,
year,
month,
amount,
skip,
};
const response = await this.getData('/game/monthly/{game}/{year}/{month}/{amount}/{skip}', params);

return (await this.getData("/game/monthly/{game}", params, cache)).data;
}
return this.calculateExtraLeaderboardStats(response.data);
}

public async getSpecificMonthlyLeaderboard(game: string, year: string, month: string, amount?: string, skip?: string, cache: boolean = true) {
const params = {
'game': game,
'year': year,
'month': month,
'amount': amount,
'skip': skip
}
public static async getMonthlyPlayerStatistics(game: string, player: string): Promise<any> {
const params = {
game,
player,
};
const response = await this.getData('/game/monthly/player/{game}/{player}', params);
return this.calculateExtraPlayerStats(response.data);
}

return (await this.getData("/game/monthly/{game}/{year}/{month}/{amount}/{skip}", params, cache)).data;
}
public static async getSpecificMonthlyPlayerStatistics(
game: string,
player: string,
year: string,
month: string,
): Promise<any> {
const params = {
game,
player,
year,
month,
};
const response = await this.getData('/game/monthly/player/{game}/{player}/{year}/{month}', params);
return this.calculateExtraPlayerStats(response.data);
}

public static async getAllTimeLeaderboard(game: string): Promise<any[]> {
const params = {
game,
};
const response = await this.getData('/game/monthly/player/{game}/{player}/{year}/{month}', params);
return this.calculateExtraLeaderboardStats(response.data);
}

public static async getAllTimePlayerStatistics(game: string, player: string): Promise<any> {
const params = {
game,
player,
};
const response = await this.getData('/game/all/{game}/{player}', params);
return this.calculateExtraPlayerStats(response.data);
}

// Calculate extra statistics for a player. There are many obscure calculations for the Hive Tools website.
public static calculateExtraPlayerStats(data: any): any {
({
win_percentage: data.win_percentage,
win_percentage_raw: data.win_percentage_raw,
games_lost: data.games_lost,
} = this.calculateWinLossInfo(data));

if (data.hasOwnProperty('kills') || data.hasOwnProperty('hider_kills')) {
let kills;

public async getMonthlyPlayerStatistics(game: string, player: string, cache: boolean = true): Promise<AxiosResponse> {
const params = {
'game': game,
'player': player
// Use either normal kills stat or Hide and Seek kills stat.
if (data.hasOwnProperty('kills')) {
kills = data.kills;
} else {
kills = data.hider_kills;
}

data.kpr = kills / data.played;

if (data.hasOwnProperty('deaths')) {
data.dpr = data.deaths / data.played;
if (data.deaths === 0) {
data.kdr = kills;
if (data.hasOwnProperty('seeker_kills')) {
data.skdr = data.seeker_kills;
}
if (data.hasOwnProperty('final_kills')) {
data.fkdr = data.final_kills;
}
if (data.hasOwnProperty('treasure_destroyed')) {
data.ddr = data.treasure_destroyed;
}
} else {
data.kdr = (kills / data.deaths).toFixed(2);
if (data.hasOwnProperty('seeker_kills')) {
data.skdr = data.seeker_kills / data.deaths;
}
if (data.hasOwnProperty('final_kills')) {
data.fkdr = (data.final_kills / data.deaths).toFixed(2);
}
if (data.hasOwnProperty('treasure_destroyed')) {
data.ddr = data.treasure_destroyed / data.deaths;
}
}
}

return (await this.getData("/game/monthly/player/{game}/{player}", params, cache)).data;
return data;
}

public async getSpecificMonthlyPlayerStatistics(game: string, player: string, year: string, month: string, cache: boolean = true): Promise<AxiosResponse> {
const params = {
'game': game,
'player': player,
'year': year,
'month': month
}
if (data.hasOwnProperty('coins')) {
data.cpr = data.coins / data.played;

return (await this.getData("/game/monthly/player/{game}/{player}/{year}/{month}", params, cache)).data;
// Just in case a new game ever has the "coins" statistic
if (data.hasOwnProperty('murderer_eliminations')) {
data.efficiency = data.murderer_eliminations / (data.coins / 10);
}
}

public async getAllTimeLeaderboard(game: string, cache: boolean = true): Promise<AxiosResponse> {
const params = {
"game": game
}
return (await this.getData("/game/monthly/player/{game}/{player}/{year}/{month}", params, cache)).data;
if (data.hasOwnProperty('deathmatches')) {
data.dmpr = data.deathmatches / data.played;
}

public async getAllTimePlayerStatistics(game: string, player: string, cache: boolean = true) {
const params = {
'game': game,
'player': player
}
return (await this.getData("/game/all/{game}/{player}", params, cache)).data;
if (data.hasOwnProperty('checkpoints')) {
data.cdr = data.checkpoints / data.deaths;
}
}
}

public static calculateExtraLeaderboardStats(data: any[]): any[] {
data.forEach((player, index) => {
data[index] = this.calculateExtraPlayerStats(player);
});

return data;
}

private static calculateWinLossInfo(data: any): any {
const returnData: any = {};
if (data.victories === 0) {
returnData.win_percentage = '0%';
returnData.win_percentage_raw = 0;
} else {
returnData.win_percentage_raw = data.victories / data.played;
returnData.win_percentage = `${(data.win_percentage_raw * 100).toFixed(2)}%`;
}
returnData.games_lost = data.played - data.victories;

return returnData;
}
}
122 changes: 63 additions & 59 deletions src/api/ApiClient.ts
Original file line number Diff line number Diff line change
@@ -1,79 +1,83 @@
import Axios, {AxiosResponse} from "axios";
import Axios, { AxiosResponse } from 'axios';

export interface Params {
[key: string]: any;
[key: string]: any;
}

interface CacheData {
[key: string]: {
timestamp: number,
data: AxiosResponse
}
[key: string]: {
expires: number;
data: AxiosResponse;
};
}

export default class ApiClient {
cacheTimeout: number;
constructor(cacheTimeout: number) {
this.cacheTimeout = cacheTimeout;
}
private basePath: string = "https://api.playhive.com/v0";
private static basePath: string = 'https://api.playhive.com/v0';
private static cacheTimeout: number = 0;

public static async callApi(path: string, pathParams: object): Promise<AxiosResponse> {
return await Axios.get(this.buildUrl(path, pathParams));
}

public async callApi(path: string, pathParams: object):Promise<AxiosResponse> {
return await Axios.get(this.buildUrl(path, pathParams));
public static buildUrl(path: string, pathParams: Params) {
if (!path.match(/^\//)) {
path = '/' + path;
}

public buildUrl(path: string, pathParams: Params) {
if (!path.match(/^\//)) {
path = '/' + path;
}
let url: string = ApiClient.buildPath(path, pathParams);
url = this.basePath + url;

let url: string = ApiClient.buildPath(path, pathParams);
url = this.basePath + url;
return url;
}

return url;
public static paramToString(param: any): string {
if (param === undefined) {
return '';
}
if (param instanceof Date) {
return param.toJSON();
}

public static paramToString(param: any): string {
if (param === undefined) {
return '';
}
if (param instanceof Date) {
return param.toJSON();
}
return param.toString();
}

return param.toString();
}
public static buildPath(path: string, pathParams: Params): string {
return path.replace(/\{([\w-]+)\}/g, (fullMatch, key) => {
let value;
if (pathParams.hasOwnProperty(key)) {
value = ApiClient.paramToString(pathParams[key]);
} else {
value = fullMatch;
}

public static buildPath(path: string, pathParams: Params): string {
return path.replace(/\{([\w-]+)\}/g, (fullMatch, key) => {
let value;
if (pathParams.hasOwnProperty(key)) {
value = ApiClient.paramToString(pathParams[key]);
} else {
value = fullMatch;
}
return encodeURIComponent(value);
});
}

return encodeURIComponent(value);
});
private static cacheData: CacheData = {};
public static async getData(path: string, pathParams: Params) {
const key = ApiClient.buildPath(path, pathParams);
const now = new Date().getTime();
if (this.cacheData.hasOwnProperty(key)) {
if (this.cacheData[key].expires < now) {
this.cacheData[key] = {
expires: now + this.cacheTimeout * 1000,
data: await this.callApi(path, pathParams),
};
}
} else {
this.cacheData[key] = {
expires: now + this.cacheTimeout * 1000,
data: await this.callApi(path, pathParams),
};
}

cacheData: CacheData = {};
public async getData(path: string, pathParams: Params, cache: boolean) {
const key = ApiClient.buildPath(path, pathParams);
const now = new Date().getTime();
if (this.cacheData.hasOwnProperty(key) && cache) {
if (this.cacheData[key].timestamp + (this.cacheTimeout * 1000) < now) {
this.cacheData[key] = {
timestamp: now,
data: await this.callApi(path, pathParams)
}
}
} else {
this.cacheData[key] = {
timestamp: now,
data: await this.callApi(path, pathParams)
}
}
return this.cacheData[key].data;
}
}
// TODO: implement rate limit checks

return this.cacheData[key].data;
}

public static setCacheTimeout(_cacheTimeout: number) {
this.cacheTimeout = _cacheTimeout;
}
}
Loading

0 comments on commit 888853e

Please sign in to comment.