initial commit of the paytime session frontend
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
export interface SteamSummary {
|
||||
avatarUrl: string;
|
||||
personaName: string | null;
|
||||
}
|
||||
|
||||
// Steam's generic "no avatar set" question-mark image — used whenever a
|
||||
// player has no avatar, or the lookup fails for any reason.
|
||||
export const DEFAULT_AVATAR_URL =
|
||||
'https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/fe/fef49e7fa7e1997310d705b2a6158ff8dc1cdfeb_full.jpg';
|
||||
|
||||
/**
|
||||
* Fetches avatar + persona name from Steam's GetPlayerSummaries endpoint.
|
||||
* Always resolves to a usable avatarUrl (falls back to the default
|
||||
* question-mark image on any failure — including a missing STEAM_API_KEY)
|
||||
* rather than null, so callers don't need their own fallback logic.
|
||||
*
|
||||
* Cached for an hour via Next's extended fetch() — avatars rarely change,
|
||||
* and this keeps calls well within Steam's rate limits without needing a
|
||||
* separate DB cache table.
|
||||
*/
|
||||
export async function getSteamSummary(steamId64: string): Promise<SteamSummary> {
|
||||
const apiKey = process.env.STEAM_API_KEY;
|
||||
if (!apiKey) {
|
||||
return { avatarUrl: DEFAULT_AVATAR_URL, personaName: null };
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await fetch(
|
||||
`https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=${apiKey}&steamids=${steamId64}`,
|
||||
{ next: { revalidate: 3600 } },
|
||||
);
|
||||
if (!res.ok) {
|
||||
return { avatarUrl: DEFAULT_AVATAR_URL, personaName: null };
|
||||
}
|
||||
|
||||
const data = await res.json();
|
||||
const player = data?.response?.players?.[0];
|
||||
|
||||
return {
|
||||
avatarUrl: player?.avatarfull || DEFAULT_AVATAR_URL,
|
||||
personaName: player?.personaname ?? null,
|
||||
};
|
||||
} catch {
|
||||
return { avatarUrl: DEFAULT_AVATAR_URL, personaName: null };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as getSteamSummary, but for many players in one go — Steam's
|
||||
* GetPlayerSummaries accepts up to 100 comma-separated steamids per call,
|
||||
* so fetching a list of players (e.g. "who was online at this point in
|
||||
* time") is one HTTP call per 100 players rather than one per player.
|
||||
* Returns a map keyed by SteamID64; missing entries mean "use the default
|
||||
* avatar" at the call site.
|
||||
*/
|
||||
export async function getSteamSummariesBatch(
|
||||
steamId64List: string[],
|
||||
): Promise<Map<string, SteamSummary>> {
|
||||
const result = new Map<string, SteamSummary>();
|
||||
const apiKey = process.env.STEAM_API_KEY;
|
||||
if (!apiKey || steamId64List.length === 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
const chunks: string[][] = [];
|
||||
for (let i = 0; i < steamId64List.length; i += 100) {
|
||||
chunks.push(steamId64List.slice(i, i + 100));
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
chunks.map(async (chunk) => {
|
||||
try {
|
||||
const res = await fetch(
|
||||
`https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=${apiKey}&steamids=${chunk.join(',')}`,
|
||||
{ next: { revalidate: 3600 } },
|
||||
);
|
||||
if (!res.ok) return;
|
||||
|
||||
const data = await res.json();
|
||||
const players = data?.response?.players ?? [];
|
||||
for (const player of players) {
|
||||
result.set(player.steamid, {
|
||||
avatarUrl: player.avatarfull || DEFAULT_AVATAR_URL,
|
||||
personaName: player.personaname ?? null,
|
||||
});
|
||||
}
|
||||
} catch {
|
||||
// this chunk's players just fall back to the default avatar downstream
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
Reference in New Issue
Block a user