"""持久化网页配置:账号数 / 接码 URL / 邮件助手 / CPA 等。""" from __future__ import annotations import json import os import threading from dataclasses import asdict, dataclass, field, fields CONFIG_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "config.local.json") _LOCK = threading.Lock() @dataclass class AppConfig: # 注册控制 account_count: int = 1 headless: bool = False # 邮件 mail_helper_url: str = "http://ali.ss5.xyz:17373" mail_domain: str = "edu.a4sky.com" mail_poll_interval_sec: int = 4 mail_poll_max_attempts: int = 60 # 4s * 60 = 4min # PayPal / 接码 phone_e164: str = "+15822201173" sms_api_url: str = "http://a.62-us.com/api/get_sms?key=a5d3262e05efaba982aba7cfae20b8bc" # 代理(两个字段独立配置): # proxy_url — 全局代理(ChatGPT 注册 / 长链 / PayPal 都走)。空 = 全程直连。 # paypal_only_proxy — 仅 PayPal 阶段用的代理。空 = PayPal 沿用 proxy_url(或直连)。 # 典型搭配:proxy_url 留空(注册直连,避免代理屏蔽 chatgpt.com),paypal_only_proxy 填代理。 proxy_url: str = "" paypal_only_proxy: str = "" paypal_proxy: str = "" # 旧字段(保留向后兼容;如填了且 paypal_only_proxy/proxy_url 都为空,会迁移到 paypal_only_proxy) # CPA cpa_url: str = "" cpa_management_key: str = "" # 调试 use_promo: bool = True # 长链生成方式 # payurl — 走 payurl.ark2.cn 第三方中转(默认) # local — 本地直连 chatgpt.com/backend-api/payments/checkout(需代理) long_link_mode: str = "payurl" long_link_proxy: str = "http://dinzitsz:n6wtkhg64jqq@82.24.43.35:6242/" # 本地长链生成用的代理(仅 local 模式生效) # API / 外网访问 api_host: str = "0.0.0.0" # 改成 "0.0.0.0" 即可外网访问 api_port: int = 7791 api_token: str = "" # 留空 = 不校验;非空时所有 /api/* 请求需 Authorization: Bearer api_cors_origin: str = "*" # CORS Allow-Origin,可填具体域名或 * @classmethod def load(cls) -> "AppConfig": if not os.path.exists(CONFIG_PATH): return cls() try: with open(CONFIG_PATH, "r", encoding="utf-8") as f: raw = json.load(f) except Exception: return cls() valid = {f.name for f in fields(cls)} cleaned = {k: v for k, v in (raw or {}).items() if k in valid} return cls(**cleaned) def save(self): with _LOCK: with open(CONFIG_PATH, "w", encoding="utf-8") as f: json.dump(asdict(self), f, ensure_ascii=False, indent=2) def update(self, patch: dict) -> "AppConfig": valid = {f.name for f in fields(self)} for k, v in (patch or {}).items(): if k not in valid: continue current = getattr(self, k) if isinstance(current, bool): setattr(self, k, bool(v) if not isinstance(v, str) else v.lower() in ("1", "true", "yes", "on")) elif isinstance(current, int): try: setattr(self, k, int(v)) except Exception: pass else: setattr(self, k, "" if v is None else str(v)) # 向后兼容:旧字段 paypal_proxy → 新字段 paypal_only_proxy(之前一段时间它被错误地等价于 proxy_url) if self.paypal_proxy and not self.paypal_only_proxy and not self.proxy_url: self.paypal_only_proxy = self.paypal_proxy self.save() return self @property def effective_global_proxy(self) -> str: """ChatGPT 注册 / 长链 / 默认浏览器 context 用的代理。空 = 直连。""" return (self.proxy_url or "").strip() @property def effective_paypal_proxy(self) -> str: """PayPal 阶段用的代理,独立字段优先;否则继承 proxy_url。空 = 直连。""" return (self.paypal_only_proxy or self.proxy_url or "").strip() # 兼容旧调用:先前 chatgpt_flow 用了 effective_proxy @property def effective_proxy(self) -> str: return self.effective_global_proxy