diff --git a/CONFIG_GUIDE.md b/CONFIG_GUIDE.md index 9915325..6694cad 100644 --- a/CONFIG_GUIDE.md +++ b/CONFIG_GUIDE.md @@ -432,6 +432,11 @@ ocxxxxxxx@cursors.online 拿到什么就写什么,缺失字段会写空值或 `NULL`。 +现在成功和失败都会尽量入库: + +- 成功时:`oauth_status = oauth=ok` +- 失败时:`oauth_status = oauth=failed` + 关于 `auto_register_max_per_loop`: - `0`:按当前差值动态补齐 diff --git a/README.md b/README.md index 545866d..56ab1f0 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,8 @@ python3 /root/standalone_cli/run.py config setup 当前你这边实际使用的数据库名是:`mail_accounts_db` +现在不仅 `oauth=ok` 的成功注册会入库,`oauth=failed` 的失败注册也会尽量记录当前已拿到的信息。 + 当前这台机器实测可用的代理是: - `http://127.0.0.1:17891` @@ -219,6 +221,7 @@ docker compose up --build - 按配置周期检查号池状态 - 号池不足且 `auto_register = true` 时自动补号 - 按 `threshold - candidates` 估算补号差值;当 `auto_register_max_per_loop = 0` 时按差值动态补齐,否则受该值限制 +- 单个补号失败不会终止整轮补号,会继续尝试后续账号 - 号池满足阈值时不执行注册 - 按 `maintain_interval_minutes` / `sub2api_maintain_interval_minutes` 自动维护 diff --git a/main.py b/main.py index 37353b8..e63d4b8 100644 --- a/main.py +++ b/main.py @@ -226,6 +226,34 @@ def _perform_registration_once(cfg: dict[str, Any], proxy: Optional[str]) -> dic token_data = json.loads(token_json) email = str(token_data.get("email") or "unknown") + oauth_status = str(token_data.get("oauth_status") or "oauth=ok").strip() or "oauth=ok" + + if oauth_status != "oauth=ok": + run_result: dict[str, Any] = {"ok": False, "email": email, "oauth_status": oauth_status} + if cfg.get("db_enabled"): + try: + db_result = save_registered_account( + cfg, + { + "email": token_data.get("email", ""), + "chatgpt_password": token_data.get("account_password", ""), + "mail_password": token_data.get("mail_password", ""), + "oauth_status": oauth_status, + "mail_token": token_data.get("mail_token", ""), + "name": token_data.get("name", ""), + "birthdate": token_data.get("birthdate", ""), + "source": cfg.get("db_source", "standalone_cli"), + }, + ) + run_result["db_saved"] = bool(db_result.get("ok", False)) + print(f"[{'+' if db_result.get('ok') else '-'}] 失败记录{'写入成功' if db_result.get('ok') else '写入失败'}: {email}") + except Exception as exc: + run_result["db_saved"] = False + run_result["db_error"] = str(exc) + print(f"[-] 失败记录写入异常: {exc}") + print("[-] 本次注册失败。") + return run_result + file_name = f"token_{email.replace('@', '_')}_{time.time_ns()}.json" file_path = Path(TOKENS_DIR) / file_name _write_text_atomic(str(file_path), token_json) @@ -366,11 +394,20 @@ def handle_daemon(args: argparse.Namespace) -> dict[str, Any]: try: result = _perform_registration_once(cfg, proxy) if not result.get("ok"): - print("[!] 本次自动补号失败,停止本轮剩余补号") - break + email = str(result.get("email") or "").strip() + status = str(result.get("oauth_status") or result.get("error") or "failed").strip() + if email: + print(f"[!] 本次自动补号失败,继续下一个: {email} ({status})") + else: + print(f"[!] 本次自动补号失败,继续下一个: {status}") + continue + if index < planned - 1: + wait_seconds = random.randint(30, 60) + print(f"[*] 本次注册成功,等待 {wait_seconds} 秒后继续下一个账号...") + time.sleep(wait_seconds) except Exception as exc: - print(f"[Error] 自动补号失败: {exc}") - break + print(f"[Error] 自动补号异常,继续下一个: {exc}") + continue else: print("[+] 当前号池满足阈值,本轮不执行注册") elif bool(cfg.get("auto_register", False)): diff --git a/openai_pool_orchestrator/register.py b/openai_pool_orchestrator/register.py index 288c36d..8c552b0 100755 --- a/openai_pool_orchestrator/register.py +++ b/openai_pool_orchestrator/register.py @@ -791,6 +791,34 @@ def _build_token_result( return json.dumps(config, ensure_ascii=False, separators=(",", ":")) +def _build_registration_failure_result( + *, + email: str = "", + account_password: str = "", + mail_password: str = "", + mail_token: str = "", + name: str = "", + birthdate: str = "", + oauth_status: str = "oauth=failed", + failure_step: str = "", + error_message: str = "", +) -> str: + payload = { + "email": str(email or "").strip(), + "account_password": str(account_password or "").strip(), + "mail_password": str(mail_password or "").strip(), + "mail_token": str(mail_token or "").strip(), + "name": str(name or "").strip(), + "birthdate": str(birthdate or "").strip(), + "oauth_status": str(oauth_status or "oauth=failed").strip() or "oauth=failed", + } + if failure_step: + payload["failure_step"] = str(failure_step).strip() + if error_message: + payload["error_message"] = str(error_message).strip() + return json.dumps(payload, ensure_ascii=False, separators=(",", ":")) + + def _write_text_atomic(file_path: str, content: str) -> None: directory = os.path.dirname(file_path) or "." os.makedirs(directory, exist_ok=True) @@ -896,6 +924,19 @@ def run( mail_provider=None, proxy_pool_config: Optional[Dict[str, Any]] = None, ) -> Optional[str]: + def _failure_result(step: str, error_message: str = "") -> str: + return _build_registration_failure_result( + email=locals().get("email", ""), + account_password=locals().get("account_password", ""), + mail_password=locals().get("mailbox_password", ""), + mail_token=locals().get("mailbox_token", ""), + name=locals().get("_rand_name", ""), + birthdate=locals().get("_rand_bday", ""), + oauth_status="oauth=failed", + failure_step=step, + error_message=error_message, + ) + static_proxy = _normalize_proxy_value(proxy) static_proxies: Any = _to_proxies_dict(static_proxy) @@ -1325,12 +1366,12 @@ def run( emitter.info(f"当前出口 IP: {current_ip}", step="check_proxy") if loc == "CN" or loc == "HK": emitter.error("检查代理哦 — 所在地不支持 (CN/HK)", step="check_proxy") - return None + return _failure_result("check_proxy", "所在地不支持 (CN/HK)") emitter.success("网络环境检查通过", step="check_proxy") _ensure_openai_relay_ready() except Exception as e: emitter.error(f"网络连接检查失败: {e}", step="check_proxy") - return None + return _failure_result("check_proxy", f"网络连接检查失败: {e}") if _stopped(): return None @@ -1360,7 +1401,7 @@ def run( mailbox_token = str(dev_token or "").strip() if not email or not dev_token: emitter.error("临时邮箱创建失败", step="create_email") - return None + return _failure_result("create_email", "临时邮箱创建失败") emitter.success(f"临时邮箱创建成功: {email}", step="create_email") # 生成随机密码(密码注册流程需要) @@ -1389,7 +1430,7 @@ def run( csrf_token = "" if not csrf_token: emitter.error("获取 CSRF Token 失败", step="oauth_init") - return None + return _failure_result("oauth_init", "获取 CSRF Token 失败") # 3c: 生成 Device ID did = s.cookies.get("oai-did") or relay_cookie_jar.get("oai-did") or "" @@ -1437,7 +1478,7 @@ def run( f"Signin 获取授权链接失败({signin_resp.status_code}): {str(signin_resp.text or '')[:220]}", step="oauth_init", ) - return None + return _failure_result("oauth_init", f"Signin 获取授权链接失败({signin_resp.status_code})") emitter.info(f"OAuth 初始化状态: {signin_resp.status_code}", step="oauth_init") # 3e: 跟随 authorize 重定向,建立 auth.openai.com 会话 @@ -1478,7 +1519,7 @@ def run( f"注册表单提交失败(状态码 {signup_resp.status_code}): {str(signup_resp.text or '')[:220]}", step="signup", ) - return None + return _failure_result("signup", f"注册表单提交失败(状态码 {signup_resp.status_code})") # ------- 步骤6:发送 OTP 验证码 ------- time.sleep(random.uniform(0.3, 0.8)) @@ -1497,7 +1538,7 @@ def run( if otp_resp.status_code != 200: emitter.error(f"验证码发送失败(状态码 {otp_resp.status_code}): {str(otp_resp.text or '')[:220]}", step="send_otp") - return None + return _failure_result("send_otp", f"验证码发送失败(状态码 {otp_resp.status_code})") if _stopped(): return None @@ -1529,7 +1570,7 @@ def run( proxy_selector=None, ) if not code: - return None + return _failure_result("wait_otp", "邮箱验证码获取失败") if _stopped(): return None @@ -1555,7 +1596,7 @@ def run( f"验证码校验失败(状态码 {code_resp.status_code}): {str(code_resp.text or '')[:220]}", step="verify_otp", ) - return None + return _failure_result("verify_otp", f"验证码校验失败(状态码 {code_resp.status_code})") if _stopped(): return None @@ -1591,7 +1632,7 @@ def run( if create_account_status != 200: emitter.error(create_account_resp.text, step="create_account") - return None + return _failure_result("create_account", str(create_account_resp.text or "")[:220]) emitter.success("账户创建成功!", step="create_account") @@ -1776,7 +1817,7 @@ def run( emitter.info("OAuth 需要邮箱 OTP 验证...", step="get_token") if not dev_token or mail_provider is None: emitter.error("OAuth OTP 验证需要邮箱 token,但不可用", step="get_token") - return None + return _failure_result("get_token", "OAuth OTP 验证需要邮箱 token,但不可用") _otp_ok = False _otp_deadline = time.time() + 120 @@ -1819,7 +1860,7 @@ def run( if not _otp_ok: emitter.error(f"OAuth OTP 验证失败,已尝试 {len(_tried_codes)} 个验证码", step="get_token") - return None + return _failure_result("get_token", f"OAuth OTP 验证失败,已尝试 {len(_tried_codes)} 个验证码") if _stopped(): return None @@ -2003,7 +2044,7 @@ def run( emitter.error("未能获取 OAuth authorization code", step="get_token") try: s.close() except: pass - return None + return _failure_result("get_token", "未能获取 OAuth authorization code") # 10g: POST /oauth/token — 用 code 换取 Token emitter.info("OAuth 5/5: 交换 Token...", step="get_token") @@ -2023,7 +2064,7 @@ def run( emitter.error(f"Token 交换失败({_token_resp.status_code}): {str(_token_resp.text or '')[:200]}", step="get_token") try: s.close() except: pass - return None + return _failure_result("get_token", f"Token 交换失败({_token_resp.status_code})") try: _token_json = _token_resp.json() @@ -2046,7 +2087,7 @@ def run( emitter.error(f"运行时发生错误: {e}", step="runtime") try: s.close() except: pass - return None + return _failure_result("runtime", str(e)) # ========================================== # CLI 入口(兼容直接运行)