import logging from typing import Any import httpx from gapido_auth.domain.errors import ExternalServiceError from gapido_auth.domain.ports import SmsClient logger = logging.getLogger(__name__) class KavenegarSmsClient(SmsClient): """Kavenegar verify/lookup implementation of the SMS provider strategy.""" def __init__( self, api_key: str, timeout_seconds: float = 10.0, transport: httpx.AsyncBaseTransport | None = None, ) -> None: """Configure credentials, timeout, and optional test transport.""" self._api_key = api_key self._timeout_seconds = timeout_seconds self._transport = transport async def send_otp(self, mobile: str, code: str, template: str) -> None: """Send an OTP through Kavenegar and raise on transport or API failure.""" url = f"https://api.kavenegar.com/v1/{self._api_key}/verify/lookup.json" payload = {"receptor": mobile, "template": template, "token": code, "type": "sms"} try: async with httpx.AsyncClient( timeout=self._timeout_seconds, transport=self._transport ) as client: response = await client.post(url, data=payload) except httpx.HTTPError as exc: logger.warning("Kavenegar network error for mobile=%s", mobile, exc_info=True) raise ExternalServiceError("Kavenegar network error") from exc if response.status_code != 200: logger.warning("Kavenegar HTTP error status=%s mobile=%s", response.status_code, mobile) raise ExternalServiceError("Kavenegar HTTP error") data: dict[str, Any] = response.json() status = data.get("return", {}).get("status") if status != 200: logger.warning("Kavenegar API error status=%s mobile=%s", status, mobile) raise ExternalServiceError("Kavenegar API error") logger.info("OTP SMS sent successfully to mobile=%s", mobile)