225 lines
7.2 KiB
Python
225 lines
7.2 KiB
Python
# Source: https://github.com/CN-Grace/QinglongScripts/blob/main/Heybox.py
|
|
# Raw: https://raw.githubusercontent.com/CN-Grace/QinglongScripts/main/Heybox.py
|
|
# Repo: CN-Grace/QinglongScripts
|
|
# Path: Heybox.py
|
|
# UploadedAt: 2026-05-24T03:44:01Z
|
|
# SHA256: cf7af03059bd3d2d7e1afc7c76282554853e47ce7576ceaadfb124683364afc1
|
|
# Category: web版/抓包
|
|
# Evidence: web/H5关键词 + cookie/token/header
|
|
|
|
#!/usr/bin/env python3
|
|
"""
|
|
cron: 0 8 * * *
|
|
new Env("小黑盒每日签到")
|
|
小黑盒 (Heybox) 每日签到脚本
|
|
- 获取签到日历和任务列表
|
|
- 执行每日签到
|
|
- 推送签到结果和奖励信息
|
|
"""
|
|
|
|
import os
|
|
import random
|
|
import string
|
|
import requests
|
|
from datetime import datetime, timezone, timedelta
|
|
|
|
from utils import log_info, log_success, log_warning, log_error, beijing_time_str
|
|
from notifier import send as notify_send
|
|
|
|
# ==================== 用户配置 ====================
|
|
HEYBOX_COOKIE = os.environ.get("HEYBOX_COOKIE", "") # pkey=...;x_xhh_tokenid=...
|
|
HEYBOX_ID = os.environ.get("HEYBOX_ID", "") # 小黑盒用户 ID
|
|
IMEI = os.environ.get("HEYBOX_IMEI", "") # 设备标识
|
|
|
|
API_BASE = "https://api.xiaoheihe.cn"
|
|
TZ_BEIJING = timezone(timedelta(hours=8))
|
|
|
|
|
|
def _random_str(length: int = 32) -> str:
|
|
chars = string.ascii_letters + string.digits
|
|
return "".join(random.choice(chars) for _ in range(length))
|
|
|
|
|
|
def _random_hex(length: int = 8) -> str:
|
|
return "".join(random.choice("0123456789ABCDEF") for _ in range(length))
|
|
|
|
|
|
def create_session() -> requests.Session:
|
|
session = requests.Session()
|
|
session.headers.update({
|
|
"User-Agent": "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.118 Safari/537.36 ApiMaxJia/1.0",
|
|
"Accept-Encoding": "gzip",
|
|
})
|
|
if HEYBOX_COOKIE:
|
|
for item in HEYBOX_COOKIE.split(";"):
|
|
item = item.strip()
|
|
if "=" in item:
|
|
k, v = item.split("=", 1)
|
|
session.cookies.set(k.strip(), v.strip(), domain="xiaoheihe.cn")
|
|
return session
|
|
|
|
|
|
def build_params() -> str:
|
|
"""构建通用 Query 参数"""
|
|
heybox_id = HEYBOX_ID or _extract_from_cookie("heybox_id", "30182259")
|
|
imei = IMEI or _extract_from_cookie("imei", "a9381821da647661")
|
|
params = [
|
|
f"heybox_id={heybox_id}",
|
|
f"imei={imei}",
|
|
"device_info=25102RKBEC",
|
|
f"nonce={_random_str(32)}",
|
|
f"hkey={_random_hex(8)}",
|
|
"os_type=Android",
|
|
"x_os_type=Android",
|
|
"x_client_type=mobile",
|
|
"os_version=16",
|
|
"version=1.3.382",
|
|
"build=1076",
|
|
]
|
|
return "&".join(params)
|
|
|
|
|
|
def _extract_from_cookie(key: str, default: str = "") -> str:
|
|
for item in HEYBOX_COOKIE.split(";"):
|
|
item = item.strip()
|
|
if "=" in item and item.split("=", 1)[0].strip() == key:
|
|
return item.split("=", 1)[1].strip()
|
|
return default
|
|
|
|
|
|
def api_get(session: requests.Session, path: str) -> dict:
|
|
"""通用 GET 请求"""
|
|
url = f"{API_BASE}{path}?{build_params()}"
|
|
try:
|
|
resp = session.get(url, timeout=15)
|
|
return resp.json()
|
|
except Exception as e:
|
|
log_error(f"API 请求失败 [{path}]: {e}")
|
|
return {}
|
|
|
|
|
|
def get_sign_status(session: requests.Session) -> tuple:
|
|
"""检查今日签到状态,返回 (is_signed_today, sign_streak, calendar)"""
|
|
result = api_get(session, "/task/sign_list/")
|
|
if result.get("status") != "ok":
|
|
log_error(f"获取签到日历失败: {result}")
|
|
return False, 0, []
|
|
|
|
sign_list = result.get("result", {}).get("sign_list", [])
|
|
today_start = datetime.now(TZ_BEIJING).replace(hour=0, minute=0, second=0, microsecond=0)
|
|
today_ts = int(today_start.timestamp())
|
|
|
|
is_signed = False
|
|
for item in sign_list:
|
|
if item["date"] == today_ts:
|
|
is_signed = item.get("is_sign", False)
|
|
break
|
|
|
|
return is_signed, sign_list
|
|
|
|
|
|
def do_sign(session: requests.Session) -> str:
|
|
"""执行签到 (v3 API),返回 state: success/ignore/fail"""
|
|
result = api_get(session, "/task/sign_v3/sign")
|
|
if result.get("status") != "ok":
|
|
log_error(f"签到请求失败: {result}")
|
|
return "fail"
|
|
|
|
state = result.get("result", {}).get("state", "fail")
|
|
state_map = {
|
|
"success": "签到成功 ✅",
|
|
"ignore": "今日已签到,无需重复 ⚠️",
|
|
"fail": "签到失败 ❌",
|
|
}
|
|
log_info(f"签到结果: {state_map.get(state, state)}")
|
|
return state
|
|
|
|
|
|
def get_task_list(session: requests.Session) -> dict:
|
|
"""获取任务列表,含签到奖励信息"""
|
|
result = api_get(session, "/task/list_v2/")
|
|
if result.get("status") != "ok":
|
|
log_warning(f"获取任务列表失败: {result}")
|
|
return {}
|
|
return result.get("result", {})
|
|
|
|
|
|
def get_account_info(session: requests.Session) -> str:
|
|
"""获取用户昵称"""
|
|
result = api_get(session, "/account/info/")
|
|
if result.get("msg") == "":
|
|
profile = result.get("result", {}).get("profile", {})
|
|
return profile.get("nickname", "未知")
|
|
return "未知"
|
|
|
|
|
|
def build_report(nickname: str, state: str, task_info: dict) -> str:
|
|
"""构建签到报告"""
|
|
lines = ["🎮 小黑盒 每日签到", "", f"👤 账号: {nickname}"]
|
|
|
|
if state == "success":
|
|
lines.append("✅ 签到状态: 签到成功!")
|
|
|
|
# 提取签到奖励
|
|
award_found = False
|
|
for group in task_info.get("task_list", []):
|
|
for task in group.get("tasks", []):
|
|
if task.get("type") == "sign":
|
|
awards = task.get("award_desc_v2", [])
|
|
if awards:
|
|
award_found = True
|
|
lines.append(f"🎁 签到奖励:")
|
|
for a in awards:
|
|
lines.append(f" {a.get('desc', '')}")
|
|
lines.append(f"🔥 连续签到: {task.get('sign_in_streak', '?')} 天")
|
|
break
|
|
|
|
if not award_found:
|
|
lines.append("🎁 奖励: 已自动发放")
|
|
elif state == "ignore":
|
|
lines.append("⚠️ 签到状态: 今日已签到")
|
|
lines.append("🎁 无需重复签到")
|
|
else:
|
|
lines.append("❌ 签到状态: 签到失败")
|
|
lines.append("💡 请检查 Cookie 是否有效")
|
|
|
|
lines.append("")
|
|
lines.append("─" * 18)
|
|
lines.append(f"🕒 执行时间: {beijing_time_str()}")
|
|
return "\n".join(lines)
|
|
|
|
|
|
def main():
|
|
if not HEYBOX_COOKIE:
|
|
log_error("未配置 HEYBOX_COOKIE")
|
|
notify_send("小黑盒签到 错误", "❌ 未配置 HEYBOX_COOKIE")
|
|
return
|
|
|
|
session = create_session()
|
|
nickname = get_account_info(session)
|
|
log_info(f"账号: {nickname}")
|
|
|
|
# 检查签到状态
|
|
is_signed, sign_list = get_sign_status(session)
|
|
|
|
state = "ignore"
|
|
if not is_signed:
|
|
log_info("今日未签到,开始执行签到...")
|
|
state = do_sign(session)
|
|
is_signed = (state in ("success", "ignore"))
|
|
else:
|
|
log_info("今日已签到,无需重复签到")
|
|
|
|
task_info = get_task_list(session)
|
|
|
|
task_info = task_info or {}
|
|
|
|
# 构建报告
|
|
report = build_report(nickname, state, task_info)
|
|
notify_send("小黑盒 签到报告", report)
|
|
log_success("推送完成")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|