Files
wehub-resource-sync 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
chore: import upstream snapshot with attribution
2026-07-13 12:39:48 +08:00

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']);
},
});