9b395f5cc3
E2E Headed Chrome / e2e-headed (macos-15) (push) Has been cancelled
E2E Headed Chrome / e2e-headed (ubuntu-latest) (push) Has been cancelled
E2E Headed Chrome / e2e-headed (windows-latest) (push) Has been cancelled
CI / build (macos-latest) (push) Has been cancelled
CI / build (ubuntu-latest) (push) Has been cancelled
CI / build (windows-latest) (push) Has been cancelled
CI / unit-test (push) Has been cancelled
CI / bun-test (push) Has been cancelled
CI / adapter-test (push) Has been cancelled
CI / smoke-test (macos-latest) (push) Has been cancelled
CI / smoke-test (ubuntu-latest) (push) Has been cancelled
Security Audit / audit (push) Has been cancelled
Build Chrome Extension / build (push) Has been cancelled
Trigger Website Rebuild (Docs Updated) / dispatch (push) Has been cancelled
104 lines
4.4 KiB
JavaScript
104 lines
4.4 KiB
JavaScript
import { cli, Strategy } from '@jackwener/opencli/registry';
|
|
import { BASE, assertRequiredFields, buildPlayerUrl, gotoAndWait, parseMoneyUsd, parseNumber, parsePlayerRef } from './utils.js';
|
|
|
|
cli({
|
|
site: 'hltv',
|
|
name: 'player-summary',
|
|
description: 'Read an HLTV player summary page',
|
|
access: 'read',
|
|
example: 'opencli hltv player-summary --player 3741/niko -f json',
|
|
domain: 'www.hltv.org',
|
|
strategy: Strategy.UI,
|
|
browser: true,
|
|
navigateBefore: false,
|
|
args: [
|
|
{ name: 'player', type: 'string', default: '3741/niko', help: 'Player ref: 3741/niko or https://www.hltv.org/player/3741/niko' },
|
|
],
|
|
columns: ['playerId', 'slug', 'nickname', 'fullName', 'age', 'teamName', 'prizeMoneyUsd', 'maps', 'rating', 'roles', 'completeStatsUrl', 'url'],
|
|
func: async (page, args) => {
|
|
const { playerId, slug } = parsePlayerRef(args.player);
|
|
const url = buildPlayerUrl(args.player);
|
|
|
|
await gotoAndWait(page, url, '.playerProfile', 'hltv player summary page');
|
|
|
|
const rows = await page.evaluate((payload) => {
|
|
const clean = (value) => String(value ?? '').replace(/\s+/g, ' ').trim();
|
|
const lines = document.body.innerText.split('\n').map(clean).filter(Boolean);
|
|
const numberFrom = (value) => {
|
|
const match = String(value ?? '').replace(/,/g, '').match(/-?\d+(?:\.\d+)?/);
|
|
return match ? Number(match[0]) : null;
|
|
};
|
|
const nextAfter = (label) => {
|
|
const idx = lines.findIndex((line) => line === label);
|
|
return idx >= 0 ? lines[idx + 1] ?? null : null;
|
|
};
|
|
const statAfter = (label) => {
|
|
const idx = lines.findIndex((line) => line === label);
|
|
if (idx < 0) return null;
|
|
for (let i = idx + 1; i < Math.min(lines.length, idx + 5); i += 1) {
|
|
const n = numberFrom(lines[i]);
|
|
if (n !== null) return n;
|
|
}
|
|
return null;
|
|
};
|
|
|
|
const nickname = clean(document.querySelector('.playerNickname')?.textContent) || payload.slug;
|
|
const fullName = clean(document.querySelector('.playerRealname')?.textContent).replace(/^[-\s]+/, '') || null;
|
|
const age = numberFrom(nextAfter('Age'));
|
|
const teamName = nextAfter('Current team');
|
|
const prizeMoneyUsd = numberFrom(nextAfter('Prize money (?)'));
|
|
const completeLink = [...document.querySelectorAll('a[href]')].find((a) => /\/stats\/players\/\d+\//.test(a.getAttribute('href') || ''));
|
|
const statsHeadingIdx = lines.findIndex((line) => line === `${nickname} statistics`);
|
|
const statsPeriodLine = statsHeadingIdx >= 0 ? lines[statsHeadingIdx + 1] : null;
|
|
const statsPeriod = statsPeriodLine ? statsPeriodLine.replace(/^\(|\)$/g, '') : null;
|
|
const mapsMatch = statsPeriod ? statsPeriod.match(/([\d,]+)\s+maps/i) : null;
|
|
|
|
return [{
|
|
playerId: payload.playerId,
|
|
slug: payload.slug,
|
|
nickname,
|
|
fullName,
|
|
age,
|
|
teamName,
|
|
prizeMoneyUsd,
|
|
statsPeriod,
|
|
maps: mapsMatch ? numberFrom(mapsMatch[1]) : null,
|
|
rating: statAfter('Rating 3.0'),
|
|
firepower: statAfter('Firepower'),
|
|
entrying: statAfter('Entrying'),
|
|
trading: statAfter('Trading'),
|
|
opening: statAfter('Opening'),
|
|
clutching: statAfter('Clutching'),
|
|
sniping: statAfter('Sniping'),
|
|
utility: statAfter('Utility'),
|
|
completeStatsUrl: completeLink ? new URL(completeLink.getAttribute('href'), payload.base).toString() : null,
|
|
url: payload.url,
|
|
}];
|
|
}, { base: BASE, playerId, slug, url: url.toString() });
|
|
|
|
return assertRequiredFields(rows.map((row) => ({
|
|
playerId: row.playerId,
|
|
slug: row.slug,
|
|
nickname: row.nickname,
|
|
fullName: row.fullName,
|
|
age: parseNumber(row.age),
|
|
teamName: row.teamName,
|
|
prizeMoneyUsd: parseMoneyUsd(row.prizeMoneyUsd),
|
|
maps: parseNumber(row.maps),
|
|
rating: parseNumber(row.rating),
|
|
roles: {
|
|
statsPeriod: row.statsPeriod,
|
|
firepower: parseNumber(row.firepower),
|
|
entrying: parseNumber(row.entrying),
|
|
trading: parseNumber(row.trading),
|
|
opening: parseNumber(row.opening),
|
|
clutching: parseNumber(row.clutching),
|
|
sniping: parseNumber(row.sniping),
|
|
utility: parseNumber(row.utility),
|
|
},
|
|
completeStatsUrl: row.completeStatsUrl,
|
|
url: row.url,
|
|
})), 'hltv player-summary', ['playerId', 'slug', 'nickname', 'completeStatsUrl', 'url']);
|
|
},
|
|
});
|