"""Twilio SMS provider via Twilio's plain REST API (no SDK dependency - just an authenticated POST). No-ops with a clear error if Twilio isn't configured; the caller (alert_service) is what additionally gates on `NOTIFICATION_SMS_ENABLED` before ever constructing this provider. """ from __future__ import annotations import httpx from app.core.config import Settings from app.notifications.base import DeliveryResult, NotificationMessage _API_BASE = "https://api.twilio.com/2010-04-01" class TwilioSmsProvider: provider_name = "twilio_sms" def __init__(self, settings: Settings) -> None: self._settings = settings async def send(self, message: NotificationMessage) -> DeliveryResult: settings = self._settings if not ( settings.twilio_account_sid and settings.twilio_auth_token and settings.twilio_from_number ): return DeliveryResult(success=False, error="Twilio is not configured") url = f"{_API_BASE}/Accounts/{settings.twilio_account_sid}/Messages.json" try: async with httpx.AsyncClient(timeout=15) as client: response = await client.post( url, auth=(settings.twilio_account_sid, settings.twilio_auth_token), data={ "From": settings.twilio_from_number, "To": message.destination_value, "Body": message.body_text, }, ) except httpx.HTTPError as exc: return DeliveryResult(success=False, error=str(exc)) if response.status_code >= 400: return DeliveryResult( success=False, error=f"Twilio error {response.status_code}: {response.text[:200]}" ) data = response.json() return DeliveryResult(success=True, external_message_id=data.get("sid"))