118 lines
3.5 KiB
TypeScript
118 lines
3.5 KiB
TypeScript
import { ChessComGame } from "@/types/chessCom";
|
|
import { getPaddedNumber } from "./helpers";
|
|
import { LoadedGame } from "@/types/game";
|
|
|
|
export const getChessComUserRecentGames = async (
|
|
username: string,
|
|
signal?: AbortSignal
|
|
): Promise<LoadedGame[]> => {
|
|
const date = new Date();
|
|
const year = date.getUTCFullYear();
|
|
const month = date.getUTCMonth() + 1;
|
|
const paddedMonth = getPaddedNumber(month);
|
|
|
|
const res = await fetch(
|
|
`https://api.chess.com/pub/player/${username}/games/${year}/${paddedMonth}`,
|
|
{ method: "GET", signal }
|
|
);
|
|
|
|
const data = await res.json();
|
|
|
|
if (
|
|
res.status >= 400 &&
|
|
data.message !== "Date cannot be set in the future"
|
|
) {
|
|
throw new Error("Error fetching games from Chess.com");
|
|
}
|
|
|
|
const games: ChessComGame[] = data?.games ?? [];
|
|
|
|
if (games.length < 50) {
|
|
const previousMonth = month === 1 ? 12 : month - 1;
|
|
const previousPaddedMonth = getPaddedNumber(previousMonth);
|
|
const yearToFetch = previousMonth === 12 ? year - 1 : year;
|
|
|
|
const resPreviousMonth = await fetch(
|
|
`https://api.chess.com/pub/player/${username}/games/${yearToFetch}/${previousPaddedMonth}`
|
|
);
|
|
|
|
const dataPreviousMonth = await resPreviousMonth.json();
|
|
|
|
games.push(...(dataPreviousMonth?.games ?? []));
|
|
}
|
|
|
|
const gamesToReturn = games
|
|
.filter((game) => game.pgn && game.end_time)
|
|
.sort((a, b) => b.end_time - a.end_time)
|
|
.slice(0, 50)
|
|
.map(formatChessComGame);
|
|
|
|
return gamesToReturn;
|
|
};
|
|
|
|
export const getChessComUserAvatar = async (
|
|
username: string
|
|
): Promise<string | null> => {
|
|
const usernameParam = encodeURIComponent(username.trim().toLowerCase());
|
|
|
|
const res = await fetch(`https://api.chess.com/pub/player/${usernameParam}`);
|
|
const data = await res.json();
|
|
const avatarUrl = data?.avatar;
|
|
|
|
return typeof avatarUrl === "string" ? avatarUrl : null;
|
|
};
|
|
|
|
const formatChessComGame = (data: ChessComGame): LoadedGame => {
|
|
const result = data.pgn.match(/\[Result "(.*?)"]/)?.[1];
|
|
const movesNb = data.pgn.match(/\d+?\. /g)?.length;
|
|
|
|
return {
|
|
id: data.uuid || data.url?.split("/").pop() || data.id,
|
|
pgn: data.pgn || "",
|
|
white: {
|
|
name: data.white?.username || "White",
|
|
rating: data.white?.rating || 0,
|
|
title: data.white?.title,
|
|
},
|
|
black: {
|
|
name: data.black?.username || "Black",
|
|
rating: data.black?.rating || 0,
|
|
title: data.black?.title,
|
|
},
|
|
result,
|
|
timeControl: getGameTimeControl(data),
|
|
date: data.end_time
|
|
? new Date(data.end_time * 1000).toLocaleDateString()
|
|
: new Date().toLocaleDateString(),
|
|
movesNb: movesNb ? movesNb * 2 : undefined,
|
|
url: data.url,
|
|
};
|
|
};
|
|
|
|
const getGameTimeControl = (game: ChessComGame): string | undefined => {
|
|
const rawTimeControl = game.time_control;
|
|
if (!rawTimeControl) return undefined;
|
|
|
|
const [firstPart, secondPart] = rawTimeControl.split("+");
|
|
if (!firstPart) return undefined;
|
|
|
|
const timeControl = Number(firstPart);
|
|
const increment = secondPart ? `+${secondPart}` : "";
|
|
if (timeControl < 60) return `${timeControl}s${increment}`;
|
|
|
|
if (timeControl < 3600) {
|
|
const minutes = Math.floor(timeControl / 60);
|
|
const seconds = timeControl % 60;
|
|
|
|
return seconds
|
|
? `${minutes}m${getPaddedNumber(seconds)}s${increment}`
|
|
: `${minutes}m${increment}`;
|
|
}
|
|
|
|
const hours = Math.floor(timeControl / 3600);
|
|
const minutes = Math.floor((timeControl % 3600) / 60);
|
|
return minutes
|
|
? `${hours}h${getPaddedNumber(minutes)}m${increment}`
|
|
: `${hours}h${increment}`;
|
|
};
|