#!/usr/bin/env python3
"""Whos.tv 自动签到+分享+查询积分（修复CSRF版 v2）"""
from datetime import timezone, timedelta
import requests, json, os, datetime, time, urllib.parse

EMAIL = "xijingp599@gmail.com"
PASSWD = "1953607414@."
BASE = "https://whos.tv"
STATUS_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "whos_status.json")
MAX_RETRIES = 3

def save(s, m="", extra=None):
    d = {"status": s, "message": m, "time": datetime.datetime.now(timezone(timedelta(hours=8))).strftime("%Y-%m-%d %H:%M:%S")}
    if extra:
        d.update(extra)
    with open(STATUS_FILE, "w") as f:
        json.dump(d, f, ensure_ascii=False, indent=2)
    print("[" + s + "] " + m)

def safe_json(r):
    try:
        return r.json()
    except:
        return None

def refresh_xsrf(session):
    """从当前session的cookie中刷新XSRF-TOKEN"""
    xsrf = session.cookies.get("XSRF-TOKEN", "")
    decoded = urllib.parse.unquote(xsrf)
    session.headers["X-XSRF-TOKEN"] = decoded
    return decoded

def main():
    ss = requests.Session()
    ss.headers.update({
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
        "Accept": "application/json, text/plain, */*",
        "Accept-Language": "zh-CN,zh;q=0.9",
        "Referer": "https://whos.tv/",
        "Origin": "https://whos.tv",
        "Content-Type": "application/json"
    })

    for attempt in range(1, MAX_RETRIES + 1):
        try:
            save("签到中...", "正在登录 (第" + str(attempt) + "次)")

            # 1. 访问主页获取初始Cookie和XSRF-TOKEN
            ss.get(BASE, timeout=15)
            refresh_xsrf(ss)

            # 2. 登录
            r = ss.post(BASE + "/api/login", json={"username": EMAIL, "password": PASSWD}, timeout=15)
            d = safe_json(r)

            if r.status_code == 500:
                if attempt < MAX_RETRIES:
                    wait_time = 30 * attempt
                    save("重试中", "服务器500错误，" + str(wait_time) + "秒后重试")
                    time.sleep(wait_time)
                    continue
                else:
                    save("失败", "网站登录接口持续500错误")
                    return

            if d is None:
                text = r.text[:50].strip()
                save("异常", "网站返回非JSON: " + text)
                return

            if d.get("code") != 200000:
                save("失败", "登录失败: " + str(d.get("message", "")))
                return

            save("签到中...", "登录成功")

            # 3. 登录后刷新token（登录会重发新token）
            time.sleep(1)
            refresh_xsrf(ss)

            # 4. 签到
            r = ss.post(BASE + "/api/user/tasks/signin", json={}, timeout=15)
            d = safe_json(r)

            if d is None:
                save("异常", "签到接口返回异常")
                return

            sign_msg = ""
            sign_days = 0
            if d.get("code") == 200000:
                earn = d.get("data", {}).get("points_earned", 0)
                bonus = d.get("data", {}).get("streak_bonus", 0)
                days = d.get("data", {}).get("consecutive_days", 0)
                sign_days = days
                sign_msg = "签到+" + str(earn + bonus) + "分(连续" + str(days) + "天)"
            elif d.get("code") == 406008:
                sign_msg = "今日已签到"
            else:
                sign_msg = "签到失败: " + str(d.get("message", ""))

            # 5. 分享x5（每次刷新token）
            time.sleep(1)
            sc = 0
            for i in range(5):
                refresh_xsrf(ss)
                r = ss.post(BASE + "/api/user/tasks/task_share/complete", json={}, timeout=15)
                d = safe_json(r)
                if d and d.get("code") == 200000:
                    sc += 1
                time.sleep(0.5)

            # 6. 查今日积分
            time.sleep(1)
            r = ss.get(BASE + "/api/user/tasks/today-points", timeout=15)
            d = safe_json(r)
            today = d.get("data", {}).get("today_points", 0) if d else 0

            # 7. 查连续签到天数
            time.sleep(1)
            r = ss.get(BASE + "/api/user/tasks", timeout=15)
            d = safe_json(r)
            if d and sign_days == 0:
                tasks = d.get("data", {}).get("daily", [])
                for t in tasks:
                    if t.get("task_key") == "daily_signin":
                        sign_days = t.get("progress", {}).get("meta", {}).get("consecutive_days", 0)
                        break

            # 8. 查总积分
            time.sleep(1)
            r = ss.get(BASE + "/api/user/profile", timeout=15)
            d = safe_json(r)
            total_points = d.get("data", {}).get("points_balance", 0) if d and d.get("code") == 200000 else 0

            result = {
                "status": "完成",
                "message": sign_msg,
                "today_points": today,
                "total_points": total_points,
                "share_count": sc,
                "streak_days": sign_days,
                "time": datetime.datetime.now(timezone(timedelta(hours=8))).strftime("%Y-%m-%d %H:%M:%S")
            }
            with open(STATUS_FILE, "w") as f:
                json.dump(result, f, ensure_ascii=False, indent=2)

            msg = sign_msg + " | 分享" + str(sc) + "/5次(+" + str(sc * 20) + "分) | 今日" + str(today) + "分 | 连续签到" + str(sign_days) + "天 | 当前积分:" + str(total_points)
            print("[完成] " + msg)
            return

        except Exception as e:
            if attempt < MAX_RETRIES:
                wait_time = 30 * attempt
                save("重试中", "异常: " + str(e)[:50] + "，" + str(wait_time) + "秒后重试")
                time.sleep(wait_time)
            else:
                save("异常", str(e)[:200])
                return

if __name__ == "__main__":
    main()
