"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.getPlayerInteraction = exports.getPlayer = void 0; const cheerio = __importStar(require("cheerio")); const https_1 = __importDefault(require("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 - (.+) \((.*)\)/i, //$1 = level, $2 = rank, $3 = elo/class (rest is found by splitting each line) ogdescription: [ //line 1 [ /lv\.(\d+) (\w+) \((\d+)\)/i, /lv\.(\d+) (\w+): class (\d+)/i //other ], ] }; /* 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) { //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) => { 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 = https_1.default.request(init, callback); req.end(); }); } /** * interprets the html from getHTML() * @param name name of player * @returns player data from site */ function readHTML(html) { let metaElems = cheerio.load(html)('meta').toArray(), foundData = { name: "", id: "", level: "", rank: "", elo: null, class: null, 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') { //all lines let lines = attr.content.split('\n').map(l => l.trim()), extraLines = []; //ensure first line is correct while (lines.length && !/pok.mon unite/i.test(lines[0])) { let line = lines.shift(); if (line) extraLines.push(line); } if (!lines.length) throw 'Unable to read data, please try again'; //bring the first lines removed back into the data lines = [ ...lines, ...extraLines.filter(d => d) ]; //first line { //will be only text after "pokemon unite:" let line = lines[0].split(':').slice(1).join(':').trim(); let regex = uniteApiRegex.ogdescription[0]; if (regex[0].test(line)) { //is master/has elo let regexData = line.match(regex[0]); if (!regexData || regexData.length < 4) throw 'Unable to read data, please try again'; foundData.level = regexData[1]; foundData.rank = regexData[2]; foundData.elo = regexData[3]; } else { //is not master/has a class let regexData = line.match(regex[1]); if (!regexData || regexData.length < 4) throw 'Unable to read data, please try again'; foundData.level = regexData[1]; foundData.rank = regexData[2]; foundData.class = regexData[3]; } } lines.shift(); //rest of lines lines.forEach(line => { let split = line.split(':').map(l => l.trim()), key = split[0].toLowerCase().replace(/[^\w]/g, ''), value = split[1]; switch (key) { case 'battles': foundData.battles = value; break; case 'wins': foundData.wins = value; break; case 'winrate': foundData.winrate = value; break; } }); /*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) { if (data.id.length) return true; return false; } /** * gets player data from uniteApi * @param name name of player * @returns player data */ async function getPlayer(name) { let html = await getHTML(name); let data = readHTML(html); if (verifyData(data)) return data; return null; } exports.getPlayer = getPlayer; /** * calls getPlayer() with the name from the interaction * @param interaction discord interaction */ async function getPlayerInteraction(interaction) { let username = interaction.options.getString('username', true); await interaction.deferReply(); let data = await getPlayer(username); if (data === null) await interaction.editReply('Unable to find user'); else await interaction.editReply('```\n' + JSON.stringify(data, null, 2) + '\n```'); } exports.getPlayerInteraction = getPlayerInteraction; //await getPlayer('IanWhysp')