diff --git a/src/config/constants.py b/src/config/constants.py index 79ead1c..c6be6ae 100644 --- a/src/config/constants.py +++ b/src/config/constants.py @@ -132,7 +132,8 @@ OTP_CODE_SEMANTIC_PATTERN = r'(?:code\s+is|验证码[是为]?\s*[::]?\s*)(\d{6 OPENAI_EMAIL_SENDERS = [ "noreply@openai.com", "no-reply@openai.com", - "@openai.com", # 通配符匹配 + "@openai.com", # 精确域名匹配 + ".openai.com", # 子域名匹配(如 otp@tm1.openai.com) ] # OpenAI 验证邮件关键词 diff --git a/src/config/settings.py b/src/config/settings.py index 45801ad..2d3fb1a 100644 --- a/src/config/settings.py +++ b/src/config/settings.py @@ -301,7 +301,7 @@ SETTING_DEFINITIONS: Dict[str, SettingDefinition] = { # Outlook 配置 "outlook_provider_priority": SettingDefinition( db_key="outlook.provider_priority", - default_value=["graph_api", "imap_new", "imap_old"], + default_value=["imap_old", "imap_new", "graph_api"], category=SettingCategory.EMAIL, description="Outlook 提供者优先级" ), @@ -373,13 +373,31 @@ def _convert_value(attr_name: str, value: str) -> Any: elif target_type == dict: if isinstance(value, dict): return value + if not value: + return {} import json - return json.loads(value) if value else {} + import ast + try: + return json.loads(value) + except (json.JSONDecodeError, ValueError): + try: + return ast.literal_eval(value) + except Exception: + return {} elif target_type == list: if isinstance(value, list): return value + if not value: + return [] import json - return json.loads(value) if value else [] + import ast + try: + return json.loads(value) + except (json.JSONDecodeError, ValueError): + try: + return ast.literal_eval(value) + except Exception: + return [] else: return value @@ -568,7 +586,7 @@ class Settings(BaseModel): email_code_poll_interval: int = 3 # Outlook 配置 - outlook_provider_priority: List[str] = ["graph_api", "imap_new", "imap_old"] + outlook_provider_priority: List[str] = ["imap_old", "imap_new", "graph_api"] outlook_health_failure_threshold: int = 5 outlook_health_disable_duration: int = 60 outlook_default_client_id: str = "24d9a0ed-8787-4584-883c-2fd79308940a" diff --git a/src/services/outlook/providers/graph_api.py b/src/services/outlook/providers/graph_api.py index 13af5d8..00cec80 100644 --- a/src/services/outlook/providers/graph_api.py +++ b/src/services/outlook/providers/graph_api.py @@ -145,11 +145,12 @@ class GraphAPIProvider(OutlookProvider): ) if resp.status_code == 401: - # Token 失效,清除缓存 + # Token 无 Graph 权限(client_id 未授权),清除缓存但不记录健康失败 + # 避免因权限不足导致健康检查器禁用该提供者,影响其他账户 if self._token_manager: self._token_manager.clear_cache() - self.record_failure(f"HTTP 401: Token 失效") - logger.error(f"[{self.account.email}] Graph API Token 失效") + self._connected = False + logger.warning(f"[{self.account.email}] Graph API 返回 401,client_id 可能无 Graph 权限,跳过") return [] if resp.status_code != 200: diff --git a/src/services/outlook/service.py b/src/services/outlook/service.py index b700a29..321d8b3 100644 --- a/src/services/outlook/service.py +++ b/src/services/outlook/service.py @@ -25,10 +25,12 @@ logger = logging.getLogger(__name__) # 默认提供者优先级 +# IMAP_OLD 最兼容(只需 login.live.com token),IMAP_NEW 次之,Graph API 最后 +# 原因:部分 client_id 没有 Graph API 权限,但有 IMAP 权限 DEFAULT_PROVIDER_PRIORITY = [ - ProviderType.GRAPH_API, - ProviderType.IMAP_NEW, ProviderType.IMAP_OLD, + ProviderType.IMAP_NEW, + ProviderType.GRAPH_API, ]