Files
deckyvault/lib/version-fetchers/uptodate-api.ts
T
adrianbonpin e5f671a992 feat: add multi-strategy version fetchers, disable auto-fetch by default
- Add 4 version-fetch strategies (UpToDateCheck API, Store Page Scrape,
  Community Hub Scrape, Store API Heuristic) — no Steam API key needed
- Create orchestrator that tries all strategies, prefers named version
  over build ID
- Add test page at /test-version-fetchers for comparing strategies
- Add standalone /api/version-test endpoint (direct Steam App ID, no DB)
- Update /api/games/:id/steamdb-version to use new strategies
- Disable auto-fetch on submit page by default (NEXT_PUBLIC_VERSION_AUTO_FETCH=true to enable)
- SteamDB scraping blocked by Cloudflare; strategies preserved for later enablement
2026-05-16 20:13:23 +08:00

104 lines
2.5 KiB
TypeScript

/**
* Strategy 1: Steam UpToDateCheck API
*
* Uses the public ISteamApps/UpToDateCheck endpoint (no API key required).
* Primarily works for Valve games with dedicated servers (CS2, TF2, Dota 2, etc.).
*
* Returns:
* - required_version: numeric version (used as buildId fallback)
* - message: sometimes contains a named version like "1.41.6.1"
*
* Non-Valve games typically return { success: false }.
*/
import type { VersionFetchResult } from "./types"
const UPTODATE_URL =
"https://api.steampowered.com/ISteamApps/UpToDateCheck/v1"
interface UpToDateResponse {
response: {
success: boolean
up_to_date?: boolean
version_is_listable?: boolean
required_version?: number
message?: string
error?: string
}
}
export async function fetchUpToDateCheck(
steamAppId: number,
): Promise<VersionFetchResult> {
const source = "UpToDateCheck API"
try {
const res = await fetch(
`${UPTODATE_URL}?appid=${steamAppId}&version=0`,
{
headers: {
"User-Agent": "DeckyVault/1.0 (deckyvault.xyz; game version lookup)",
},
signal: AbortSignal.timeout(8000),
},
)
if (!res.ok) {
return {
versionString: null,
buildId: null,
source,
success: false,
error: `HTTP ${res.status}`,
}
}
const data: UpToDateResponse = await res.json()
if (!data.response.success) {
return {
versionString: null,
buildId: null,
source,
success: false,
error: data.response.error ?? "App not supported",
}
}
let versionString: string | null = null
let buildId: string | null = null
// Extract named version from message
// e.g. "Server version required: 1.41.6.1" → "1.41.6.1"
if (data.response.message) {
const namedMatch = data.response.message.match(
/(?:version|required)[:\s]+([\d.]+)/i,
)
if (namedMatch) {
versionString = namedMatch[1]
}
}
// Use required_version as buildId
if (data.response.required_version) {
buildId = String(data.response.required_version)
}
const success = !!(versionString || buildId)
return {
versionString,
buildId,
source,
success,
error: success ? undefined : "No version data in response",
}
} catch (err) {
return {
versionString: null,
buildId: null,
source,
success: false,
error: err instanceof Error ? err.message : "Unknown error",
}
}
}