Create pinzan.py

This commit is contained in:
wd210010 authored and GitHub committed 2025-12-24 09:48:52 +08:00
1 parent cffaa5e658
commit 76c7b4f302
1 file changed
+334
+334
View File
@@ -0,0 +1,334 @@
#!/usr/bin/python3
# -- coding: utf-8 --
# -------------------------------
# @Author : github@wd210010 https://github.com/wd210010/only_for_happly
# @Time : 2025/12/24 9:23
# -------------------------------
# cron "0 0 8 * * *" script-path=xxx.py,tag=匹配cron用
# const $ = new Env('品赞签到')
# 变量名:Pzandaili ACCOUNT1#PASSWORD1@ACCOUNT2#PASSWORD2 或 & 分隔
# 品赞注册链接:https://www.ipzan.com?pid=b8quopmio
import warnings
import os
import random
import base64
import requests
import time
from dataclasses import dataclass
from typing import List, Optional, Dict, Any
from contextlib import contextmanager
# -------------------------- 全局配置 --------------------------
# 基础URL配置
CONFIG_URLS = {
"home": "https://www.ipzan.com?pid=b1sf3o4ao",
"login": "https://service.ipzan.com/users-login",
"receive": "https://service.ipzan.com/home/userWallet-receive"
}
# 环境变量配置
ENV_CONFIG = {
"var_name": "Pzandaili",
"separators": ["@", "&"], # 多账户分隔符
"fixed_key": "QWERIPZAN1290QWER" # 加密固定密钥
}
# 请求配置
REQUEST_CONFIG = {
"timeout": 15,
"sleep_range": (2, 5), # 多账户延迟范围
"retry_times": 1, # 接口重试次数
"user_agents": [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36"
]
}
# 关闭SSL警告
warnings.filterwarnings("ignore")
# -------------------------- 数据结构定义 --------------------------
@dataclass
class AccountInfo:
"""账户信息数据类"""
index: int
account: str
password: str
@dataclass
class ProcessResult:
"""账户处理结果数据类"""
account_index: int
account_masked: str
status: str # success/failed/duplicate/error
message: str
raw_data: Optional[Any] = None
# -------------------------- 工具函数 --------------------------
def get_random_user_agent() -> str:
"""获取随机User-Agent"""
return random.choice(REQUEST_CONFIG["user_agents"])
def get_base_headers() -> Dict[str, str]:
"""获取基础请求头"""
return {
"Accept": "application/json, text/plain, */*",
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
"Connection": "keep-alive",
"Content-Type": "application/json;charset=UTF-8",
"Origin": "https://ipzan.com",
"Referer": "https://ipzan.com/",
"User-Agent": get_random_user_agent()
}
def mask_account(account: str) -> str:
"""账号脱敏显示"""
if len(account) > 7:
return f"{account[:3]}****{account[-4:]}"
return account[:2] + "****" + account[-1:] if len(account) > 3 else "****"
@contextmanager
def request_session():
"""请求会话上下文管理器,自动关闭会话"""
session = requests.Session()
try:
session.headers.update(get_base_headers())
yield session
finally:
session.close()
def retry_on_failure(max_retries: int = 1):
"""重试装饰器"""
def decorator(func):
def wrapper(*args, **kwargs):
last_exception = None
for attempt in range(max_retries + 1):
try:
return func(*args, **kwargs)
except Exception as e:
last_exception = e
if attempt < max_retries:
sleep_time = random.uniform(1, 3)
print(f"⚠️ 尝试{attempt+1}失败,{sleep_time:.1f}秒后重试:{str(e)[:50]}")
time.sleep(sleep_time)
else:
raise e
raise last_exception
return wrapper
return decorator
# -------------------------- 核心加密逻辑 --------------------------
def encrypt_account(phone: str, password: str) -> str:
"""IPZAN专属加密逻辑(官网JS还原)"""
# 基础加密
plain_text = f"{phone}{ENV_CONFIG['fixed_key']}{password}"
encoded_str = base64.b64encode(plain_text.encode("utf-8")).decode("utf-8")
# 生成400位混淆字符串
random_hex = []
for _ in range(80):
hex_str = hex(int(random.random() * 10**16))[2:]
random_hex.append(hex_str)
random_hex = "".join(random_hex).ljust(400, "0")[:400]
# 分段拼接
parts = [
random_hex[:100], encoded_str[:8],
random_hex[100:200], encoded_str[8:20],
random_hex[200:300], encoded_str[20:],
random_hex[300:400]
]
return "".join(parts)
# -------------------------- 账户加载 --------------------------
def load_accounts_from_env() -> List[AccountInfo]:
"""加载环境变量中的账户信息,返回结构化的账户列表"""
env_value = os.getenv(ENV_CONFIG["var_name"])
if not env_value:
raise ValueError(
f"❌ 环境变量 {ENV_CONFIG['var_name']} 未设置!\n"
f"📝 配置格式参考:\n"
f" - 单账户:账号#密码\n"
f" - 多账户:账号1#密码1@账号2#密码2(或&分隔)"
)
# 解析多账户分隔符
accounts_str_list = [env_value]
for sep in ENV_CONFIG["separators"]:
if sep in env_value:
accounts_str_list = [s.strip() for s in env_value.split(sep) if s.strip()]
break
# 解析每个账户信息
accounts = []
for idx, account_str in enumerate(accounts_str_list, 1):
parts = account_str.split("#", 2)
# 格式校验(忽略推送链接部分)
if len(parts) < 2:
print(f"⚠️ 跳过格式错误账户{idx}:{account_str}(需为 账号#密码 格式)")
continue
acc, pwd = parts[0].strip(), parts[1].strip()
if acc and pwd:
accounts.append(AccountInfo(
index=idx,
account=acc,
password=pwd
))
if not accounts:
raise ValueError("❌ 未加载到有效账户!请检查环境变量配置。")
return accounts
# -------------------------- 核心业务逻辑 --------------------------
@retry_on_failure(max_retries=REQUEST_CONFIG["retry_times"])
def login_account(session: requests.Session, account: str, password: str) -> str:
"""账户登录,返回Token"""
login_headers = get_base_headers()
login_headers["Authorization"] = "Bearer null"
encrypted_account = encrypt_account(account, password)
login_data = {"account": encrypted_account, "source": "ipzan-home-one"}
response = session.post(
url=CONFIG_URLS["login"],
headers=login_headers,
json=login_data,
timeout=REQUEST_CONFIG["timeout"]
)
response.raise_for_status()
login_result = response.json()
if login_result.get("code") != 0:
raise ValueError(f"登录失败:{login_result.get('message', '未知错误')}")
token = login_result.get("data", {}).get("token")
if not token:
raise ValueError("登录成功但未返回Token")
return token
@retry_on_failure(max_retries=REQUEST_CONFIG["retry_times"])
def receive_ip(session: requests.Session, token: str) -> Dict[str, Any]:
"""领取IP,返回领取结果"""
receive_headers = get_base_headers()
receive_headers["Authorization"] = f"Bearer {token}"
response = session.get(
url=CONFIG_URLS["receive"],
headers=receive_headers,
timeout=REQUEST_CONFIG["timeout"]
)
response.raise_for_status()
return response.json()
def process_single_account(account_info: AccountInfo) -> ProcessResult:
"""处理单个账户的完整流程"""
account_masked = mask_account(account_info.account)
print(f"\n[{account_info.index}] 📱 开始处理账号:{account_masked}")
try:
with request_session() as session:
# 1. 登录
token = login_account(session, account_info.account, account_info.password)
print(" └─ ✅ 登录成功")
# 2. 领取IP
receive_result = receive_ip(session, token)
code = receive_result.get("code")
msg = receive_result.get("message", "")
data = receive_result.get("data", "")
# 3. 处理领取结果
if code == 0:
status = "success"
message = f"领取成功:{data}"
print(f" └─ 🎉 {message}")
elif code == -1 and "领取过" in str(msg):
status = "duplicate"
message = f"本周已领:{msg}"
print(f" └─ ⚠️ {message}")
else:
status = "failed"
message = f"领取失败:{msg}"
print(f" └─ ❌ {message}")
return ProcessResult(
account_index=account_info.index,
account_masked=account_masked,
status=status,
message=message,
raw_data=receive_result
)
except Exception as e:
error_msg = f"运行异常:{str(e)[:50]}"
print(f" └─ ❌ {error_msg}")
return ProcessResult(
account_index=account_info.index,
account_masked=account_masked,
status="error",
message=error_msg
)
# -------------------------- 主程序 --------------------------
def main():
"""主程序入口"""
print("="*60)
print("🎯 IPZAN 多账户自动领取工具(每周领IP)- 无推送版")
print(f"🔗 注册链接:{CONFIG_URLS['home']}")
print("="*60)
all_summaries = []
try:
# 1. 加载账户
accounts = load_accounts_from_env()
print(f"\n📊 共加载 {len(accounts)} 个有效账户,开始执行任务...")
# 2. 处理每个账户
for idx, account in enumerate(accounts):
result = process_single_account(account)
# 构建汇总信息
status_icon = {
"success": "✅",
"failed": "❌",
"duplicate": "⚠️",
"error": "❌"
}.get(result.status, "❓")
summary = f"账号{result.account_index}:{result.account_masked}\n{status_icon} {result.message}"
all_summaries.append(summary)
# 多账户延迟
if idx < len(accounts) - 1:
sleep_time = random.uniform(*REQUEST_CONFIG["sleep_range"])
print(f"\n⏳ 多账户防并发,延迟 {sleep_time:.1f} 秒...")
time.sleep(sleep_time)
except Exception as e:
error_summary = f"❌ 程序全局错误:{str(e)[:100]}"
print(f"\n{error_summary}")
all_summaries.append(error_summary)
finally:
# 输出最终结果
print("\n" + "="*60)
print("\n📝 执行结果汇总:")
for summary in all_summaries:
print(f" {summary}")
print("\n✅ 脚本执行完成!")
print("="*60)
if __name__ == "__main__":
main()