# Source: https://github.com/CN-Grace/QinglongScripts/blob/main/Tieba.py
# Raw: https://raw.githubusercontent.com/CN-Grace/QinglongScripts/main/Tieba.py
# Repo: CN-Grace/QinglongScripts
# Path: Tieba.py
# UploadedAt: 2026-05-23T15:48:24Z
# SHA256: 9b1ac22330324c1b5579826fd6202a49e6e4aa857d1819be3cd7073dd7c092c2
# Category: web版/账密
# Evidence: web/H5关键词 + username/password/login

#!/usr/bin/env python3
"""
cron: 0 0 * * *
new Env("百度贴吧签到")
百度贴吧 每日自动签到脚本
- 获取用户登录状态
- 获取关注的贴吧列表（含等级、经验值）
- 对每个贴吧进行签到
- 统计签到结果和等级分布
"""

import hashlib
import os
import random
import time
import requests
from typing import Optional, List, Dict, Any

from utils import log_info, log_success, log_warning, log_error, beijing_time_str
from notifier import send as notify_send

# ==================== 用户配置 ====================
TIEBA_COOKIE = os.environ.get("TIEBA_COOKIE", "")


def create_session(cookie: str) -> requests.Session:
    """创建带 Cookie 的 requests.Session（贴吧专用 User-Agent 和 Host）"""
    session = requests.Session()
    session.headers.update({
        "Host": "tieba.baidu.com",
        "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36",
        "Connection": "keep-alive",
        "Accept-Encoding": "gzip, deflate",
        "Cache-Control": "no-cache",
    })
    cookie_dict = {item.split("=")[0]: item.split("=")[1] for item in cookie.split("; ") if "=" in item}
    requests.utils.add_dict_to_cookiejar(session.cookies, cookie_dict)
    return session


def encode_data(data: Dict, sign_key: str = "tiebaclient!!!") -> Dict:
    """对请求数据进行签名"""
    s = ""
    for key in sorted(data.keys()):
        s += f"{key}={data[key]}"
    sign = hashlib.md5((s + sign_key).encode("utf-8")).hexdigest().upper()
    data.update({"sign": sign})
    return data


def request(session: requests.Session, url: str, method: str = "get", data: Optional[Dict] = None, retry: int = 3) -> Dict:
    """带重试的请求函数"""
    for i in range(retry):
        try:
            if method.lower() == "get":
                response = session.get(url, timeout=10)
            else:
                response = session.post(url, data=data, timeout=10)
            response.raise_for_status()
            if not response.text.strip():
                raise ValueError("空响应内容")
            return response.json()
        except Exception as e:
            if i == retry - 1:
                raise Exception(f"请求失败: {e!s}")
            wait_time = 1.5 * (2 ** i) + random.uniform(0, 1)
            time.sleep(wait_time)
    raise Exception(f"请求失败，已达最大重试次数 {retry}")


# ---------- 核心功能 ----------
def get_user_info(session: requests.Session) -> tuple:
    """获取用户登录信息，返回 (tbs, user_name) 或 (False, 错误信息)"""
    try:
        result = request(session, "http://tieba.baidu.com/dc/common/tbs")
        if result.get("is_login", 0) == 0:
            return False, "登录失败，Cookie 异常"
        tbs = result.get("tbs", "")
        try:
            user_info = request(session, "https://tieba.baidu.com/f/user/json_userinfo")
            user_data = user_info.get("data", "")
            user_name = user_data.get("show_nickname", "未知用户") if isinstance(user_data, dict) else "未知用户"
        except Exception:
            user_name = "未知用户"
        return tbs, user_name
    except Exception as e:
        return False, f"登录验证异常: {e}"


def get_favorite(session: requests.Session, bduss: str) -> List[Dict]:
    """获取用户关注的贴吧列表，包含等级信息"""
    forums = []
    page_no = 1
    like_url = "http://c.tieba.baidu.com/c/f/forum/like"

    while True:
        data = encode_data({
            "BDUSS": bduss,
            "_client_type": "2",
            "_client_id": "wappc_1534235498291_488",
            "_client_version": "9.7.8.0",
            "_phone_imei": "000000000000000",
            "from": "1008621y",
            "page_no": str(page_no),
            "page_size": "200",
            "model": "MI+5",
            "net_type": "1",
            "timestamp": str(int(time.time())),
            "vcode_tag": "11",
        })

        try:
            res = request(session, like_url, "post", data)
            if "forum_list" in res:
                for forum_type in ["non-gconforum", "gconforum"]:
                    if forum_type in res["forum_list"]:
                        items = res["forum_list"][forum_type]
                        if isinstance(items, list):
                            for f in items:
                                f["_is_signed"] = (forum_type == "gconforum")
                            forums.extend(items)
                        elif isinstance(items, dict):
                            items["_is_signed"] = (forum_type == "gconforum")
                            forums.append(items)
            if res.get("has_more") != "1":
                break
            page_no += 1
            time.sleep(random.uniform(1, 2))
        except Exception as e:
            log_error(f"获取贴吧列表出错: {e}")
            break

    log_info(f"共获取到 {len(forums)} 个关注的贴吧")
    return forums


def build_level_summary(forums: List[Dict]) -> Dict:
    """从贴吧列表中提取等级汇总"""
    total_exp = 0
    level_stats = {}
    forum_levels = []

    for f in forums:
        name = f.get("name", "")
        level_id = int(f.get("level_id", 0))
        cur_score = int(f.get("cur_score", 0))
        levelup_score = int(f.get("levelup_score", 0))
        total_exp += cur_score

        if level_id not in level_stats:
            level_stats[level_id] = {"count": 0, "total_exp": 0}
        level_stats[level_id]["count"] += 1
        level_stats[level_id]["total_exp"] += cur_score

        forum_levels.append({
            "name": name,
            "level_id": level_id,
            "cur_score": cur_score,
            "levelup_score": levelup_score,
            "exp_percent": round(cur_score / levelup_score * 100, 1) if levelup_score > 0 else 0,
        })

    sorted_levels = sorted(level_stats.items(), key=lambda x: x[0], reverse=True)
    return {
        "forum_count": len(forums),
        "total_exp": total_exp,
        "level_stats": sorted_levels,
        "forum_levels": forum_levels,
    }


def sign_forums(session: requests.Session, bduss: str, forums: List[Dict], tbs: str) -> Dict[str, Any]:
    """对贴吧列表进行签到"""
    success_count = 0
    error_count = 0
    exist_count = 0
    shield_count = 0
    total = len(forums)
    details = []

    log_info(f"开始签到 {total} 个贴吧")

    base_data = {
        "_client_type": "2",
        "_client_version": "9.7.8.0",
        "_phone_imei": "000000000000000",
        "model": "MI+5",
        "net_type": "1",
    }

    last_request_time = time.time()
    for idx, forum in enumerate(forums):
        elapsed = time.time() - last_request_time
        delay = max(0, 1.0 + random.uniform(0.5, 1.5) - elapsed)
        time.sleep(delay)
        last_request_time = time.time()

        if (idx + 1) % 10 == 0:
            extra_delay = random.uniform(5, 10)
            log_info(f"已签到 {idx + 1}/{total} 个贴吧，休息 {extra_delay:.2f} 秒")
            time.sleep(extra_delay)

        forum_name = forum.get("name", "")
        forum_id = forum.get("id", "")
        level_id = forum.get("level_id", "?")
        log_prefix = f"【{forum_name}】吧(Lv.{level_id})({idx + 1}/{total})"

        try:
            data = base_data.copy()
            data.update({"BDUSS": bduss, "fid": forum_id, "kw": forum_name, "tbs": tbs, "timestamp": str(int(time.time()))})
            data = encode_data(data)

            result = request(session, "http://c.tieba.baidu.com/c/c/forum/sign", "post", data)
            error_code = result.get("error_code", "")
            rank = None

            if error_code == "0":
                success_count += 1
                if "user_info" in result:
                    rank = result["user_info"].get("user_sign_rank")
                    log_success(f"{log_prefix} 签到成功，第{rank}个签到" if rank else f"{log_prefix} 签到成功")
                else:
                    log_success(f"{log_prefix} 签到成功")
                details.append({"name": forum_name, "status": "success", "rank": rank, "level": level_id})
            elif error_code == "160002":
                exist_count += 1
                log_warning(f"{log_prefix} {result.get('error_msg', '今日已签到')}")
                details.append({"name": forum_name, "status": "exist", "rank": None, "level": level_id})
            elif error_code == "340006":
                shield_count += 1
                log_warning(f"{log_prefix} 贴吧已被屏蔽")
                details.append({"name": forum_name, "status": "shield", "rank": None, "level": level_id})
            else:
                error_count += 1
                log_error(f"{log_prefix} 签到失败，错误: {result.get('error_msg', '未知错误')}")
                details.append({"name": forum_name, "status": "error", "rank": None, "level": level_id})
        except Exception as e:
            error_count += 1
            log_error(f"{log_prefix} 签到异常: {e!s}")
            details.append({"name": forum_name, "status": "error", "rank": None, "level": level_id})

    return {"total": total, "success": success_count, "exist": exist_count, "shield": shield_count, "error": error_count, "details": details}


def build_report(stats: Dict, user_name: str, details: List[Dict], level_summary: Optional[Dict] = None) -> str:
    """构建签到报告"""
    lines = ["📢 百度贴吧 签到报告", "", f"👤 账号: {user_name}", ""]

    lines.append("📊 签到统计")
    lines.append(f"📌 贴吧总数: {stats['total']}")
    lines.append(f"✅ 签到成功: {stats['success']}")
    lines.append(f"⚠️ 已经签到: {stats['exist']}")
    lines.append(f"🚫 被屏蔽的: {stats['shield']}")
    lines.append(f"❌ 签到失败: {stats['error']}")

    if level_summary:
        lines.append("")
        lines.append("🎯 等级汇总")
        lines.append(f"📈 总经验值: {level_summary['total_exp']:,}")
        top_levels = level_summary["level_stats"][:5]
        level_dist = "  |  ".join([f"Lv.{lv}: {info['count']}个吧" for lv, info in top_levels])
        lines.append(f"🏅 等级分布: {level_dist}")

    if details:
        lines.append("")
        lines.append("📋 详细签到情况")
        for d in details:
            name = d["name"]
            status = d["status"]
            rank = d.get("rank")
            level = d.get("level", "?")
            if status == "success":
                emoji = "✅"
                rank_text = f" (第{rank}个)" if rank else ""
            elif status == "exist":
                emoji = "⚠️"; rank_text = ""
            elif status == "shield":
                emoji = "🚫"; rank_text = ""
            else:
                emoji = "❌"; rank_text = ""
            lines.append(f"{emoji} {name} Lv.{level}{rank_text}")

    lines.append("")
    lines.append("─" * 18)
    lines.append(f"🕒 执行时间: {beijing_time_str()}")
    return "\n".join(lines)


def main() -> Dict:
    """主流程"""
    cookie_dict = {item.split("=")[0]: item.split("=")[1] for item in TIEBA_COOKIE.split("; ") if "=" in item}
    bduss = cookie_dict.get("BDUSS", "")
    if not bduss:
        log_error("Cookie 中未找到 BDUSS，请检查配置")
        return {"user_name": "未知", "stats": {"total": 0, "success": 0, "exist": 0, "shield": 0, "error": 0}, "details": [], "level_summary": None}

    session = create_session(TIEBA_COOKIE)

    tbs, user_name = get_user_info(session)
    if not tbs:
        log_error(user_name)
        return {"user_name": "登录失败", "stats": {"total": 0, "success": 0, "exist": 0, "shield": 0, "error": 0}, "details": [], "level_summary": None}

    log_success(f"登录成功，用户名: {user_name}")

    forums = get_favorite(session, bduss=bduss)
    if not forums:
        log_warning("未获取到任何贴吧，请检查 Cookie 或网络")
        return {"user_name": user_name, "stats": {"total": 0, "success": 0, "exist": 0, "shield": 0, "error": 0}, "details": [], "level_summary": None}

    level_summary = build_level_summary(forums)
    log_info(f"总经验值: {level_summary['total_exp']:,}，最高等级: Lv.{level_summary['level_stats'][0][0] if level_summary['level_stats'] else '?'}")

    result = sign_forums(session, bduss, forums, tbs)
    log_info(f"签到完成: 总数 {result['total']}，成功 {result['success']}，已签 {result['exist']}，屏蔽 {result['shield']}，失败 {result['error']}")

    return {"user_name": user_name, "stats": result, "details": result["details"], "level_summary": level_summary}


if __name__ == "__main__":
    result = main()
    if result:
        report = build_report(result["stats"], result["user_name"], result["details"], result.get("level_summary"))
        notify_send("百度贴吧 签到报告", report)
