Initial commit

This commit is contained in:
2026-06-13 23:22:53 +01:00
commit ea1eccb4a5
7 changed files with 389 additions and 0 deletions
+84
View File
@@ -0,0 +1,84 @@
#!/usr/bin/env python
"""Log in to the West Wood Club (PerfectGym) API and print the bearer token.
The token is written to stdout (and nothing else), so it can be piped to a file:
python login.py > token.txt
Credentials are read from the WESTWOOD_EMAIL and WESTWOOD_PASSWORD environment
variables; any that are missing are prompted for on the terminal. Prompts and
errors go to stderr, keeping stdout clean for the token.
"""
import getpass
import json
import os
import sys
import urllib.error
import urllib.request
BASE_URL = "https://goapi2.perfectgym.com"
WHITE_LABEL_ID = "7d073db5-0ef8-4d78-89ec-4a8bebaf4cbc"
USER_AGENT = (
"West Wood Club/1.28.3.0 "
"(com.perfectgym.perfectgymgo2.westwoodclub; build:1028003000; Android 16)"
)
def log_in(email: str, password: str) -> str:
"""Return the bearer token (the value for the Authorization header)."""
body = json.dumps(
{
"email": email,
"password": password,
"clientApplicationInfo": {
"type": "whitelabel",
"whiteLabelId": WHITE_LABEL_ID,
},
}
).encode()
request = urllib.request.Request(
f"{BASE_URL}/v1/Authorize/LogInWithEmail",
data=body,
method="POST",
headers={
"Accept": "application/json",
"Content-Type": "application/json; charset=UTF-8",
"Accept-Language": "en",
"X-Go-App-Platform": "Android",
"X-Go-App-Version": "1.28.3",
"X-Go-White-Label-ID": WHITE_LABEL_ID,
"User-Agent": USER_AGENT,
},
)
with urllib.request.urlopen(request) as response:
payload = json.load(response)
if payload.get("errors"):
raise SystemExit(f"login failed: {payload['errors']}")
data = payload.get("data") or {}
token = data.get("token")
if not token:
raise SystemExit(f"no token in response: {payload}")
return token
def main() -> None:
email = os.environ.get("WESTWOOD_EMAIL") or input("Email: ")
password = os.environ.get("WESTWOOD_PASSWORD") or getpass.getpass("Password: ")
try:
token = log_in(email, password)
except urllib.error.HTTPError as exc:
raise SystemExit(f"HTTP {exc.code}: {exc.read().decode('utf-8', 'replace')}")
except urllib.error.URLError as exc:
raise SystemExit(f"request failed: {exc.reason}")
print(token)
if __name__ == "__main__":
main()