From 2f2fb51764c18a1760bf59bf7a47ee0df3207dd5 Mon Sep 17 00:00:00 2001 From: cnlimiter Date: Sun, 15 Mar 2026 19:10:53 +0800 Subject: [PATCH] =?UTF-8?q?refactor(config):=20=E4=BC=98=E5=8C=96Outlook?= =?UTF-8?q?=E6=8F=90=E4=BE=9B=E8=80=85=E4=BC=98=E5=85=88=E7=BA=A7=E5=92=8C?= =?UTF-8?q?=E9=AA=8C=E8=AF=81=E7=A0=81=E5=8C=B9=E9=85=8D=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 调整Outlook提供者优先级:IMAP_OLD > IMAP_NEW > Graph API - 增强OpenAI验证邮件发件人匹配模式,支持子域名 - 改进Graph API提供者的401错误处理,避免因权限不足误判 - 修复配置解析中的类型转换问题 --- src/config/constants.py | 3 ++- src/config/settings.py | 26 +++++++++++++++++---- src/services/outlook/providers/graph_api.py | 7 +++--- src/services/outlook/service.py | 6 +++-- 4 files changed, 32 insertions(+), 10 deletions(-) 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, ]