orangeapisms

Django app to add support for Orange API SMS-MO, SMS-MT/DR


Keywords
orange, api, sms
License
Unlicense
Install
pip install orangeapisms==0.28

Documentation

orangeapisms

Django app to add support for Orange API SMS-MO and SMS-MT (with DR)

Install

  • pip install orangeapisms
  • Edit your settings.py file and add:
INSTALLED_APPS = list(INSTALLED_APPS) + ['orangeapisms', 'django_forms_bootstrap']
  • Configure your orangeapi.json file (place it next to your settings.py file):
{
    "handler_module": "myapp.orange_handler",
    "use_db": true,
    "sender_address": "+22300000000",
    "sender_name": "POTUS",
    "client_id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "client_secret": "xxxxxxxxxxxxxxxx",
    "enable_tester": true,
    "default_sender_name": "sender_address"
}
  • Setup Database with ./manage.py migrate

That's it ! Test it by accessing /oapi/ and playing with the tester.

client_id: Your Client ID (mandatory)
client_secret: Your Client Secret (mandatory)
handler_module: python path to your module handling messages (mandatory)
use_db: whether to store SMS in DB (SMSMessage Model)
smsmt_url: URL of your API (might change depending on your plan)
oauth_url: OAuth URL for Orange API
sender_address: Your subscribed phone number
sender_name: Your custom sender name (can be number or string)
enable_tester: To enable tester & logs WebUI on /oapi/
default_sender_name: What to use as default sender name
send_async: whether to deffer SMS sending to celery
celery_module: python path to your celery tasks module
country: ISO 3166-1 code for your country (used for balance checking)
fix_msisdn: whether to fix SMS-MT destination without prefix
country_prefix: MSISDN numeric prefix for your country (to fix SMS-MT without prefix)

Usage

After installation (previous step), you are able to send & receive individual SMS. To automatically process incoming SMS, you will have to customise the handler module which you specified in ORANGE_API['handler_module'].

The module would call three different functions based on events:

  • smsmo(message) on an incoming SMS-MO
  • smsmt(message) on an outgoing (sent by you) SMS-MT
  • smsdr(message) on an incoming delivery-receipt notification. The passed message is the SMS-MT which received the DR.

Sample handler module:

import datetime
import logging

from orangeapisms.utils import send_sms
from myapp.models import UserModel

logger = logging.getLogger(__name__)


def handle_smsmo(message):
    logger.info("Received an SMS-MO: {}".format(message))

    def register_user(message, keyword, text):
        # break-down the formatted SMS into variables
        try:
            name, sex, dob = text.split()
        except:
            return message.reply('Invalid format')

        # valid user entries
        if sex not in ['m', 'f']:
            return message.reply('Unable to understand sex')

        # reuse input into different data structure
        try:
            d = dob.split('-')
            birthdate = datetime.datetime(d[3], d[2], d[1])
        except:
            return message.reply('Unable to understand date of birth')

        # make use of the data including message metadata
        user = UserModel.objects.create(
            name=name, sex=sex, dob=birthdate,
            phone=message.sender_address)

        return message.reply("Congratulations, you're registered as #{}"
                             .format(user.id))

    def broadcast_to_users(message, keyword, text):
        # loop on all Users in DB
        for user in UserModel.objects.all():
            # send a custom message to that user
            send_sms(user.phone, "Hey {u}, {c}".format(u=user.name, c=text))

    keywords = {
        'register': register_user,
        'broadcast': broadcast_to_users,
    }

    # find the proper keyword
    keyword, text = message.content.split(' ', 1)
    if keyword in keywords.keys():
        return keywords.get(keyword)(message, keyword, text.strip().lower())

    # fallback on error
    return message.reply('Unknown request')


def handle_smsmt(message):
    logger.info("Sent an SMS-MT: {}".format(message))


def handle_smsdr(message):
    logger.info("Received an SMS-DR: {}".format(message))

Using a broker to send SMS-MT

By default, SMS-MT are sent synchronously meaning your request is stalled until the API call is complete.

If you need to send multiple SMS-MT while not blocking the request thread, you will want to defer sending to a broker.

This library integrates easily with celery so you can do just that in a breeze.

To use Asynchronous SMS-MT sending, you will need to :

  • Install and configure celery onto your project (see instructions bellow if needed)
  • Edit your settings.py to include the following options
# wether to send asynchronously or not
'send_async': True,
# python path of your celery module containing the task
'celery_module': 'myproject.celery'
  • Add a custom task to your celery module
@app.task()
def submit_sms_mt_request_task(payload, message):
    from orangeapisms.utils import do_submit_sms_mt_request
    return do_submit_sms_mt_request(payload, message)

That's it. Now every SMS-MT will be deferred to celery and processed by your broker.

Launch a celery worker to test it!

Basic celery configuration

If you are not familiar with celery and want to quickly test the async feature, follow this steps:

  • Install redis on your computer and start it
sudo apt-get install redis
service redis start
  • Install celery and redis with `pip install celery redis`
  • Add the celery configuration to your settings.py:
BROKER_URL = 'redis://localhost:6379/0'
CELERY_RESULT_BACKEND = 'redis://localhost:6379/0'
  • Create a module in your project for celery:
import os

from celery import Celery

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings')
app = Celery('project')
app.config_from_object('django.conf:settings')


@app.task()
def submit_sms_mt_request_task(payload, message):
    from orangeapisms.utils import do_submit_sms_mt_request
    return do_submit_sms_mt_request(payload, message)
  • Launch a worker
celery -A project worker -l info