-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhttp.py
More file actions
55 lines (43 loc) · 2.19 KB
/
Copy pathhttp.py
File metadata and controls
55 lines (43 loc) · 2.19 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import re
import httpx
from backend.app.core.config import Settings
E164_PATTERN = re.compile(r"^\+[1-9]\d{7,14}$")
class SMSDeliveryError(RuntimeError):
"""A safe SMS error that never exposes credentials or provider payloads."""
class HTTPSMSDelivery:
"""Send SMS through a provider-neutral JSON-over-HTTP gateway contract."""
def __init__(self, settings: Settings, client: httpx.Client | None = None) -> None:
self.settings = settings
self._client = client
def send(self, *, recipient: str, body: str) -> str:
if not self.settings.sms_delivery_enabled:
return "skipped"
if not self.settings.sms_delivery_configured:
raise SMSDeliveryError("SMS delivery is enabled but not fully configured")
if self.settings.app_environment == "production" and not self.settings.sms_base_url.startswith("https://"):
raise SMSDeliveryError("Production SMS delivery requires an HTTPS gateway endpoint")
if not E164_PATTERN.fullmatch(recipient):
raise SMSDeliveryError("The registered mobile number is not valid for SMS delivery")
payload = {
"recipient": recipient,
"message": body,
"sender_id": self.settings.sms_sender_id,
}
if self.settings.sms_dlt_entity_id:
payload["entity_id"] = self.settings.sms_dlt_entity_id
if self.settings.sms_dlt_template_id:
payload["template_id"] = self.settings.sms_dlt_template_id
headers = {
"Authorization": f"Bearer {self.settings.sms_api_key.get_secret_value()}",
"Content-Type": "application/json",
}
try:
if self._client is not None:
response = self._client.post(self.settings.sms_base_url, json=payload, headers=headers)
else:
with httpx.Client(timeout=self.settings.sms_request_timeout_seconds) as client:
response = client.post(self.settings.sms_base_url, json=payload, headers=headers)
response.raise_for_status()
except httpx.HTTPError as exc:
raise SMSDeliveryError("Password reset SMS could not be delivered") from exc
return "sent"