from pydantic_settings import BaseSettings from pydantic import Field, SecretStr from typing import Optional class Settings(BaseSettings): # MCP Server server_name: str = Field(default="PIM MCP Server", alias="SERVER_NAME") server_port: int = Field(default=8000, alias="PORT") server_host: str = Field(default="0.0.0.0", alias="HOST") environment: str = Field(default="development", alias="ENVIRONMENT") # API Authentication mcp_api_key: Optional[SecretStr] = Field(default=None, alias="MCP_API_KEY") # IMAP Configuration imap_host: Optional[str] = Field(default=None, alias="IMAP_HOST") imap_port: int = Field(default=993, alias="IMAP_PORT") imap_username: Optional[str] = Field(default=None, alias="IMAP_USERNAME") imap_password: Optional[SecretStr] = Field(default=None, alias="IMAP_PASSWORD") imap_use_ssl: bool = Field(default=True, alias="IMAP_USE_SSL") # SMTP Configuration smtp_host: Optional[str] = Field(default=None, alias="SMTP_HOST") smtp_port: int = Field(default=587, alias="SMTP_PORT") smtp_username: Optional[str] = Field(default=None, alias="SMTP_USERNAME") smtp_password: Optional[SecretStr] = Field(default=None, alias="SMTP_PASSWORD") smtp_use_tls: bool = Field(default=True, alias="SMTP_USE_TLS") smtp_from_email: Optional[str] = Field(default=None, alias="SMTP_FROM_EMAIL") smtp_from_name: Optional[str] = Field(default=None, alias="SMTP_FROM_NAME") # CalDAV Configuration caldav_url: Optional[str] = Field(default=None, alias="CALDAV_URL") caldav_username: Optional[str] = Field(default=None, alias="CALDAV_USERNAME") caldav_password: Optional[SecretStr] = Field(default=None, alias="CALDAV_PASSWORD") ics_calendars: Optional[str] = Field(default=None, alias="ICS_CALENDARS") ics_calendar_timeout: int = Field(default=20, alias="ICS_CALENDAR_TIMEOUT") # CardDAV Configuration carddav_url: Optional[str] = Field(default=None, alias="CARDDAV_URL") carddav_username: Optional[str] = Field(default=None, alias="CARDDAV_USERNAME") carddav_password: Optional[SecretStr] = Field(default=None, alias="CARDDAV_PASSWORD") contacts_addressbook_url: Optional[str] = Field( default=None, alias="CONTACTS_ADDRESSBOOK_URL", ) # SQLite Cache sqlite_path: str = Field(default="/data/cache.db", alias="SQLITE_PATH") cache_ttl_seconds: int = Field(default=300, alias="CACHE_TTL_SECONDS") # Feature Flags enable_email: bool = Field(default=True, alias="ENABLE_EMAIL") enable_calendar: bool = Field(default=True, alias="ENABLE_CALENDAR") enable_contacts: bool = Field(default=True, alias="ENABLE_CONTACTS") # Email Notification Settings enable_email_notifications: bool = Field( default=False, alias="ENABLE_EMAIL_NOTIFICATIONS" ) notification_mailboxes: str = Field( default="INBOX", alias="NOTIFICATION_MAILBOXES", ) notification_poll_interval: int = Field( default=60, alias="NOTIFICATION_POLL_INTERVAL", ) notification_idle_timeout: int = Field( default=1680, # 28 minutes (RFC recommends refresh before 29 min) alias="NOTIFICATION_IDLE_TIMEOUT", ) # Poke Webhook Settings poke_webhook_url: Optional[str] = Field( default="https://poke.com/api/v1/inbound-sms/webhook", alias="POKE_WEBHOOK_URL", ) poke_api_key: Optional[SecretStr] = Field( default=None, alias="POKE_API_KEY", ) poke_webhook_timeout: int = Field( default=30, alias="POKE_WEBHOOK_TIMEOUT", ) poke_webhook_max_retries: int = Field( default=3, alias="POKE_WEBHOOK_MAX_RETRIES", ) model_config = { "env_file": ".env", "env_file_encoding": "utf-8", "populate_by_name": True, "extra": "ignore", } def is_email_configured(self) -> bool: return all([ self.enable_email, self.imap_host, self.imap_username, self.imap_password, ]) def is_smtp_configured(self) -> bool: return all([ self.smtp_host, self.smtp_username, self.smtp_password, self.smtp_from_email, ]) def is_caldav_configured(self) -> bool: return all([ self.enable_calendar, self.caldav_url, self.caldav_username, self.caldav_password, ]) def is_calendar_configured(self) -> bool: return all([ self.enable_calendar, (self.is_caldav_configured() or self.get_ics_calendars()), ]) def get_ics_calendars(self) -> list[tuple[Optional[str], str]]: if not self.ics_calendars: return [] calendars: list[tuple[Optional[str], str]] = [] for entry in self.ics_calendars.split(","): item = entry.strip() if not item: continue if "|" in item: name, url = item.split("|", 1) name = name.strip() or None url = url.strip() else: name = None url = item if url: calendars.append((name, url)) return calendars def is_contacts_configured(self) -> bool: return all([ self.enable_contacts, self.carddav_url, self.carddav_username, self.carddav_password, self.contacts_addressbook_url, ]) def is_notification_configured(self) -> bool: return all([ self.enable_email_notifications, self.is_email_configured(), self.poke_api_key, self.poke_webhook_url, ]) def get_notification_mailboxes(self) -> list[str]: return [m.strip() for m in self.notification_mailboxes.split(",") if m.strip()] settings = Settings()