"""Hammer R2 create/list until Cloudflare API stops returning 52x.""" from __future__ import annotations import json import os import time import urllib.error import urllib.request ACCT = "510534f7f6284344aadaf2f5a0794d48" BUCKET = "climperor-player-data" API = "https://api.cloudflare.com/client/v4" def creds(): email = os.environ.get("CLOUDFLARE_EMAIL") or os.environ.get( "KEYZOO_ASSET_META_USERNAME" ) key = os.environ.get("CLOUDFLARE_API_KEY") or os.environ.get( "KEYZOO_ASSET_SECRET_GLOBAL_API_KEY" ) if not email or not key: raise SystemExit("missing credentials") return email, key def call(method: str, path: str, body: dict | None = None) -> tuple[int, dict | str]: email, key = creds() data = None if body is None else json.dumps(body).encode() req = urllib.request.Request( API + path, data=data, method=method, headers={ "X-Auth-Email": email, "X-Auth-Key": key, "Content-Type": "application/json", "User-Agent": "climperor-retry-r2", }, ) try: with urllib.request.urlopen(req, timeout=60) as resp: return resp.status, json.loads(resp.read().decode()) except urllib.error.HTTPError as e: raw = e.read().decode("utf-8", errors="replace") try: return e.code, json.loads(raw) except Exception: return e.code, raw[:120] def main() -> int: list_path = f"/accounts/{ACCT}/r2/buckets" get_path = f"/accounts/{ACCT}/r2/buckets/{BUCKET}" for i in range(20): code, payload = call("GET", get_path) print(f"[{i+1}] GET {BUCKET} -> {code}", flush=True) if code == 200: print("bucket ready", flush=True) return 0 if code == 404 or (isinstance(payload, dict) and not payload.get("success")): # not found → create code2, payload2 = call("POST", list_path, {"name": BUCKET}) print(f"[{i+1}] POST create -> {code2}", flush=True) if code2 in (200, 201): print("created", flush=True) return 0 text = str(payload2).lower() if "already exists" in text or "10004" in text: print("exists", flush=True) return 0 # also try list code3, payload3 = call("GET", list_path) names = [] if isinstance(payload3, dict): buckets = (payload3.get("result") or {}).get("buckets") or payload3.get( "result" ) if isinstance(buckets, list): for b in buckets: if isinstance(b, dict): names.append(b.get("name")) print(f"[{i+1}] LIST -> {code3} names={names}", flush=True) if BUCKET in names: print("bucket ready via list", flush=True) return 0 time.sleep(5 + (i % 5)) return 1 if __name__ == "__main__": raise SystemExit(main())