Files
any-auto-register/core/base_platform.py
2026-04-14 14:40:49 +08:00

156 lines
5.5 KiB
Python

"""平台插件基类"""
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from typing import Optional
from enum import Enum
import os
import time
class AccountStatus(str, Enum):
REGISTERED = "registered"
TRIAL = "trial"
SUBSCRIBED = "subscribed"
EXPIRED = "expired"
INVALID = "invalid"
@dataclass
class Account:
platform: str
email: str
password: str
user_id: str = ""
region: str = ""
token: str = ""
status: AccountStatus = AccountStatus.REGISTERED
trial_end_time: int = 0 # unix timestamp
extra: dict = field(default_factory=dict) # 平台自定义字段
created_at: int = field(default_factory=lambda: int(time.time()))
@dataclass
class RegisterConfig:
"""注册任务配置"""
executor_type: str = "protocol" # protocol | headless | headed
captcha_solver: str = "yescaptcha" # yescaptcha | 2captcha | manual
proxy: Optional[str] = None
extra: dict = field(default_factory=dict)
class BasePlatform(ABC):
# 子类必须定义
name: str = ""
display_name: str = ""
version: str = "1.0.0"
# 子类声明支持的执行器类型,未列出的自动降级到 protocol
supported_executors: list = ["protocol", "headless", "headed"]
def __init__(self, config: RegisterConfig = None):
self.config = config or RegisterConfig()
self._task_control = None
requested_executor = str(self.config.executor_type or "").strip() or "protocol"
if requested_executor not in self.supported_executors:
fallback = (
"protocol"
if "protocol" in self.supported_executors
else (self.supported_executors[0] if self.supported_executors else "protocol")
)
print(
f"[{self.display_name or self.name}] 执行器 '{requested_executor}' 不受支持,"
f"自动切换为 '{fallback}' (支持: {self.supported_executors})"
)
self.config.executor_type = fallback
else:
self.config.executor_type = requested_executor
@abstractmethod
def register(self, email: str, password: str = None) -> Account:
"""执行注册流程,返回 Account"""
...
@abstractmethod
def check_valid(self, account: Account) -> bool:
"""检测账号是否有效"""
...
def get_trial_url(self, account: Account) -> Optional[str]:
"""生成试用激活链接(可选实现)"""
return None
def get_platform_actions(self) -> list:
"""
返回平台支持的额外操作列表,每项格式:
{"id": str, "label": str, "params": [{"key": str, "label": str, "type": str}]}
"""
return []
def execute_action(self, action_id: str, account: Account, params: dict) -> dict:
"""
执行平台特定操作,返回 {"ok": bool, "data": any, "error": str}
"""
raise NotImplementedError(f"平台 {self.name} 不支持操作: {action_id}")
def get_quota(self, account: Account) -> dict:
"""查询账号配额(可选实现)"""
return {}
def bind_task_control(self, task_control) -> None:
"""绑定协作式任务控制器,供邮箱等待/人工跳过等场景复用。"""
self._task_control = task_control
mailbox = getattr(self, "mailbox", None)
if mailbox is not None:
mailbox._task_control = task_control
def get_mailbox_otp_timeout(self, default: int = 120) -> int:
"""统一解析邮箱 OTP 等待秒数,避免平台内散落魔法值。"""
extra = getattr(self.config, "extra", {}) or {}
candidates = (
extra.get("mailbox_otp_timeout_seconds"),
extra.get("email_otp_timeout_seconds"),
extra.get("otp_timeout"),
default,
)
for value in candidates:
if value in (None, ""):
continue
try:
resolved = int(value)
except (TypeError, ValueError):
continue
if resolved > 0:
return resolved
return default
def _make_executor(self):
"""根据 config 创建执行器"""
from .executors.protocol import ProtocolExecutor
t = self.config.executor_type
if t == "protocol":
return ProtocolExecutor(proxy=self.config.proxy)
elif t == "headless":
from .executors.playwright import PlaywrightExecutor
return PlaywrightExecutor(proxy=self.config.proxy, headless=True)
elif t == "headed":
from .executors.playwright import PlaywrightExecutor
return PlaywrightExecutor(proxy=self.config.proxy, headless=False)
raise ValueError(f"未知执行器类型: {t}")
def _make_captcha(self, **kwargs):
"""根据 config 创建验证码解决器"""
from .base_captcha import YesCaptcha, ManualCaptcha, LocalSolverCaptcha
t = self.config.captcha_solver
if t == "yescaptcha":
key = kwargs.get("key") or self.config.extra.get("yescaptcha_key", "")
return YesCaptcha(key)
elif t == "manual":
return ManualCaptcha()
elif t == "local_solver":
url = (
self.config.extra.get("solver_url")
or os.getenv("LOCAL_SOLVER_URL")
or f"http://127.0.0.1:{os.getenv('SOLVER_PORT', '8889')}"
)
return LocalSolverCaptcha(url)
raise ValueError(f"未知验证码解决器: {t}")