import os, re, time, json, datetime, requests, gradio as gr # ───────────────────── 1. Vercel API ───────────────────── TOKEN = os.getenv("SVR_TOKEN") TEAM = os.getenv("VERCEL_TEAM_ID") if not TOKEN: raise EnvironmentError("SVR_TOKEN 환경변수를 설정하세요.") API = "https://api.vercel.com" HEAD = {"Authorization": f"Bearer {TOKEN}"} # ───────────────────── 2. BEST 데이터 ──────────────────── BEST_FILE, PER_PAGE = "best_games.json", 48 def _init_best(): if not os.path.exists(BEST_FILE): json.dump([], open(BEST_FILE, "w")) def _load_best(): try: data = json.load(open(BEST_FILE)) for it in data: if "ts" not in it: it["ts"] = int(it.get("timestamp", time.time())) return data except Exception: return [] # ───────────────────── 3. NEW: 6글자 vercel.app 배포 ───── # 3. NEW: 6글자 vercel.app 배포 PAT = re.compile(r"^[a-z0-9]{6}\.vercel\.app$", re.I) def fetch_all(limit=200): """ READY 배포 중 'tmkdop.vercel.app' 처럼 6글자 도메인이 달린 것만 수집 · url 필드와 alias 배열을 모두 검색 """ try: params = {"limit": limit} if TEAM: params["teamId"] = TEAM resp = requests.get(f"{API}/v6/deployments", headers=HEAD, params=params, timeout=30) resp.raise_for_status() games = [] for d in resp.json().get("deployments", []): if d.get("state") != "READY": continue # ── url + alias 배열 통합 검사 candidates = [d.get("url", "")] candidates.extend(d.get("alias", [])) six = next((dom for dom in candidates if PAT.match(dom)), None) if not six: continue games.append({ "title": d.get("name", "(제목 없음)"), "url": f"https://{six}", "ts": int(d.get("created", time.time() * 1000) / 1000) }) return sorted(games, key=lambda x: x["ts"], reverse=True) except Exception as e: print("Vercel API 오류:", e) return [] # ───────────────────── 4. 페이지네이션 ─────────────────── def page(lst, pg): s=(pg-1)*PER_PAGE; e=s+PER_PAGE total=(len(lst)+PER_PAGE-1)//PER_PAGE return lst[s:e], total # ───────────────────── 5. HTML 3-열 그리드 ─────────────── def html(cards, pg, total): if not cards: return "
{c['title']}
{date}
Page "+str(pg)+" / "+str(total)+"
" return h # ───────────────────── 6. Gradio Blocks UI ─────────────── def build(): _init_best() with gr.Blocks(title="Vibe Game Craft", css="body{overflow-x:hidden;}") as demo: gr.Markdown("