Created
February 15, 2022 14:43
-
-
Save yezz123/d0f592af45eb46c28a2e45cb1e1ab2f9 to your computer and use it in GitHub Desktop.
A simple way to test Twilio SMS Sender
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from __future__ import unicode_literals | |
import logging | |
from django.conf import settings | |
from django.utils.encoding import force_text | |
from twilio.rest import Client | |
from decimal import Decimal | |
from pytz import timezone | |
try: | |
from django.urls import reverse | |
except ImportError: | |
from django.core.urlresolvers import reverse | |
from .models import OutgoingSMS | |
logger = logging.getLogger("client") | |
def build_callback_url(request, urlname, message): | |
location = reverse(urlname, kwargs={"pk": message.pk}) | |
callback_domain = getattr(settings, "TWILIO_CALLBACK_DOMAIN", None) | |
if callback_domain: | |
url = "{}://{}{}".format( | |
"https" if getattr(settings, "TWILIO_CALLBACK_USE_HTTPS", False) else "http", | |
callback_domain, | |
location | |
) | |
elif request is not None: | |
url = request.build_absolute_uri(location) | |
else: | |
raise ValueError( | |
"Unable to build callback url. Configure TWILIO_CALLBACK_DOMAIN " | |
"or pass request object to function call" | |
) | |
return url | |
def send_sms(request, to_number, body, callback_urlname="sms_status_callback"): | |
client = Client(settings.TWILIO_ACCOUNT_SID, settings.TWILIO_AUTH_TOKEN) | |
from_number = settings.TWILIO_PHONE_NUMBER | |
message = OutgoingSMS.objects.create( | |
from_number=from_number, | |
to_number=to_number, | |
body=body, | |
) | |
status_callback = None | |
if callback_urlname: | |
status_callback = build_callback_url(request, callback_urlname, message) | |
logger.debug("Sending SMS message to %s with callback url %s: %s.", | |
to_number, status_callback, body) | |
if not getattr(settings, "TWILIO_DRY_MODE", False): | |
sent = client.messages.create( | |
to=to_number, | |
from_=from_number, | |
body=body, | |
status_callback=status_callback | |
) | |
logger.debug("SMS message sent: %s", sent.__dict__) | |
message.sms_sid = sent.sid | |
message.account_sid = sent.account_sid | |
message.status = sent.status | |
message.to_parsed = sent.to | |
if sent.price: | |
message.price = Decimal(force_text(sent.price)) | |
message.price_unit = sent.price_unit | |
# Messages returned from Twilio are in UTC | |
message.sent_at = sent.date_created.replace(tzinfo=timezone('UTC')) | |
message.save(update_fields=["sms_sid", "account_sid", "status", "to_parsed", "sent_at"]) | |
else: | |
logger.info("SMS: from %s to %s: %s", from_number, to_number, body) | |
return message |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from django.db import models | |
# Used in the Test | |
class OutgoingSMS(models.Model): | |
sms_sid = models.CharField(max_length=34, default="", blank=True) | |
account_sid = models.CharField(max_length=34, default="", blank=True) | |
from_number = models.CharField(max_length=30) | |
to_number = models.CharField(max_length=30) | |
to_parsed = models.CharField(max_length=30, default="", blank=True) | |
body = models.TextField(max_length=160, default="", blank=True) | |
sent_at = models.DateTimeField(null=True, blank=True) | |
delivered_at = models.DateTimeField(null=True, blank=True) | |
status = models.CharField(max_length=20, default="", blank=True) | |
created_at = models.DateTimeField(auto_now_add=True) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from django.test import TestCase, Client | |
from django.conf import settings | |
from datetime import datetime | |
from pytz import UTC | |
class SmsSendingTest(TestCase): | |
def test_send_sms(self): | |
result = utils.send_sms( | |
request=None, | |
to_number=settings.TWILIO_VERIFIED_NUMBER, | |
body='Test Message from tox' | |
) | |
self.assertTrue(isinstance(result, OutgoingSMS)) | |
self.assertEqual(result.status, 'queued') | |
self.assertTrue(isinstance(result.sent_at, datetime)) | |
self.assertEqual(result.sent_at.tzinfo, UTC) | |
self.assertEqual(result.created_at.tzinfo, UTC) | |
self.assertIsNone(result.delivered_at) | |
# make fake response | |
client = Client( | |
HTTP_USER_AGENT='Mozilla/5.0', | |
HTTP_X_TWILIO_SIGNATURE='emin' | |
) | |
response = client.post('/messaging/callback/sent/{pk}/'.format(pk=result.pk), { | |
'MessageStatus': 'sent', | |
'ApiVersion': '', | |
'SmsSid': '', | |
'SmsStatus': 'sent', | |
'To': settings.TWILIO_VERIFIED_NUMBER, | |
'From': settings.TWILIO_PHONE_NUMBER, | |
'MessageSid': '', | |
'AccountSid': settings.TWILIO_ACCOUNT_SID | |
}) | |
self.assertEqual(response.status_code, 200) | |
self.assertEqual(response._headers['content-type'][1], 'application/xml') | |
# check if sms details updated | |
sms = OutgoingSMS.objects.get(pk=result.pk) | |
self.assertTrue(isinstance(sms.delivered_at, datetime)) | |
self.assertEqual(sms.status, 'sent') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment