Files
duoplus/duoplus_register.py

455 lines
17 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import requests
import json
import time
import random
import string
import hashlib
from typing import Optional, Dict, Any
from dotenv import load_dotenv
import os
from captcha_solver import TwoCaptchaSolver
from tracker import DataTracker
from email_manager import EmailManager, AutoEmailVerification
from colorama import init, Fore, Style
# 初始化 colorama
init(autoreset=True)
class DuoPlusRegister:
"""DuoPlus 协议注册类"""
def __init__(self, captcha_api_key: str):
self.session = requests.Session()
self.captcha_solver = TwoCaptchaSolver(captcha_api_key)
self.tracker = DataTracker() # 初始化数据追踪器
self.base_url = "https://my.duoplus.cn"
self.api_url = "https://api.duoplus.cn"
self.captcha_app_id = None
# 生成设备指纹
self.device_fp = self._generate_device_fp()
# 设置请求头
self.session.headers.update({
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36',
'Accept': 'application/json, text/plain, */*',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
'Accept-Encoding': 'gzip, deflate, br',
'Origin': 'https://my.duoplus.cn',
'Referer': 'https://my.duoplus.cn/',
'Content-Type': 'application/json',
'sec-ch-ua': '"Not)A;Brand";v="8", "Chromium";v="138", "Google Chrome";v="138"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
'sec-fetch-site': 'same-site',
'sec-fetch-mode': 'cors',
'sec-fetch-dest': 'empty',
'pragma': 'no-cache',
'cache-control': 'no-cache',
'lang': 'zh-CN',
'duoplus-fp': self.device_fp,
'priority': 'u=1, i'
})
# 获取初始 cookies 和配置
self._init_session()
def _generate_device_fp(self) -> str:
"""生成设备指纹"""
# 简单的设备指纹生成,实际可能需要更复杂的算法
random_str = ''.join(random.choices(string.ascii_lowercase + string.digits, k=16))
return hashlib.md5(random_str.encode()).hexdigest()
def _init_session(self):
"""初始化会话,获取必要的 cookies 和配置"""
try:
# 先访问注册页面获取 cookies
response = self.session.get(f"{self.base_url}/sign-up")
print(f"{Fore.GREEN}[INFO] 初始化会话成功")
# 发送数据追踪请求(模拟真实浏览器行为)
self.tracker.track_registration_page()
# 获取腾讯验证码配置
self._get_captcha_config()
except Exception as e:
print(f"{Fore.RED}[ERROR] 初始化会话失败: {str(e)}")
def _get_captcha_config(self) -> Optional[Dict[str, Any]]:
"""
获取腾讯验证码配置信息
Returns:
验证码配置信息
"""
try:
# 调用 API 获取验证码配置
config_url = f"{self.api_url}/common/tencentConfig"
# 更新请求头中的 referer
headers = self.session.headers.copy()
headers['referer'] = 'https://my.duoplus.cn/'
headers['authorization'] = '' # 注册时可能不需要 authorization
response = self.session.get(config_url, headers=headers)
if response.status_code == 200:
data = response.json()
if data.get('code') == 200:
self.captcha_app_id = data['data']['captcha_app_id']
print(f"{Fore.GREEN}[INFO] 获取验证码配置成功app_id: {self.captcha_app_id}")
return {
"app_id": self.captcha_app_id,
"page_url": f"{self.base_url}/sign-up"
}
else:
print(f"{Fore.RED}[ERROR] 获取验证码配置失败: {data.get('message')}")
else:
print(f"{Fore.RED}[ERROR] 请求验证码配置失败: HTTP {response.status_code}")
except Exception as e:
print(f"{Fore.RED}[ERROR] 获取验证码配置异常: {str(e)}")
# 如果获取失败,返回默认值
return {
"app_id": "192789496", # 使用实际的 app_id 作为备份
"page_url": f"{self.base_url}/sign-up"
}
def send_verification_code(self, email: str) -> bool:
"""
发送验证码
Args:
email: 邮箱地址
Returns:
是否成功发送
"""
print(f"{Fore.CYAN}[INFO] 准备发送验证码到: {email}")
# 获取验证码配置
captcha_config = self._get_captcha_config()
if not captcha_config:
print(f"{Fore.RED}[ERROR] 无法获取验证码配置")
return False
# 使用 2captcha 解决验证码
print(f"{Fore.YELLOW}[INFO] 正在处理腾讯滑块验证码...")
captcha_result = self.captcha_solver.solve_tencent_captcha(
app_id=captcha_config["app_id"],
page_url=captcha_config["page_url"]
)
if not captcha_result:
print(f"{Fore.RED}[ERROR] 验证码处理失败")
return False
# 发送验证码请求 - 使用正确的 API 端点
send_code_url = f"{self.api_url}/email/verificationCode"
# 正确的请求体格式
payload = {
"type": 1, # 1 表示注册
"email": email,
"rand_str": captcha_result["randstr"],
"ticket": captcha_result["ticket"]
}
# 更新请求头
headers = self.session.headers.copy()
headers.update({
'referer': 'https://my.duoplus.cn/',
'authorization': '',
'accept': 'application/json'
})
try:
print(f"{Fore.YELLOW}[INFO] 发送请求到: {send_code_url}")
print(f"{Fore.YELLOW}[DEBUG] 请求体: {json.dumps(payload, ensure_ascii=False)}")
response = self.session.post(send_code_url, json=payload, headers=headers)
result = response.json()
if response.status_code == 200 and result.get("code") == 200:
print(f"{Fore.GREEN}[SUCCESS] 验证码发送成功: {result.get('message', 'Success')}")
return True
else:
print(f"{Fore.RED}[ERROR] 发送验证码失败: {result.get('message', 'Unknown error')}")
return False
except Exception as e:
print(f"{Fore.RED}[ERROR] 请求失败: {str(e)}")
return False
def register(self, email: str, password: str, verification_code: str) -> Dict[str, Any]:
"""
执行注册
Args:
email: 邮箱地址
password: 密码
verification_code: 邮箱验证码
Returns:
注册结果字典,包含 token 等信息
"""
print(f"{Fore.CYAN}[INFO] 开始注册账号...")
# 使用正确的注册端点
register_url = f"{self.api_url}/account/signup"
# 正确的请求体格式
payload = {
"username": email, # 使用邮箱作为用户名
"password": password,
"verification_code": verification_code,
"isAgress": True, # 同意条款
"from_landing": 0
}
# 更新请求头
headers = self.session.headers.copy()
headers.update({
'referer': 'https://my.duoplus.cn/',
'authorization': '',
'accept': 'application/json'
})
try:
print(f"{Fore.YELLOW}[INFO] 发送注册请求到: {register_url}")
print(f"{Fore.YELLOW}[DEBUG] 请求体: {json.dumps(payload, ensure_ascii=False)}")
response = self.session.post(register_url, json=payload, headers=headers)
result = response.json()
if response.status_code == 200 and result.get("code") == 200:
print(f"{Fore.GREEN}[SUCCESS] 注册成功!")
# 获取返回的数据
data = result.get("data", {})
if data:
print(f"{Fore.GREEN}[INFO] Access Token: {data.get('access_token')[:20]}...")
print(f"{Fore.GREEN}[INFO] Token 有效期: {data.get('expired_in')}")
print(f"{Fore.GREEN}[INFO] 赠送余额: {data.get('gift_balance', '$0.00')}")
# 获取用户详细信息
self._get_user_profile(data.get('access_token'))
return {"success": True, "data": data}
else:
print(f"{Fore.RED}[ERROR] 注册失败: {result.get('message', 'Unknown error')}")
print(f"{Fore.RED}[DEBUG] 完整响应: {json.dumps(result, ensure_ascii=False)}")
return {"success": False, "message": result.get('message')}
except Exception as e:
print(f"{Fore.RED}[ERROR] 请求失败: {str(e)}")
return {"success": False, "message": str(e)}
def _get_user_profile(self, access_token: str) -> Optional[Dict[str, Any]]:
"""
获取用户详细信息
Args:
access_token: 访问令牌
Returns:
用户信息字典
"""
profile_url = f"{self.api_url}/account/profile"
headers = self.session.headers.copy()
headers.update({
'authorization': access_token,
'accept': 'application/json'
})
try:
response = self.session.post(profile_url, json={}, headers=headers)
result = response.json()
if response.status_code == 200 and result.get("code") == 200:
profile = result.get("data", {})
print(f"{Fore.GREEN}[INFO] 用户ID: {profile.get('user_id')}")
print(f"{Fore.GREEN}[INFO] 邮箱: {profile.get('email')}")
print(f"{Fore.GREEN}[INFO] 团队ID: {profile.get('team_id')}")
print(f"{Fore.GREEN}[INFO] 团队名称: {profile.get('team_name')}")
return profile
except Exception as e:
print(f"{Fore.YELLOW}[WARNING] 获取用户信息失败: {str(e)}")
return None
def generate_random_password(self, length: int = 12) -> str:
"""生成随机密码"""
characters = string.ascii_letters + string.digits + "!@#$%^&*"
return ''.join(random.choice(characters) for _ in range(length))
def auto_register(self, email: str, password: Optional[str] = None) -> Dict[str, Any]:
"""
自动注册流程
Args:
email: 邮箱地址
password: 密码(如果不提供则自动生成)
Returns:
注册结果字典
"""
if not password:
password = self.generate_random_password()
print(f"{Fore.YELLOW}[INFO] 生成随机密码: {password}")
# 保存注册信息
print(f"{Fore.CYAN}{'='*60}")
print(f"{Fore.CYAN}注册信息:")
print(f"{Fore.CYAN}{'='*60}")
print(f" 📧 邮箱: {email}")
print(f" 🔑 密码: {password}")
print(f"{Fore.CYAN}{'='*60}\n")
# 发送验证码
if not self.send_verification_code(email):
return {"success": False, "message": "发送验证码失败"}
# 等待用户输入验证码
print(f"{Fore.CYAN}[INPUT] 请查看邮箱并输入验证码: ", end="")
verification_code = input().strip()
# 执行注册
result = self.register(email, password, verification_code)
if result.get("success"):
# 保存账号信息到文件
self._save_account_info(email, password, result.get("data", {}))
return result
def auto_register_with_temp_email(self) -> Dict[str, Any]:
"""
使用临时邮箱全自动注册
Returns:
注册结果字典
"""
print(f"{Fore.CYAN}[INFO] 使用自建邮箱进行全自动注册...")
# 创建邮箱管理器
email_manager = EmailManager()
auto_verifier = AutoEmailVerification(email_manager)
# 创建临时邮箱
print(f"{Fore.CYAN}[INFO] 创建临时邮箱...")
email_info = auto_verifier.create_temp_email(domain="cursor.edu.kg")
if not email_info.get("success"):
return {"success": False, "message": "创建临时邮箱失败"}
email_address = email_info["email"]
email_password = email_info["password"]
print(f"{Fore.GREEN}[INFO] 临时邮箱创建成功")
print(f"{Fore.GREEN}[INFO] 邮箱: {email_address}")
print(f"{Fore.GREEN}[INFO] 邮箱密码: {email_password}")
# 生成DuoPlus账号密码
duoplus_password = self.generate_random_password()
# 保存注册信息
print(f"\n{Fore.CYAN}{'='*60}")
print(f"{Fore.CYAN}DuoPlus 注册信息:")
print(f"{Fore.CYAN}{'='*60}")
print(f" 📧 邮箱: {email_address}")
print(f" 🔑 密码: {duoplus_password}")
print(f"{Fore.CYAN}{'='*60}\n")
# 发送验证码
if not self.send_verification_code(email_address):
return {"success": False, "message": "发送验证码失败"}
# 自动获取验证码
print(f"{Fore.CYAN}[INFO] 等待接收验证码...")
verification_code = auto_verifier.wait_for_code(
email_info,
sender_filter="duoplus",
timeout=60
)
if not verification_code:
print(f"{Fore.YELLOW}[WARNING] 自动获取验证码失败,请手动输入")
print(f"{Fore.CYAN}[INPUT] 请输入验证码: ", end="")
verification_code = input().strip()
# 执行注册
result = self.register(email_address, duoplus_password, verification_code)
if result.get("success"):
# 保存账号信息到文件,包括邮箱密码
data = result.get("data", {})
data["email_password"] = email_password # 保存邮箱密码
self._save_account_info(email_address, duoplus_password, data)
return result
def _save_account_info(self, email: str, password: str, data: Dict[str, Any]):
"""
保存账号信息到文件
Args:
email: 邮箱
password: 密码
data: 注册返回的数据
"""
try:
import datetime
filename = "registered_accounts.txt"
with open(filename, "a", encoding="utf-8") as f:
f.write(f"\n{'='*60}\n")
f.write(f"注册时间: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
f.write(f"邮箱: {email}\n")
f.write(f"密码: {password}\n")
f.write(f"Access Token: {data.get('access_token', 'N/A')}\n")
f.write(f"Refresh Token: {data.get('refresh_token', 'N/A')}\n")
f.write(f"Token 有效期: {data.get('expired_in', 0)}\n")
f.write(f"赠送余额: {data.get('gift_balance', '$0.00')}\n")
print(f"{Fore.GREEN}[INFO] 账号信息已保存到 {filename}")
except Exception as e:
print(f"{Fore.YELLOW}[WARNING] 保存账号信息失败: {str(e)}")
def main():
# 加载环境变量
load_dotenv()
# 获取 API Key
captcha_api_key = os.getenv('CAPTCHA_API_KEY')
if not captcha_api_key:
print(f"{Fore.RED}[ERROR] 请在 .env 文件中设置 CAPTCHA_API_KEY")
return
# 创建注册器
registrar = DuoPlusRegister(captcha_api_key)
# 获取用户输入
print(f"{Fore.CYAN}=== DuoPlus 自动注册工具 ===")
email = input(f"{Fore.YELLOW}请输入邮箱地址: ").strip()
use_random_password = input(f"{Fore.YELLOW}是否使用随机密码? (y/n): ").strip().lower() == 'y'
if use_random_password:
password = None
else:
password = input(f"{Fore.YELLOW}请输入密码: ").strip()
# 执行注册
if registrar.auto_register(email, password):
print(f"{Fore.GREEN}[SUCCESS] 注册流程完成!")
else:
print(f"{Fore.RED}[FAILED] 注册失败!")
if __name__ == "__main__":
main()