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
92 lines
4.0 KiB
JavaScript
92 lines
4.0 KiB
JavaScript
// lichess user — fetch a public Lichess player profile.
|
|
//
|
|
// Hits `/api/user/<username>`. Returns the agent-useful slice: handle, title,
|
|
// flags (online / patron), counts, top-rated perf, profile bio.
|
|
import { cli, Strategy } from '@jackwener/opencli/registry';
|
|
import { EmptyResultError } from '@jackwener/opencli/errors';
|
|
import { LICHESS_BASE, formatTimestamp, lichessFetch, requireUsername } from './utils.js';
|
|
|
|
cli({
|
|
site: 'lichess',
|
|
name: 'user',
|
|
access: 'read',
|
|
description: 'Fetch a Lichess player profile by username (rating, perfs, counts)',
|
|
domain: 'lichess.org',
|
|
strategy: Strategy.PUBLIC,
|
|
browser: false,
|
|
args: [
|
|
{ name: 'username', positional: true, required: true, help: 'Lichess username (case-insensitive)' },
|
|
],
|
|
columns: [
|
|
'username',
|
|
'id',
|
|
'title',
|
|
'patron',
|
|
'online',
|
|
'tosViolation',
|
|
'createdAt',
|
|
'seenAt',
|
|
'gamesAll',
|
|
'gamesWin',
|
|
'gamesLoss',
|
|
'gamesDraw',
|
|
'topPerfName',
|
|
'topPerfRating',
|
|
'topPerfGames',
|
|
'fideRating',
|
|
'country',
|
|
'bio',
|
|
'url',
|
|
],
|
|
func: async (args) => {
|
|
const username = requireUsername(args.username);
|
|
const url = `${LICHESS_BASE}/api/user/${encodeURIComponent(username)}`;
|
|
const body = await lichessFetch(url, 'lichess user');
|
|
if (!body || typeof body !== 'object') {
|
|
throw new EmptyResultError('lichess user', `Lichess user "${username}" returned empty payload.`);
|
|
}
|
|
// Lichess marks closed accounts with `disabled: true` and strips data.
|
|
// Surface as EmptyResultError instead of a row of nulls (silent-fallback).
|
|
if (body.disabled === true) {
|
|
throw new EmptyResultError('lichess user', `Lichess user "${username}" is closed/disabled.`);
|
|
}
|
|
const perfs = body.perfs && typeof body.perfs === 'object' ? body.perfs : {};
|
|
// Pick the perf with the most games (excluding puzzle/storm/racer ephemera).
|
|
const playablePerfs = Object.entries(perfs).filter(([k, v]) => v && typeof v === 'object' && !['puzzle', 'storm', 'racer', 'streak'].includes(k));
|
|
let topPerfName = null;
|
|
let topPerfRating = null;
|
|
let topPerfGames = null;
|
|
for (const [name, p] of playablePerfs) {
|
|
const games = typeof p.games === 'number' ? p.games : 0;
|
|
if (topPerfGames == null || games > topPerfGames) {
|
|
topPerfName = name;
|
|
topPerfGames = games;
|
|
topPerfRating = typeof p.rating === 'number' ? p.rating : null;
|
|
}
|
|
}
|
|
const counts = body.count && typeof body.count === 'object' ? body.count : {};
|
|
const profile = body.profile && typeof body.profile === 'object' ? body.profile : {};
|
|
return [{
|
|
username: typeof body.username === 'string' ? body.username : username,
|
|
id: typeof body.id === 'string' ? body.id : null,
|
|
title: typeof body.title === 'string' ? body.title : null,
|
|
patron: body.patron === true,
|
|
online: body.online === true,
|
|
tosViolation: body.tosViolation === true,
|
|
createdAt: formatTimestamp(body.createdAt),
|
|
seenAt: formatTimestamp(body.seenAt),
|
|
gamesAll: typeof counts.all === 'number' ? counts.all : null,
|
|
gamesWin: typeof counts.win === 'number' ? counts.win : null,
|
|
gamesLoss: typeof counts.loss === 'number' ? counts.loss : null,
|
|
gamesDraw: typeof counts.draw === 'number' ? counts.draw : null,
|
|
topPerfName,
|
|
topPerfRating,
|
|
topPerfGames,
|
|
fideRating: typeof profile.fideRating === 'number' ? profile.fideRating : null,
|
|
country: typeof profile.country === 'string' ? profile.country : null,
|
|
bio: typeof profile.bio === 'string' ? profile.bio.trim() : null,
|
|
url: `${LICHESS_BASE}/@/${encodeURIComponent(typeof body.username === 'string' ? body.username : username)}`,
|
|
}];
|
|
},
|
|
});
|