(() => { "use strict"; const recentList = document.getElementById("steamRecent"); const topList = document.getElementById("steamTop"); const updated = document.getElementById("steamUpdated"); const refresh = document.getElementById("steamRefresh"); const nowLabel = document.getElementById("steamNowLabel"); const nowBody = document.getElementById("steamNowBody"); const countEl = document.getElementById("steamCount"); if (!recentList || !topList || !updated) return; const SOURCE = "/api/steam/activity"; const POLL_MS = 60000; let timer = null; let busy = false; const escapeHtml = (s) => String(s).replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'", })[c]); const IMG_FALLBACK = "data:image/svg+xml," + encodeURIComponent( "" + "" + "🎮" ); const fmtHours = (h) => { if (h == null) return ""; if (h >= 168) return `${(h / 24).toFixed(1)} д`; if (h >= 1) return `${h.toFixed(1)} ч`; return `${Math.round(h * 60)} мин`; }; const fmtDate = (ts) => { if (!ts) return ""; const d = new Date(ts * 1000); const today = new Date(); today.setHours(0, 0, 0, 0); if (d >= today) return "сегодня"; if (d >= new Date(today.getTime() - 86400000)) return "вчера"; return d.toLocaleDateString("ru-RU", { day: "2-digit", month: "2-digit", year: "numeric" }); }; const bindCovers = (root) => { root.querySelectorAll("img.st-cover").forEach((img) => { img.addEventListener("error", () => { img.src = IMG_FALLBACK; }, { once: true }); }); }; const rowHtml = (g, right, sub) => `
  • ${escapeHtml(g.name)} ${sub} ${right}
  • `; const emptyStatus = (closed) => ({ html: closed ? "Детали игр скрыты в настройках приватности Steam" : "Пока нет данных", cls: closed ? "ym-status ym-error" : "ym-status", }); const renderGames = (data) => { const closed = Boolean(data.profile && data.profile.public === false); const recentHtml = (data.recent || []).map((g) => { const parts = []; if (g.hours_total) parts.push(`всего ${fmtHours(g.hours_total)}`); const last = fmtDate(g.last_played); if (last) parts.push(`играл ${last}`); const right = g.hours_2weeks != null ? `${fmtHours(g.hours_2weeks)} / 2 нед` : ""; return rowHtml(g, right, parts.join(" · ") || "—"); }).join(""); const topHtml = (data.top || []).map((g) => { const last = fmtDate(g.last_played); const sub = last ? `играл ${last}` : "—"; return rowHtml(g, fmtHours(g.hours_total), sub); }).join(""); const recentEmpty = emptyStatus(closed); const topEmpty = emptyStatus(closed); recentList.innerHTML = recentHtml || `
  • ${recentEmpty.html}
  • `; topList.innerHTML = topHtml || `
  • ${topEmpty.html}
  • `; bindCovers(recentList); bindCovers(topList); if (countEl) countEl.textContent = data.game_count ? `всего игр: ${data.game_count}` : ""; }; const renderNow = (data) => { if (!nowBody) return; const now = data.now; if (now) { if (nowLabel) nowLabel.innerHTML = 'сейчас в игре'; const name = escapeHtml(now.name || "Steam"); nowBody.innerHTML = ` ${name} открыть в Steam `; bindCovers(nowBody); } else { if (nowLabel) nowLabel.innerHTML = 'статус'; const state = data.state || ""; const text = !state || state === "не в сети" ? "не в сети" : `не в игре · ${state}`; nowBody.innerHTML = `${escapeHtml(text)}`; } }; const showError = (msg) => { const html = `
  • ${escapeHtml(msg)}
  • `; recentList.innerHTML = html; topList.innerHTML = html; if (nowBody) nowBody.innerHTML = `${escapeHtml(msg)}`; updated.textContent = "ошибка"; }; const tick = async () => { if (busy) return; busy = true; try { const res = await fetch(SOURCE, { headers: { Accept: "application/json" } }); const data = await res.json(); if (!res.ok || !data.ok) { showError(data.error || `Ошибка ${res.status}`); return; } renderGames(data); renderNow(data); const t = new Date(data.updated_at * 1000); updated.textContent = `обновлено ${t.toLocaleTimeString("ru-RU")}${data.stale ? " (кэш)" : ""}`; } catch (e) { showError("Нет связи с сервером"); } finally { busy = false; if (timer) clearTimeout(timer); timer = setTimeout(tick, POLL_MS); } }; const poke = () => { if (timer) clearTimeout(timer); tick(); }; if (refresh) refresh.addEventListener("click", poke); document.addEventListener("visibilitychange", () => { if (!document.hidden) poke(); }); tick(); })();