Spaces:
Paused
Paused
| """ | |
| Outlook 服务基础定义 | |
| 包含枚举类型和数据类 | |
| """ | |
| from dataclasses import dataclass, field | |
| from datetime import datetime | |
| from enum import Enum | |
| from typing import Optional, Dict, Any, List | |
| class ProviderType(str, Enum): | |
| """Outlook 提供者类型""" | |
| IMAP_OLD = "imap_old" # 旧版 IMAP (outlook.office365.com) | |
| IMAP_NEW = "imap_new" # 新版 IMAP (outlook.live.com) | |
| GRAPH_API = "graph_api" # Microsoft Graph API | |
| class TokenEndpoint(str, Enum): | |
| """Token 端点""" | |
| LIVE = "https://login.live.com/oauth20_token.srf" | |
| CONSUMERS = "https://login.microsoftonline.com/consumers/oauth2/v2.0/token" | |
| COMMON = "https://login.microsoftonline.com/common/oauth2/v2.0/token" | |
| class IMAPServer(str, Enum): | |
| """IMAP 服务器""" | |
| OLD = "outlook.office365.com" | |
| NEW = "outlook.live.com" | |
| class ProviderStatus(str, Enum): | |
| """提供者状态""" | |
| HEALTHY = "healthy" # 健康 | |
| DEGRADED = "degraded" # 降级 | |
| DISABLED = "disabled" # 禁用 | |
| class EmailMessage: | |
| """邮件消息数据类""" | |
| id: str # 消息 ID | |
| subject: str # 主题 | |
| sender: str # 发件人 | |
| recipients: List[str] = field(default_factory=list) # 收件人列表 | |
| body: str = "" # 正文内容 | |
| body_preview: str = "" # 正文预览 | |
| received_at: Optional[datetime] = None # 接收时间 | |
| received_timestamp: int = 0 # 接收时间戳 | |
| is_read: bool = False # 是否已读 | |
| has_attachments: bool = False # 是否有附件 | |
| raw_data: Optional[bytes] = None # 原始数据(用于调试) | |
| def to_dict(self) -> Dict[str, Any]: | |
| """转换为字典""" | |
| return { | |
| "id": self.id, | |
| "subject": self.subject, | |
| "sender": self.sender, | |
| "recipients": self.recipients, | |
| "body": self.body, | |
| "body_preview": self.body_preview, | |
| "received_at": self.received_at.isoformat() if self.received_at else None, | |
| "received_timestamp": self.received_timestamp, | |
| "is_read": self.is_read, | |
| "has_attachments": self.has_attachments, | |
| } | |
| class TokenInfo: | |
| """Token 信息数据类""" | |
| access_token: str | |
| expires_at: float # 过期时间戳 | |
| token_type: str = "Bearer" | |
| scope: str = "" | |
| refresh_token: Optional[str] = None | |
| def is_expired(self, buffer_seconds: int = 120) -> bool: | |
| """检查 Token 是否已过期""" | |
| import time | |
| return time.time() >= (self.expires_at - buffer_seconds) | |
| def from_response(cls, data: Dict[str, Any], scope: str = "") -> "TokenInfo": | |
| """从 API 响应创建""" | |
| import time | |
| return cls( | |
| access_token=data.get("access_token", ""), | |
| expires_at=time.time() + data.get("expires_in", 3600), | |
| token_type=data.get("token_type", "Bearer"), | |
| scope=scope or data.get("scope", ""), | |
| refresh_token=data.get("refresh_token"), | |
| ) | |
| class ProviderHealth: | |
| """提供者健康状态""" | |
| provider_type: ProviderType | |
| status: ProviderStatus = ProviderStatus.HEALTHY | |
| failure_count: int = 0 # 连续失败次数 | |
| last_success: Optional[datetime] = None # 最后成功时间 | |
| last_failure: Optional[datetime] = None # 最后失败时间 | |
| last_error: str = "" # 最后错误信息 | |
| disabled_until: Optional[datetime] = None # 禁用截止时间 | |
| def record_success(self): | |
| """记录成功""" | |
| self.status = ProviderStatus.HEALTHY | |
| self.failure_count = 0 | |
| self.last_success = datetime.now() | |
| self.disabled_until = None | |
| def record_failure(self, error: str): | |
| """记录失败""" | |
| self.failure_count += 1 | |
| self.last_failure = datetime.now() | |
| self.last_error = error | |
| def should_disable(self, threshold: int = 3) -> bool: | |
| """判断是否应该禁用""" | |
| return self.failure_count >= threshold | |
| def is_disabled(self) -> bool: | |
| """检查是否被禁用""" | |
| if self.disabled_until and datetime.now() < self.disabled_until: | |
| return True | |
| return False | |
| def disable(self, duration_seconds: int = 300): | |
| """禁用提供者""" | |
| from datetime import timedelta | |
| self.status = ProviderStatus.DISABLED | |
| self.disabled_until = datetime.now() + timedelta(seconds=duration_seconds) | |
| def enable(self): | |
| """启用提供者""" | |
| self.status = ProviderStatus.HEALTHY | |
| self.disabled_until = None | |
| self.failure_count = 0 | |
| def to_dict(self) -> Dict[str, Any]: | |
| """转换为字典""" | |
| return { | |
| "provider_type": self.provider_type.value, | |
| "status": self.status.value, | |
| "failure_count": self.failure_count, | |
| "last_success": self.last_success.isoformat() if self.last_success else None, | |
| "last_failure": self.last_failure.isoformat() if self.last_failure else None, | |
| "last_error": self.last_error, | |
| "disabled_until": self.disabled_until.isoformat() if self.disabled_until else None, | |
| } | |