65 lines
2.3 KiB
Python
65 lines
2.3 KiB
Python
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import logging
|
|
from datetime import datetime, UTC
|
|
|
|
|
|
from .config import EmailSmtpConfigAdapter
|
|
from .smtp_client import probe_smtp
|
|
from .types import EmailAccount
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class EmailSmtpStatusAdapter:
|
|
def __init__(self):
|
|
self._config_adapter = EmailSmtpConfigAdapter()
|
|
self._last_probe: dict[str, dict] = {}
|
|
|
|
async def probe(self, account: dict) -> bool:
|
|
account_dict = self._config_adapter.resolve_account(account.get("account_id", "default"))
|
|
email_acct = EmailSmtpConfigAdapter.make_email_account(account_dict)
|
|
|
|
smtp_ok = await probe_smtp(email_acct.smtp.host, email_acct.smtp.port, email_acct.smtp.use_tls)
|
|
imap_ok = await self._probe_imap(email_acct)
|
|
|
|
result = {
|
|
"status": "ok" if (smtp_ok and imap_ok) else ("degraded" if (smtp_ok or imap_ok) else "down"),
|
|
"smtp_connected": smtp_ok,
|
|
"imap_connected": imap_ok,
|
|
"probed_at": datetime.now(UTC).isoformat(),
|
|
"account_id": account.get("account_id", "default"),
|
|
}
|
|
self._last_probe[account.get("account_id", "default")] = result
|
|
return smtp_ok and imap_ok
|
|
|
|
def build_summary(self, snapshot: object) -> dict:
|
|
account_id = getattr(snapshot, "account_id", "default")
|
|
result = self._last_probe.get(account_id, {})
|
|
return {
|
|
"channel_type": "email-smtp",
|
|
"account_id": account_id,
|
|
"status": result.get("status", "unknown"),
|
|
"smtp_ok": result.get("smtp_connected", False),
|
|
"imap_ok": result.get("imap_connected", False),
|
|
"last_probe": result.get("probed_at", ""),
|
|
}
|
|
|
|
async def _probe_imap(self, account: EmailAccount) -> bool:
|
|
from imap_tools import MailBox
|
|
|
|
try:
|
|
|
|
def _check():
|
|
with MailBox(account.imap.host, account.imap.port).login(
|
|
account.imap.username, account.imap.password, initial_folder="INBOX"
|
|
) as mb:
|
|
status = mb.folder.status("INBOX")
|
|
return status is not None
|
|
|
|
return await asyncio.to_thread(_check)
|
|
except Exception as e:
|
|
logger.debug("IMAP probe failed: %s", e)
|
|
return False
|