Files
notifications-api/app/clients/sms/aws_sns.py

121 lines
4.3 KiB
Python
Raw Permalink Normal View History

2023-08-29 13:12:18 -07:00
import os
import re
2022-06-17 11:16:23 -07:00
from time import monotonic
import botocore
import phonenumbers
2023-02-28 16:50:00 -05:00
from boto3 import client
2022-06-17 11:16:23 -07:00
from app.clients import AWS_CLIENT_CONFIG
2022-06-17 11:16:23 -07:00
from app.clients.sms import SmsClient
2023-02-28 16:50:00 -05:00
from app.cloudfoundry_config import cloud_config
2022-06-17 11:16:23 -07:00
class AwsSnsClient(SmsClient):
"""
AwsSns sms client
"""
2023-04-25 07:50:56 -07:00
def init_app(self, current_app, *args, **kwargs):
2023-08-29 13:12:18 -07:00
if os.getenv("LOCALSTACK_ENDPOINT_URL"):
self._client = client(
"sns",
region_name=cloud_config.sns_region,
aws_access_key_id=cloud_config.sns_access_key,
aws_secret_access_key=cloud_config.sns_secret_key,
config=AWS_CLIENT_CONFIG,
endpoint_url=os.getenv("LOCALSTACK_ENDPOINT_URL"),
2023-08-29 13:12:18 -07:00
)
else:
self._client = client(
"sns",
region_name=cloud_config.sns_region,
aws_access_key_id=cloud_config.sns_access_key,
aws_secret_access_key=cloud_config.sns_secret_key,
config=AWS_CLIENT_CONFIG,
)
2022-06-17 11:16:23 -07:00
super(SmsClient, self).__init__(*args, **kwargs)
self.current_app = current_app
self._valid_sender_regex = re.compile(r"^\+?\d{5,14}$")
2022-10-14 14:45:27 +00:00
2022-06-17 11:16:23 -07:00
@property
def name(self):
2023-08-29 14:54:30 -07:00
return "sns"
2022-06-17 11:16:23 -07:00
def _valid_sender_number(self, sender):
return sender and re.match(self._valid_sender_regex, sender)
2022-06-17 11:16:23 -07:00
def send_sms(self, to, content, reference, sender=None, international=False):
matched = False
2025-04-03 08:02:08 -07:00
if "+" not in to:
to = f"+{to}"
2025-04-03 08:16:33 -07:00
2025-04-03 08:02:08 -07:00
for match in phonenumbers.PhoneNumberMatcher(to, None):
2022-06-17 11:16:23 -07:00
matched = True
2023-08-29 14:54:30 -07:00
to = phonenumbers.format_number(
match.number, phonenumbers.PhoneNumberFormat.E164
)
2022-06-17 11:16:23 -07:00
# See documentation
# https://docs.aws.amazon.com/sns/latest/dg/sms_publish-to-phone.html#sms_publish_sdk
attributes = {
"AWS.SNS.SMS.SMSType": {
"DataType": "String",
"StringValue": "Transactional",
}
}
2024-11-12 15:19:52 -08:00
default_num = " ".join(self.current_app.config["AWS_US_TOLL_FREE_NUMBER"])
2024-11-12 15:09:18 -08:00
if isinstance(sender, str):
non_scrubbable = " ".join(sender)
2024-11-12 15:19:52 -08:00
2024-11-12 15:09:18 -08:00
self.current_app.logger.info(
2025-02-27 13:24:32 -08:00
f"notify-debug-api-1385 sender {non_scrubbable} is a {type(sender)} \
default is a {type(default_num)}"
2024-11-12 15:09:18 -08:00
)
else:
self.current_app.logger.warning(
2025-02-27 13:24:32 -08:00
f"notify-debug-api-1385 sender is type {type(sender)}!! {sender}"
2024-11-12 15:09:18 -08:00
)
if self._valid_sender_number(sender):
2024-11-12 13:18:05 -08:00
self.current_app.logger.info(
2025-02-27 13:24:32 -08:00
f"notify-debug-api-1385 use valid sender {non_scrubbable} instead of default {default_num}"
2024-11-12 13:18:05 -08:00
)
2022-06-17 11:16:23 -07:00
attributes["AWS.MM.SMS.OriginationNumber"] = {
"DataType": "String",
"StringValue": sender,
}
2023-02-28 16:50:00 -05:00
else:
2024-11-12 13:14:43 -08:00
self.current_app.logger.info(
2025-02-27 13:24:32 -08:00
f"notify-debug-api-1385 use default {default_num} instead of invalid sender"
2024-11-12 13:14:43 -08:00
)
2024-11-12 14:40:18 -08:00
2022-06-17 11:16:23 -07:00
attributes["AWS.MM.SMS.OriginationNumber"] = {
"DataType": "String",
"StringValue": self.current_app.config["AWS_US_TOLL_FREE_NUMBER"],
}
try:
start_time = monotonic()
2023-08-29 14:54:30 -07:00
response = self._client.publish(
PhoneNumber=to, Message=content, MessageAttributes=attributes
)
2022-06-17 11:16:23 -07:00
except botocore.exceptions.ClientError as e:
self.current_app.logger.exception("An error occurred sending sms")
2022-06-17 11:16:23 -07:00
raise str(e)
except Exception as e:
self.current_app.logger.exception("An error occurred sending sms")
2022-06-17 11:16:23 -07:00
raise str(e)
finally:
elapsed_time = monotonic() - start_time
2023-08-29 14:54:30 -07:00
self.current_app.logger.info(
"AWS SNS request finished in {}".format(elapsed_time)
)
2022-06-17 11:16:23 -07:00
return response["MessageId"]
if not matched:
self.current_app.logger.error("No valid numbers found in {}".format(to))
raise ValueError("No valid numbers found for SMS delivery")