Add Yaohuo verification-based self-service signup

This commit is contained in:
zeer
2026-04-15 15:36:50 +08:00
parent de130f1052
commit a65b67485e
9 changed files with 568 additions and 20 deletions

View File

@@ -1,8 +1,13 @@
from __future__ import annotations
import logging
import random
import re
from datetime import datetime, timedelta, timezone
from typing import Any
import requests
from .graph import GraphAPIError, GraphClient, TokenManager
from .settings import Settings
@@ -191,3 +196,72 @@ class Office365Service:
if "already exists" in lowered or "another object with the same value" in lowered:
status_code = 409
return ServiceOperationError(message=message, status_code=status_code, details=exc.response)
class YaohuoVerificationService:
def __init__(self, settings: Settings):
self.settings = settings
def verification_ready(self) -> bool:
return self.settings.yaohuo_verification_enabled and bool(self.settings.yaohuo_cookie)
def ensure_ready(self) -> None:
if not self.settings.yaohuo_verification_enabled:
raise ServiceConfigurationError("妖火论坛验证功能未启用。")
if not self.settings.yaohuo_cookie:
raise ServiceConfigurationError("YAOHUO_COOKIE 未配置,无法发送妖火私信验证码。")
def generate_code(self) -> str:
return f"{random.randint(0, 999999):06d}"
def expires_at(self) -> datetime:
return datetime.now(timezone.utc) + timedelta(seconds=self.settings.yaohuo_verification_code_ttl_seconds)
def send_verification_code(self, target_user_id: str, code: str) -> None:
self.ensure_ready()
normalized_user_id = self._normalize_target_user_id(target_user_id)
content = f"【Office 365 自助开通验证】您的验证码是:{code}{self.settings.yaohuo_verification_code_ttl_seconds // 60} 分钟内有效。"
payload = {
"touseridlist": normalized_user_id,
"content": content,
"action": "gomod",
"classid": "0",
"siteid": "1000",
"types": "",
"issystem": "",
"g": "发送消息",
}
try:
response = requests.post(
self.settings.yaohuo_message_url,
data=payload,
headers={
"Cookie": self.settings.yaohuo_cookie,
"Referer": self.settings.yaohuo_message_url,
"User-Agent": "Mozilla/5.0",
},
timeout=30,
)
except requests.RequestException as exc:
raise ServiceOperationError(f"发送妖火验证码失败: {exc}", status_code=502) from exc
if response.status_code >= 400:
raise ServiceOperationError(
f"发送妖火验证码失败,状态码 {response.status_code}",
status_code=502,
)
body = response.text
if "发短信息" in body and "发送成功" not in body and "返回上级" in body:
logger.warning("妖火私信发送结果无法明确判断成功,按成功处理。")
return
if any(keyword in body for keyword in ("成功", "发送成功", "发送完毕")):
return
def _normalize_target_user_id(self, target_user_id: str) -> str:
normalized = re.sub(r"\s+", "", str(target_user_id or ""))
if not normalized or not normalized.isdigit():
raise ValueError("请输入有效的妖火 ID。")
return normalized