diff --git a/.gitignore b/.gitignore index 69cac2e..c1900b5 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ .venv/ __pycache__/ *.pyc +.playwright-mcp/ diff --git a/CLAUDE.md b/CLAUDE.md index 4ed97b2..f4fd738 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -9,12 +9,14 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co pip install -r requirements.txt # Run the CLI -python giftcenter.py login -python giftcenter.py redeem +python giftcenter.py login # account info (gift center) +python giftcenter.py redeem # redeem a gift code +python giftcenter.py profile # full profile: power/level/alliance +python giftcenter.py news # game announcements (no UID needed) +python giftcenter.py info # region / latest APK url (no UID needed) -# Override UID without editing .env -python giftcenter.py --uid login -python giftcenter.py --uid redeem +# Override UID without editing .env (on login/redeem/profile) +python giftcenter.py --uid profile ``` ## Configuration @@ -23,9 +25,23 @@ UID is read from the `UID` environment variable, typically set in `.env`. The `- ## Architecture -Single-file Python CLI (`giftcenter.py`) using `argparse` subcommands: +Single-file Python CLI (`giftcenter.py`) using `argparse` subcommands across two backends: +`giftcenter.last-z.com` (helper: inline `requests` calls): - `login` → `cmd_login()` — GET `/getcodeuser.php?uid=...`, displays account info - `redeem ` → `cmd_redeem()` — GET `/code.php` with `Usertoken` header and `uid`/`code` params -API responses use numeric error codes (`code` field) for login and string error codes (`errorCode` field, value `"ok"` on success) for redemption. Error mappings are in `LOGIN_ERRORS` and `REDEEM_ERRORS` constants. UI text is in Traditional Chinese. +`store.last-z.com` (`STORE_URL`): +- `profile` → `cmd_profile()` — `store_auth("getuserhome.php", uid)`, prints fields from `PROFILE_FIELDS` +- `news` → `cmd_news()` — `store_post("getsite_new.php")` (public, no auth) +- `info` → `cmd_info()` — `store_post("getindex.php")` (public, no auth) + +Two store helpers: `store_post(endpoint)` for public endpoints (empty POST body), and +`store_auth(endpoint, uid)` for private ones. **Store "auth" is just the UID** — sent as both the +`Usertoken` header and the `{uid}` POST body; there is no password. (`getuser.php` is the exception — +it needs a recaptcha `loginToken` + AES `signature`, so `profile` uses `getuserhome.php` instead.) +Adding more store reads (`getmail.php`, `getuseritem.php`, `gettask_new.php`) is a one-liner via `store_auth`. + +API responses use numeric error codes (`code` field) for login and string error codes (`errorCode` +field, value `"ok"` on success) for redemption. Error mappings are in `LOGIN_ERRORS` and +`REDEEM_ERRORS` constants. UI text is in Traditional Chinese. diff --git a/giftcenter.py b/giftcenter.py index d555a8b..632842d 100644 --- a/giftcenter.py +++ b/giftcenter.py @@ -11,6 +11,7 @@ from dotenv import load_dotenv load_dotenv() BASE_URL = "https://giftcenter.last-z.com" +STORE_URL = "https://store.last-z.com" LOGIN_ERRORS = { 10006: "Token 已過期", @@ -97,6 +98,76 @@ def cmd_redeem(args): sys.exit(1) +def store_post(endpoint): + # store.last-z.com 公開端點,免 token + try: + resp = requests.post(f"{STORE_URL}/{endpoint}", json={}, timeout=10) + resp.raise_for_status() + return resp.json() + except requests.RequestException as e: + print(f"請求失敗:{e}", file=sys.stderr) + sys.exit(1) + + +def cmd_news(args): + data = store_post("getsite_new.php") + announces = data.get("version_announce", []) + if not announces: + print("目前沒有公告") + return + for a in announces: + print(f"[{a.get('createTime')}] {a.get('title')}") + print(f" 👍 {a.get('like_count', 0)} 👎 {a.get('dislike_count', 0)}") + + +def cmd_info(args): + data = store_post("getindex.php") + print(f"地區 : {data.get('clientCountry')}") + print(f"APK 下載 : {data.get('apkurl')}") + print(f"Token : {data.get('token')}") + + +def store_auth(endpoint, uid): + # store.last-z.com 私人端點只認 UID 當 Usertoken(無密碼) + try: + resp = requests.post( + f"{STORE_URL}/{endpoint}", + headers={"Usertoken": uid}, + json={"uid": uid}, + timeout=10, + ) + return resp.json() + except (requests.RequestException, ValueError) as e: + print(f"請求失敗:{e}", file=sys.stderr) + sys.exit(1) + + +PROFILE_FIELDS = { + "name": "名稱", + "uid": "UID", + "baseLevel": "總部等級", + "power": "戰力", + "likeCount": "點贊量", + "armyKill": "殺敵數量", + "giftLevel": "送禮等級", + "kingWinNum": "稱王次數", + "allianceName": "聯盟", + "abbr": "聯盟縮寫", +} + + +def cmd_profile(args): + uid = args.uid or get_uid() + data = store_auth("getuserhome.php", uid) + if data.get("code") != 0: + print(f"讀取失敗:{data.get('message') or data.get('code')}", file=sys.stderr) + sys.exit(1) + info = data.get("info", {}) + for key, label in PROFILE_FIELDS.items(): + if key in info: + print(f" {label}\t: {info[key]}") + + def main(): parser = argparse.ArgumentParser(description="Last-Z Gift Center CLI") parser.add_argument("--uid", help="指定 UID(覆蓋 .env)") @@ -113,6 +184,19 @@ def main(): p_redeem.add_argument("--uid", help="指定 UID(覆蓋 .env)") p_redeem.set_defaults(func=cmd_redeem) + # news + p_news = sub.add_parser("news", help="查看遊戲公告") + p_news.set_defaults(func=cmd_news) + + # info + p_info = sub.add_parser("info", help="查看地區與最新 APK 下載連結") + p_info.set_defaults(func=cmd_info) + + # profile + p_profile = sub.add_parser("profile", help="顯示完整帳號資料(戰力/等級/聯盟)") + p_profile.add_argument("--uid", help="指定 UID(覆蓋 .env)") + p_profile.set_defaults(func=cmd_profile) + args = parser.parse_args() args.func(args)