实现邮箱API集成和注册数量限制功能

This commit is contained in:
hkyc
2025-05-22 22:43:44 +08:00
parent 4ee6a2e195
commit 3fb99d17e0
5 changed files with 348 additions and 70 deletions

84
EmailAPIClient.py Normal file
View File

@@ -0,0 +1,84 @@
import requests
import json
from loguru import logger
class EmailAPIClient:
"""自建邮箱API客户端
用于从API获取邮箱账号
"""
def __init__(self, api_url="https://steamapi.cursorpro.com.cn/api/create_user"):
"""初始化API客户端
Args:
api_url: API服务器地址
"""
self.api_url = api_url
def create_email_account(self):
"""创建新的邮箱账号
通过调用API创建一个新的邮箱账号
Returns:
dict: 包含以下字段的字典如果失败则返回None
- id: 账号ID
- username: 用户名
- email: 邮箱地址
- password: 邮箱密码
- success: 是否成功
- message: 信息
"""
try:
logger.info(f"正在从API获取邮箱账号: {self.api_url}")
response = requests.post(self.api_url, timeout=30)
if response.status_code != 200:
logger.error(f"API请求失败状态码: {response.status_code}")
return None
try:
result = response.json()
except json.JSONDecodeError:
logger.error("API返回的数据不是有效的JSON格式")
return None
if not result.get("success", False):
logger.error(f"API返回错误: {result.get('message', '未知错误')}")
return None
logger.info(f"成功获取邮箱账号: {result.get('email')}")
return result
except Exception as e:
logger.error(f"获取邮箱账号出错: {str(e)}")
return None
def get_email_credentials(self):
"""获取邮箱凭据
获取邮箱地址和密码,并格式化为凭据字典
Returns:
dict: 包含email和password字段的字典如果失败则返回None
"""
account = self.create_email_account()
if not account:
return None
return {
'email': account.get('email'),
'password': account.get('password')
}
# 测试代码
if __name__ == "__main__":
client = EmailAPIClient()
email_data = client.get_email_credentials()
if email_data:
print(f"获取到的邮箱: {email_data['email']}")
print(f"获取到的密码: {email_data['password']}")
else:
print("获取邮箱账号失败")

View File

@@ -8,16 +8,17 @@ from tkinter import filedialog
from ThreadManagerWithPyno import GUIThreadManagerWithPyno
from PynoCaptchaSolver import PynoCaptchaConfig, PynoCaptchaSolver
from SteamCaptchaHelper import SteamCaptchaHelper
from EmailAPIClient import EmailAPIClient
import time
# 配置日志
from loguru import logger
logger.add("steam_registration_pyno.log", rotation="10 MB")
logger.add("steam_registration.log", rotation="10 MB")
class RegistrationGUIWithPynoV2:
class SteamRegistrationGUI:
def __init__(self, root):
self.root = root
self.root.title("Steam注册 - PyNoCaptcha V2")
self.root.title("Steam账号注册助手")
self.root.geometry("800x800")
self.root.minsize(800, 800) # 设置最小窗口大小
@@ -30,7 +31,7 @@ class RegistrationGUIWithPynoV2:
top_frame.pack(fill="x", side="top", padx=5, pady=5)
# 创建配置框架
config_frame = ttk.LabelFrame(top_frame, text="配置信息")
config_frame = ttk.LabelFrame(top_frame, text="基本配置")
config_frame.pack(fill="x", padx=5, pady=5)
# 加载默认配置
@@ -42,11 +43,11 @@ class RegistrationGUIWithPynoV2:
self.config_vars = {}
self._create_config_widgets(config_frame)
# 创建PyNoCaptcha配置框架
captcha_frame = ttk.LabelFrame(top_frame, text="PyNoCaptcha配置")
# 创建验证码配置框架
captcha_frame = ttk.LabelFrame(top_frame, text="验证码配置")
captcha_frame.pack(fill="x", padx=5, pady=5)
# 创建PyNoCaptcha配置输入框
# 创建验证码配置输入框
self.captcha_vars = {}
self._create_captcha_widgets(captcha_frame)
@@ -71,17 +72,51 @@ class RegistrationGUIWithPynoV2:
ttk.Button(steam_frame, text="刷新验证信息",
command=self._refresh_steam_info).grid(row=3, column=0, columnspan=2, padx=5, pady=5)
# 创建邮箱选项框架
email_option_frame = ttk.LabelFrame(top_frame, text="邮箱设置")
email_option_frame.pack(fill="x", padx=5, pady=5)
# 添加邮箱来源选择
self.email_source_var = tk.StringVar(value="txt_file")
email_source_options = [
("自建邮箱API", "api"),
("本地文件", "txt_file"),
("微软Graph", "graph")
]
# 注册数量设置
reg_count_frame = ttk.Frame(email_option_frame)
reg_count_frame.pack(fill="x", padx=5, pady=5)
ttk.Label(reg_count_frame, text="注册数量:").grid(row=0, column=0, padx=5, pady=2, sticky="e")
self.reg_count_var = tk.StringVar(value="10")
ttk.Entry(reg_count_frame, textvariable=self.reg_count_var, width=10).grid(row=0, column=1, padx=5, pady=2, sticky="w")
ttk.Label(reg_count_frame, text="(0表示不限制)").grid(row=0, column=2, padx=5, pady=2, sticky="w")
# 邮箱来源选择
email_source_frame = ttk.Frame(email_option_frame)
email_source_frame.pack(fill="x", padx=5, pady=5)
for i, (text, value) in enumerate(email_source_options):
ttk.Radiobutton(
email_source_frame,
text=text,
variable=self.email_source_var,
value=value,
command=self._toggle_email_source
).grid(row=0, column=i, padx=10, pady=2)
# 创建文件选择框架
files_frame = ttk.LabelFrame(top_frame, text="文件选择")
files_frame.pack(fill="x", padx=5, pady=5)
self.files_frame = ttk.LabelFrame(top_frame, text="文件选择")
self.files_frame.pack(fill="x", padx=5, pady=5)
# 邮箱文件选择
self.email_path = tk.StringVar(value=os.path.join(self.script_dir, 'email_password.txt'))
self._create_file_selector(files_frame, "邮箱文件:", self.email_path, 0)
self._create_file_selector(self.files_frame, "邮箱文件:", self.email_path, 0)
# 代理文件选择
self.proxy_path = tk.StringVar(value=os.path.join(self.script_dir, 'proxy_ips.txt'))
self._create_file_selector(files_frame, "代理文件:", self.proxy_path, 1)
self._create_file_selector(self.files_frame, "代理文件:", self.proxy_path, 1)
# 按钮框架 - 直接放在主界面上,任务列表上方
button_frame = ttk.Frame(main_frame)
@@ -92,7 +127,7 @@ class RegistrationGUIWithPynoV2:
button_frame,
text="开始注册",
command=self.start_registration,
width=20
width=15
)
self.start_button.pack(side="left", padx=10, pady=5, expand=True)
@@ -102,7 +137,7 @@ class RegistrationGUIWithPynoV2:
text="停止注册",
command=self.stop_registration,
state="disabled",
width=20
width=15
)
self.stop_button.pack(side="left", padx=10, pady=5, expand=True)
@@ -111,10 +146,19 @@ class RegistrationGUIWithPynoV2:
button_frame,
text="测试验证码",
command=self.test_captcha,
width=20
width=15
)
self.test_captcha_button.pack(side="left", padx=10, pady=5, expand=True)
# 添加获取邮箱测试按钮
self.test_email_button = ttk.Button(
button_frame,
text="测试邮箱API",
command=self.test_email_api,
width=15
)
self.test_email_button.pack(side="left", padx=10, pady=5, expand=True)
# 中间框架 - 包含任务状态表格
middle_frame = ttk.Frame(main_frame)
middle_frame.pack(fill="both", expand=True, padx=5, pady=5)
@@ -162,9 +206,31 @@ class RegistrationGUIWithPynoV2:
self._stop_progress_update = False
# 添加版本标签
version_label = ttk.Label(main_frame, text="PyNoCaptcha V2 版本", foreground="blue")
version_label = ttk.Label(main_frame, text="Steam账号注册助手 V3.0", foreground="blue")
version_label.pack(side="bottom", padx=5, pady=5)
# 初始化邮箱API客户端
self.email_api_client = EmailAPIClient()
# 初始设置邮箱来源
self._toggle_email_source()
def _toggle_email_source(self):
"""根据邮箱来源选择切换界面显示"""
source = self.email_source_var.get()
# 首先隐藏所有与邮箱相关的文件选择框
for widget in self.files_frame.winfo_children():
if "邮箱文件" in str(widget):
widget.grid_remove()
# 根据所选邮箱来源显示相应控件
if source == "txt_file":
# 显示邮箱文件选择框
for widget in self.files_frame.winfo_children():
if "邮箱文件" in str(widget):
widget.grid()
def _refresh_steam_info(self):
"""刷新Steam验证信息"""
threading.Thread(target=self._refresh_steam_info_thread, daemon=True).start()
@@ -209,11 +275,11 @@ class RegistrationGUIWithPynoV2:
]
for i, (key, label, options) in enumerate(config_items):
ttk.Label(parent, text=label,width=10).grid(row=i, column=0, padx=5, pady=2, sticky="e")
ttk.Label(parent, text=label, width=10).grid(row=i, column=0, padx=5, pady=2, sticky="e")
if options:
var = tk.StringVar(value=str(self.config.get(key, "")))
widget = ttk.Combobox(parent, textvariable=var, values=options,width=50)
widget = ttk.Combobox(parent, textvariable=var, values=options, width=50)
else:
var = tk.StringVar(value=str(self.config.get(key, "")))
widget = ttk.Entry(parent, textvariable=var, width=50)
@@ -222,22 +288,22 @@ class RegistrationGUIWithPynoV2:
self.config_vars[key] = var
def _create_captcha_widgets(self, parent):
"""创建PyNoCaptcha配置输入控件"""
"""创建验证码配置输入控件"""
captcha_items = [
("pyno_user_token", "PyNoCaptcha API密钥:", None),
("pyno_sitekey", "验证码网站Key:", None),
("pyno_referer", "网站Referer:", None),
("pyno_user_token", "API密钥:", None),
("pyno_sitekey", "网站Key:", None),
("pyno_referer", "Referer:", None),
("pyno_user_agent", "User-Agent:", None),
("pyno_timeout", "超时时间(秒):", None),
("pyno_debug", "调试模式:", ["True", "False"])
]
for i, (key, label, options) in enumerate(captcha_items):
ttk.Label(parent, text=label,width=12).grid(row=i, column=0, padx=5, pady=2, sticky="e")
ttk.Label(parent, text=label, width=10).grid(row=i, column=0, padx=5, pady=2, sticky="e")
if options:
var = tk.StringVar(value=str(self.config.get(key, "True" if key == "pyno_debug" else "False")))
widget = ttk.Combobox(parent, textvariable=var, values=options,width=50)
widget = ttk.Combobox(parent, textvariable=var, values=options, width=50)
else:
default_value = ""
if key == "pyno_sitekey":
@@ -334,7 +400,7 @@ class RegistrationGUIWithPynoV2:
value = value.lower() == "true"
config[key] = value
# 保存PyNoCaptcha配置
# 保存验证码配置
for key, var in self.captcha_vars.items():
value = var.get()
if key == "pyno_timeout":
@@ -343,6 +409,10 @@ class RegistrationGUIWithPynoV2:
value = value.lower() == "true"
config[key] = value
# 保存邮箱来源配置
config['email_source'] = self.email_source_var.get()
config['reg_count'] = int(self.reg_count_var.get())
with open(self.config_path, 'w') as f:
json.dump(config, f, indent=2)
return True
@@ -352,7 +422,9 @@ class RegistrationGUIWithPynoV2:
def _validate_inputs(self):
"""验证输入"""
if not os.path.exists(self.email_path.get()):
# 检查邮箱来源
email_source = self.email_source_var.get()
if email_source == "txt_file" and not os.path.exists(self.email_path.get()):
messagebox.showerror("错误", "邮箱文件不存在")
return False
@@ -366,17 +438,27 @@ class RegistrationGUIWithPynoV2:
messagebox.showerror("错误", f"请填写 {key} 配置项")
return False
# 验证PyNoCaptcha配置
# 验证验证码配置
required_captcha_keys = ["pyno_user_token"]
for key in required_captcha_keys:
if not self.captcha_vars[key].get().strip():
messagebox.showerror("错误", f"请填写 {key} 配置项")
return False
# 验证注册数量
try:
count = int(self.reg_count_var.get())
if count < 0:
messagebox.showerror("错误", "注册数量不能为负数")
return False
except ValueError:
messagebox.showerror("错误", "注册数量必须是整数")
return False
return True
def get_captcha_config(self):
"""获取PyNoCaptcha配置"""
"""获取验证码配置"""
return PynoCaptchaConfig(
user_token=self.captcha_vars["pyno_user_token"].get(),
sitekey=self.captcha_vars["pyno_sitekey"].get(),
@@ -386,11 +468,52 @@ class RegistrationGUIWithPynoV2:
debug=self.captcha_vars["pyno_debug"].get().lower() == "true"
)
def test_email_api(self):
"""测试邮箱API功能"""
# 禁用测试按钮
self.test_email_button.config(state="disabled")
# 在新线程中测试
threading.Thread(target=self._test_email_api_thread, daemon=True).start()
def _test_email_api_thread(self):
"""在新线程中测试邮箱API"""
try:
test_id = "测试邮箱API"
self.update_status(test_id, status="正在从API获取邮箱...")
# 获取邮箱账号
email_data = self.email_api_client.get_email_credentials()
if not email_data:
self.update_status(test_id, status="获取邮箱失败", result="失败")
messagebox.showerror("错误", "无法从API获取邮箱账号")
return
# 显示结果
self.update_status(
test_id,
status=f"获取邮箱成功: {email_data['email']}",
account_name="N/A",
password=email_data['password'],
result="成功"
)
messagebox.showinfo("成功", f"成功获取邮箱: {email_data['email']}")
except Exception as e:
logger.error(f"测试邮箱API出错: {str(e)}")
messagebox.showerror("错误", f"测试过程中出错: {str(e)}")
self.update_status(test_id, status=f"出错: {str(e)}", result="错误")
finally:
# 恢复按钮状态
self.test_email_button.config(state="normal")
def test_captcha(self):
"""测试PyNoCaptcha验证码"""
"""测试验证码"""
# 验证配置
if not self.captcha_vars["pyno_user_token"].get().strip():
messagebox.showerror("错误", "请先填写PyNoCaptcha API密钥")
messagebox.showerror("错误", "请先填写验证码API密钥")
return
# 保存配置
@@ -404,9 +527,9 @@ class RegistrationGUIWithPynoV2:
threading.Thread(target=self._test_captcha_thread, daemon=True).start()
def _test_captcha_thread(self):
"""在新线程中测试PyNoCaptcha"""
"""在新线程中测试验证码"""
try:
test_id = "测试PyNoCaptcha"
test_id = "测试验证码"
self.update_status(test_id, status="正在准备验证码测试...")
# 首先获取动态sitekey
@@ -481,7 +604,7 @@ class RegistrationGUIWithPynoV2:
except Exception as e:
logger.error(f"测试过程中出错: {str(e)}")
messagebox.showerror("错误", f"测试过程中出错: {str(e)}")
self.update_status("测试PyNoCaptcha", status=f"出错: {str(e)}", result="错误")
self.update_status("测试验证码", status=f"出错: {str(e)}", result="错误")
finally:
# 恢复按钮状态
self.test_captcha_button.config(state="normal")
@@ -547,17 +670,24 @@ class RegistrationGUIWithPynoV2:
messagebox.showerror("错误", f"获取Steam验证信息失败: {str(e)}")
return
# 使用PyNoCaptcha版本的GUIThreadManager
self.manager = GUIThreadManagerWithPyno(
self.config_path,
self.email_path.get(),
self.proxy_path.get(),
self,
completed_tasks
)
# 创建参数字典
manager_params = {
'config_path': self.config_path,
'email_path': self.email_path.get(),
'proxy_path': self.proxy_path.get(),
'gui': self,
'completed_tasks': completed_tasks,
'email_source': self.email_source_var.get(),
'reg_count': int(self.reg_count_var.get()),
'email_api_client': self.email_api_client if self.email_source_var.get() == "api" else None
}
# 使用线程管理器
self.manager = GUIThreadManagerWithPyno(**manager_params)
self.manager.start()
except Exception as e:
logger.error(f"启动注册线程出错: {str(e)}")
messagebox.showerror("错误", str(e))
finally:
self.start_button.config(state="normal")
@@ -572,7 +702,7 @@ class RegistrationGUIWithPynoV2:
if __name__ == "__main__":
root = tk.Tk()
gui = RegistrationGUIWithPynoV2(root)
gui = SteamRegistrationGUI(root)
# 启动时自动刷新Steam信息
threading.Thread(target=gui._refresh_steam_info_thread, daemon=True).start()
root.mainloop()

View File

@@ -4,11 +4,12 @@ import os
import threading
from ProxyPool import ProxyPool
from SteamRegistrationWithPyno import SteamRegistrationWithPyno
from EmailAPIClient import EmailAPIClient
class ThreadManagerWithPyno:
"""线程管理器 - 使用PyNoCaptcha版本"""
def __init__(self, config_path, email_file_path, proxy_file_path):
"""线程管理器 - 使用验证码解决方案"""
def __init__(self, config_path, email_file_path, proxy_file_path, email_source="txt_file", reg_count=0, email_api_client=None):
self.config = self._load_config(config_path)
self._validate_config()
self.proxy_pool = ProxyPool(proxy_file_path)
@@ -18,6 +19,11 @@ class ThreadManagerWithPyno:
self._running = True
self._registrations = set()
self._registrations_lock = threading.Lock()
self.email_source = email_source
self.reg_count = reg_count
self.processed_count = 0
self.count_lock = threading.Lock()
self.email_api_client = email_api_client or EmailAPIClient()
def _validate_config(self):
"""验证配置有效性"""
@@ -62,10 +68,28 @@ class ThreadManagerWithPyno:
return self._registration_local.registration
def process_email(self, email_data):
"""处理一个邮箱账号的注册"""
# 检查是否达到注册数量限制
if self.reg_count > 0:
with self.count_lock:
if self.processed_count >= self.reg_count:
print(f"已达到注册数量限制: {self.reg_count}")
return
self.processed_count += 1
registration = self._get_registration()
registration.main(email_data)
def get_email_from_api(self):
"""从API获取邮箱账号"""
try:
email_data = self.email_api_client.get_email_credentials()
if email_data:
return email_data
except Exception as e:
print(f"从API获取邮箱失败: {e}")
return None
def stop(self):
"""停止所有任务"""
self._running = False
@@ -92,28 +116,47 @@ class ThreadManagerWithPyno:
"""启动处理"""
self._running = True
try:
with open(self.email_file, "r") as file:
for line in file:
if not self._running:
if self.email_source == "api":
# 使用API获取邮箱
while self._running and (self.reg_count == 0 or self.processed_count < self.reg_count):
email_data = self.get_email_from_api()
if not email_data:
print("无法从API获取邮箱任务停止")
break
try:
email_data = self.parse_email_credentials(line.strip())
self.executor.submit(self.process_email, email_data)
except ValueError as e:
print(f"错误的邮箱格式: {e}")
continue
self.executor.submit(self.process_email, email_data)
else:
# 使用本地文件获取邮箱
with open(self.email_file, "r", encoding="utf-8") as file:
for line in file:
if not self._running or (self.reg_count > 0 and self.processed_count >= self.reg_count):
break
try:
email_data = self.parse_email_credentials(line.strip())
self.executor.submit(self.process_email, email_data)
except ValueError as e:
print(f"错误的邮箱格式: {e}")
continue
finally:
self.executor.shutdown(wait=True)
class GUIThreadManagerWithPyno(ThreadManagerWithPyno):
def __init__(self, config_path, email_file_path, proxy_file_path, gui, completed_tasks=None):
super().__init__(config_path, email_file_path, proxy_file_path)
def __init__(self, config_path, email_path, proxy_path, gui, completed_tasks=None, email_source="txt_file", reg_count=0, email_api_client=None):
super().__init__(config_path, email_path, proxy_path, email_source, reg_count, email_api_client)
self.gui = gui
self.completed_tasks = completed_tasks or set()
def process_email(self, email_data):
"""处理单个邮件账号"""
try:
# 检查是否达到注册数量限制
if self.reg_count > 0:
with self.count_lock:
if self.processed_count >= self.reg_count:
print(f"已达到注册数量限制: {self.reg_count}")
return
self.processed_count += 1
print(f"当前处理数量: {self.processed_count}/{self.reg_count}")
if not self._running: # 检查是否应该继续
self.gui.update_status(email_data['email'], "任务已停止")
return
@@ -141,15 +184,36 @@ class GUIThreadManagerWithPyno(ThreadManagerWithPyno):
def start(self):
"""启动处理"""
self._running = True
with open(self.email_file, 'r', encoding='utf-8') as file:
with ThreadPoolExecutor(max_workers=self.config['executornum']) as self.executor:
for line in file:
if not self._running:
break
try:
email_data = self.parse_email_credentials(line)
self.processed_count = 0 # 重置计数器
try:
if self.email_source == "api":
# 使用API获取邮箱
with ThreadPoolExecutor(max_workers=self.config['executornum']) as self.executor:
while self._running and (self.reg_count == 0 or self.processed_count < self.reg_count):
email_data = self.get_email_from_api()
if not email_data:
self.gui.update_status("API错误", "无法从API获取邮箱", result="失败")
break
# 跳过已完成的任务
if email_data['email'] not in self.completed_tasks:
self.executor.submit(self.process_email, email_data)
except ValueError as e:
self.gui.update_status(email_data['email'],f"解析邮箱文件失败: {e}")
else:
print(f"跳过已完成的邮箱: {email_data['email']}")
else:
# 使用本地文件获取邮箱
with open(self.email_file, 'r', encoding='utf-8') as file:
with ThreadPoolExecutor(max_workers=self.config['executornum']) as self.executor:
for line in file:
if not self._running or (self.reg_count > 0 and self.processed_count >= self.reg_count):
break
try:
email_data = self.parse_email_credentials(line)
# 跳过已完成的任务
if email_data['email'] not in self.completed_tasks:
self.executor.submit(self.process_email, email_data)
except ValueError as e:
self.gui.update_status("解析错误", f"解析邮箱文件失败: {e}")
except Exception as e:
self.gui.update_status("系统错误", f"启动任务失败: {str(e)}", result="失败")

View File

@@ -2,16 +2,16 @@
# -*- coding: utf-8 -*-
"""
Steam注册程序 - PyNoCaptcha V3版本启动器
使用PyNoCaptcha解决验证码解决了sitekey获取问题
Steam注册程序 - V3版本启动器
支持API获取邮箱账号和注册数量限制功能
"""
import tkinter as tk
from RegistrationGUIWithPynoV2 import RegistrationGUIWithPynoV2
from RegistrationGUIWithPynoV2 import SteamRegistrationGUI
if __name__ == "__main__":
print("正在启动Steam注册助手 PyNoCaptcha V3版本...")
print("这个版本修复了验证码头信息问题使用正确的User-Agent获取sitekey")
print("正在启动Steam账号注册助手 V3.0...")
print("这个版本新增自建邮箱API获取功能和注册数量限制功能")
root = tk.Tk()
gui = RegistrationGUIWithPynoV2(root)
gui = SteamRegistrationGUI(root)
root.mainloop()