Files
1800queue/src/api.ts
2022-01-27 16:39:45 -06:00

151 lines
4.0 KiB
TypeScript

import * as cheerio from 'cheerio';
import { IncomingMessage } from 'http';
import http from 'https';
//making long regex rather than splitting the string at ":" because regex would be easier to debug than logic in the case the website changes
//while names cant have spaces, the name slot in ogtitle could be shown as "No Player Name" which has spaces
const uniteApiRegex = {
//$1 = name, $2 = id
ogtitle: /Unite API - ([\w\d ]+) \((.*)\)/,
//$1 = level, $2 = rank, $3 = elo, $4 = battles, $5 = wins, $6 = win rate
ogdescription: /Pokémon Unite : Lv.(\d+) (\w+) \((\d+)\)\n Battles : (\d+)\n Wins : (\d+)\n Win Rate : (\d+)%/,
}
type uniteApiData = {
name: string,
id: string,
level: string,
rank: string,
elo: string,
battles: string,
wins: string,
winrate: string
}
/*
og:title
Unite API - IanWhysp (X188GF7)
og:description
Pokémon Unite : Lv.40 Master (1741)
Battles : 1089
Wins : 576
Win Rate : 52%
*/
/**
* gets the html of the uniteApi page for the player
* @param name name of player
* @returns the html of the page through a promise (rejects if the page status is not 200)
*/
function getHTML(name: string): Promise<string> {
name = name.replace(/[^\w\d]/g, '');
return new Promise((resolve, reject) => {
const init = {
host: 'uniteapi.dev',
path: `/p/${encodeURIComponent(name)}`,
method: 'GET',
};
const callback = (response: IncomingMessage) => {
if (response.statusCode !== 200) {
reject(`HTTP ERROR ${response.statusCode}: ${response.statusMessage}`);
return;
}
let result = Buffer.alloc(0);
response.on('data', function(chunk) {
result = Buffer.concat([result, chunk]);
});
response.on('end', function() {
// result has response body buffer
resolve(result.toString());
});
};
const req = http.request(init, callback);
req.end();
});
}
/**
* interprets the html from getHTML()
* @param name name of player
* @returns player data from site
*/
function readHTML(html: string): uniteApiData {
let metaElems = cheerio.load(html)('meta').toArray(),
foundData: uniteApiData = {
name: "",
id: "",
level: "",
rank: "",
elo: "",
battles: "",
wins: "",
winrate: ""
};
//filter down to just ones named "og:..."
metaElems = metaElems.filter(el => el.attribs.property?.startsWith('og:'));
metaElems.forEach(el => {
let attr = el.attribs;
if (attr.property === 'og:title') {
let data = uniteApiRegex.ogtitle.exec(attr.content);
if (data !== null && data.length >= 3) {
foundData.name = data[1];
foundData.id = data[2];
}
} else if (attr.property === 'og:description') {
let data = uniteApiRegex.ogdescription.exec(attr.content);
if (data !== null && data.length >= 7) {
foundData.level = data[1];
foundData.rank = data[2];
foundData.elo = data[3];
foundData.battles = data[4];
foundData.wins = data[5];
foundData.winrate = data[6];
}
}
})
return foundData;
}
/**
* verifies the data
* @param data uniteApi data
* @returns boolean, valid or invalid
*/
function verifyData(data: uniteApiData): boolean {
if (data.id.length)
return true;
return false;
}
/**
* gets player data from uniteApi
* @param name name of player
* @returns player data
*/
export async function getPlayer(name: string): Promise<uniteApiData|null> {
let html = await getHTML(name);
let data = readHTML(html);
if (verifyData(data))
return data;
return null;
}
//await getPlayer('IanWhysp')