Monitor Valve reservation packages

This commit is contained in:
seanbetts committed 2026-05-10 21:26:46 +01:00
1 parent d293ea04b6
commit 37337c630c
2 files changed
+252 -4

No files matched your search

+129 -4
View File
@@ -14,6 +14,7 @@ mkdir -p "$OUT_DIR" "$REPORT_DIR"
ERROR_FILE="$REPORT_DIR/valve-errors.txt"
: > "$ERROR_FILE"
: > "$REPORT_DIR/valve-key-lines.txt"
: > "$REPORT_DIR/valve-reservation-packages.tsv"
fetch_page() {
name="$1"
@@ -25,15 +26,139 @@ fetch_page() {
fi
}
fetch_json() {
name="$1"
url="$2"
if ! curl -A 'Mozilla/5.0' --retry 2 --retry-delay 2 --max-time 45 -fsSL "$url" > "$OUT_DIR/$name.json" 2>/dev/null; then
printf '%s\n' "failed to fetch $url" >> "$ERROR_FILE"
rm -f "$OUT_DIR/$name.json"
return 1
fi
}
fetch_page "steamdeck-site" "https://www.steamdeck.com/" || true
fetch_page "steam-support-deck" "https://help.steampowered.com/en/wizard/HelpWithSteamDeck" || true
fetch_page "steam-controller-store" "https://store.steampowered.com/app/4165870/" || true
fetch_page "steam-hardware-root" "https://steampowered.com/hardware" || true
rg -n "Steam Controller|Steam Machine|Steam Frame|coming soon|manual|safety|warranty|preorder|release" \
"$OUT_DIR" \
> "$REPORT_DIR/valve-key-lines.txt" || true
for appid in 4165870 4165890 4165910; do
fetch_json "appdetails-$appid-us" "https://store.steampowered.com/api/appdetails?appids=$appid&cc=us&l=en" || true
done
for packageid in 1558609 1629446 1629447 1629458 1629460 1629484 1629486; do
fetch_json "packagedetails-$packageid-us" "https://store.steampowered.com/api/packagedetails?packageids=$packageid&cc=us&l=en" || true
done
python3 - "$OUT_DIR" "$REPORT_DIR/valve-reservation-packages.tsv" <<'PY'
import json
import sys
from pathlib import Path
out_dir = Path(sys.argv[1])
report_path = Path(sys.argv[2])
apps = {
"4165870": "Steam Controller",
"4165890": "Steam Frame",
"4165910": "Steam Machine",
}
packages = {
"1558609": "Steam Controller",
"1629446": "Steam Machine",
"1629447": "Steam Machine",
"1629458": "Steam Machine",
"1629460": "Steam Machine",
"1629484": "Steam Frame",
"1629486": "Steam Frame",
}
def clean(value):
if value is None:
return ""
return str(value).replace("\t", " ").replace("\n", " ").strip()
def read_json(path):
if not path.exists():
return None, "missing"
try:
return json.loads(path.read_text(encoding="utf-8")), ""
except Exception as exc:
return None, f"invalid json: {exc}"
rows = [["type", "product", "id", "status", "name", "release_or_price", "details"]]
for appid, product in apps.items():
payload, error = read_json(out_dir / f"appdetails-{appid}-us.json")
entry = payload.get(appid, {}) if isinstance(payload, dict) else {}
success = entry.get("success") is True
data = entry.get("data") if isinstance(entry.get("data"), dict) else {}
status = "public" if success else "unavailable"
if error:
status = error
name = data.get("name", "")
price = ""
price_overview = data.get("price_overview")
if isinstance(price_overview, dict):
price = price_overview.get("final_formatted", "")
release_date = data.get("release_date")
release_or_price = price
coming_soon = ""
if isinstance(release_date, dict):
release_or_price = release_or_price or release_date.get("date", "")
coming_soon = release_date.get("coming_soon", "")
package_ids = data.get("packages") if isinstance(data.get("packages"), list) else []
package_groups = data.get("package_groups") if isinstance(data.get("package_groups"), list) else []
details = (
f"packages={','.join(str(package_id) for package_id in package_ids)}; "
f"package_groups={len(package_groups)}; "
f"coming_soon={coming_soon}; "
f"price={price}"
)
rows.append(["app", product, appid, status, name, release_or_price, details])
for packageid, product in packages.items():
payload, error = read_json(out_dir / f"packagedetails-{packageid}-us.json")
entry = payload.get(packageid, {}) if isinstance(payload, dict) else {}
success = entry.get("success") is True
status = "public" if success else "private"
if error:
status = error
data = entry.get("data") if isinstance(entry.get("data"), dict) else {}
name = data.get("name", "")
price = ""
package_price = data.get("price")
if isinstance(package_price, dict):
price = package_price.get("final_formatted", "")
apps_detail = []
for app in data.get("apps", []) if isinstance(data.get("apps"), list) else []:
if isinstance(app, dict):
app_id = app.get("id", "")
app_name = app.get("name", "")
apps_detail.append(f"{app_id}:{app_name}")
details = f"apps={','.join(apps_detail)}" if success else "packagedetails success:false"
rows.append(["package", product, packageid, status, name, price, details])
report_path.write_text(
"\n".join("\t".join(clean(value) for value in row) for row in rows) + "\n",
encoding="utf-8",
)
PY
{
printf '%s\n' "Reservation package API snapshot:"
sed -n '1,40p' "$REPORT_DIR/valve-reservation-packages.tsv"
printf '\n%s\n' "HTML key matches:"
} > "$REPORT_DIR/valve-key-lines.txt"
rg -n -o -g '*.html' "Steam Controller|Steam Machine|Steam Frame|coming soon|manual|safety|warranty|preorder|release" \
"$OUT_DIR" | sort -u \
>> "$REPORT_DIR/valve-key-lines.txt" || true
printf '%s\n' \
"Saved Valve pages to $OUT_DIR" \
"Saved matching lines to $REPORT_DIR/valve-key-lines.txt"
"Saved matching lines to $REPORT_DIR/valve-key-lines.txt" \
"Saved reservation package report to $REPORT_DIR/valve-reservation-packages.tsv"
+123
View File
@@ -0,0 +1,123 @@
import os
import stat
import subprocess
import textwrap
import unittest
from pathlib import Path
from tempfile import TemporaryDirectory
ROOT = Path(__file__).resolve().parents[1]
def write_executable(path, content):
path.write_text(textwrap.dedent(content).lstrip(), encoding="utf-8")
path.chmod(path.stat().st_mode | stat.S_IXUSR)
class CheckValveEndpointsTests(unittest.TestCase):
def test_fetches_hardware_app_and_reservation_package_details(self):
tmp = TemporaryDirectory()
self.addCleanup(tmp.cleanup)
tmp_path = Path(tmp.name)
run_dir = tmp_path / "run"
fake_bin = tmp_path / "bin"
fake_bin.mkdir()
write_executable(
fake_bin / "curl",
"""
#!/bin/sh
for arg in "$@"; do
url="$arg"
done
case "$url" in
*appdetails*appids=4165870*)
printf '%s\\n' '{"4165870":{"success":true,"data":{"name":"Steam Controller","release_date":{"date":"May 4, 2026"},"packages":[1558609],"price_overview":{"final_formatted":"$99.00"},"package_groups":[{"name":"default","subs":[{"packageid":1558609}]}]}}}'
;;
*appdetails*appids=4165890*)
printf '%s\\n' '{"4165890":{"success":true,"data":{"name":"Steam Frame","release_date":{"coming_soon":true,"date":"Coming soon"},"package_groups":[]}}}'
;;
*appdetails*appids=4165910*)
printf '%s\\n' '{"4165910":{"success":true,"data":{"name":"Steam Machine","release_date":{"coming_soon":true,"date":"Coming soon"},"package_groups":[]}}}'
;;
*packagedetails*packageids=1558609*)
printf '%s\\n' '{"1558609":{"success":true,"data":{"name":"Steam Controller","apps":[{"id":4165870,"name":"Steam Controller"}],"price":{"final_formatted":"$99.00"}}}}'
;;
*packagedetails*packageids=*)
package="${url##*=}"
printf '{"%s":{"success":false}}\\n' "$package"
;;
*)
printf '%s\\n' '<html><body>Steam Frame Steam Machine Steam Controller coming soon</body></html>'
;;
esac
""",
)
write_executable(
fake_bin / "rg",
"""
#!/bin/sh
exit 1
""",
)
env = os.environ.copy()
env.update({"PATH": f"{fake_bin}:{env['PATH']}"})
result = subprocess.run(
[str(ROOT / "scripts" / "check_valve_endpoints.sh"), str(run_dir)],
cwd=ROOT,
env=env,
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
check=False,
)
self.assertEqual("", result.stderr)
self.assertEqual(0, result.returncode)
valve_dir = run_dir / "api" / "valve"
for appid in ("4165870", "4165890", "4165910"):
self.assertTrue((valve_dir / f"appdetails-{appid}-us.json").exists())
for packageid in (
"1558609",
"1629446",
"1629447",
"1629458",
"1629460",
"1629484",
"1629486",
):
self.assertTrue((valve_dir / f"packagedetails-{packageid}-us.json").exists())
report = (run_dir / "reports" / "valve-reservation-packages.tsv").read_text(
encoding="utf-8"
)
self.assertIn(
"package\tSteam Controller\t1558609\tpublic\tSteam Controller\t$99.00",
report,
)
self.assertIn(
"package\tSteam Machine\t1629446\tprivate\t\t\tpackagedetails success:false",
report,
)
self.assertIn(
"package\tSteam Frame\t1629484\tprivate\t\t\tpackagedetails success:false",
report,
)
self.assertIn("app\tSteam Machine\t4165910\tpublic\tSteam Machine\tComing soon", report)
self.assertIn("app\tSteam Frame\t4165890\tpublic\tSteam Frame\tComing soon", report)
key_lines = (run_dir / "reports" / "valve-key-lines.txt").read_text(
encoding="utf-8"
)
self.assertIn("Reservation package API snapshot:", key_lines)
self.assertIn("package\tSteam Frame\t1629486\tprivate", key_lines)
if __name__ == "__main__":
unittest.main()