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

124 lines
4.3 KiB
Python
Raw Normal View History

import json
import logging
from monotonic import monotonic
from requests import request, RequestException
from app.clients.sms import (SmsClient, SmsClientResponseException)
from app.clients import STATISTICS_DELIVERED, STATISTICS_FAILURE
logger = logging.getLogger(__name__)
2016-04-05 14:39:59 +01:00
# Firetext will send a delivery receipt with three different status codes.
# The `firetext_response` maps these codes to the notification statistics status and notification status.
# If we get a pending (status = 2) delivery receipt followed by a declined (status = 1) delivery receipt we will set
# the notification status to temporary-failure rather than permanent failure.
# See the code in the notification_dao.update_notifications_status_by_id
firetext_responses = {
'0': {
"message": 'Delivered',
"notification_statistics_status": STATISTICS_DELIVERED,
"success": True,
"notification_status": 'delivered'
},
'1': {
"message": 'Declined',
"success": False,
"notification_statistics_status": STATISTICS_FAILURE,
"notification_status": 'permanent-failure'
},
'2': {
"message": 'Undelivered (Pending with Network)',
"success": True,
"notification_statistics_status": None,
"notification_status": 'pending'
}
}
2016-04-05 14:39:59 +01:00
def get_firetext_responses(status):
return firetext_responses[status]
class FiretextClientResponseException(SmsClientResponseException):
def __init__(self, response, exception):
status_code = response.status_code if response is not None else 504
text = response.text if response is not None else "Gateway Time-out"
self.status_code = status_code
self.text = text
self.exception = exception
def __str__(self):
return "Code {} text {} exception {}".format(self.status_code, self.text, str(self.exception))
class FiretextClient(SmsClient):
'''
FireText sms client.
'''
def init_app(self, current_app, statsd_client, *args, **kwargs):
super(SmsClient, self).__init__(*args, **kwargs)
self.current_app = current_app
self.api_key = current_app.config.get('FIRETEXT_API_KEY')
self.from_number = current_app.config.get('FROM_NUMBER')
self.name = 'firetext'
self.url = "https://www.firetext.co.uk/api/sendsms/json"
self.statsd_client = statsd_client
def get_name(self):
return self.name
def record_outcome(self, success, response):
status_code = response.status_code if response else 503
log_message = "API {} request {} on {} response status_code {}".format(
"POST",
"succeeded" if success else "failed",
self.url,
status_code
)
2016-09-23 15:59:23 +01:00
if success:
self.current_app.logger.info(log_message)
self.statsd_client.incr("clients.firetext.success")
2016-09-23 15:59:23 +01:00
else:
self.statsd_client.incr("clients.firetext.error")
self.current_app.logger.error(log_message)
def send_sms(self, to, content, reference, sender=None):
data = {
"apiKey": self.api_key,
"from": self.from_number if sender is None else sender,
"to": to.replace('+', ''),
"message": content,
"reference": reference
}
start_time = monotonic()
try:
response = request(
"POST",
self.url,
2017-04-04 16:05:25 +01:00
data=data,
timeout=60
)
response.raise_for_status()
try:
json.loads(response.text)
if response.json()['code'] != 0:
raise ValueError()
except (ValueError, AttributeError) as e:
self.record_outcome(False, response)
raise FiretextClientResponseException(response=response, exception=e)
self.record_outcome(True, response)
except RequestException as e:
self.record_outcome(False, e.response)
raise FiretextClientResponseException(response=e.response, exception=e)
finally:
elapsed_time = monotonic() - start_time
self.current_app.logger.info("Firetext request for {} finished in {}".format(reference, elapsed_time))
self.statsd_client.timing("clients.firetext.request-time", elapsed_time)
return response