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