first commit: DuoPlus云手机协议注册工具 - 完整实现

This commit is contained in:
huangzhenpc
2025-08-12 14:33:24 +08:00
commit f9dcea2d06
15 changed files with 2314 additions and 0 deletions

455
duoplus_register.py Normal file
View File

@@ -0,0 +1,455 @@
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()