Send SMS through Service Task (http-connector) and Twilio (Script Based)

Sharing another variation/boilerplate of using the Service Task to quickly send SMS messages through Twilio.
This is a variation of the work done in: Send Email through Service Task and MailGun (Script Based). Consider this a starting point/basics worked out and you modify/extend for your purposes.

Requirements:

You will need a Twilio account and a Twilio phone number that supports SMS.

You need your Account SID and Account Auth Token which you can get in the top left of the Twilio Console homepage: Twilio | Login

Process:

The Send SMS service task uses http-connector as follows:

url: https://api.twilio.com/2010-04-01/YOUR_ACCOUNT_SID_GOES_HERE/Messages.json

Take note of the .json at the end of the URL. Twilio supports a XML or JSON reponse. If you omit the .json twilio will return a XML response (which Camunda SPIN will still parse correctly). More info on the Twilio Responses can be found here

method: POST

headers:

  1. content-type: application/x-www-form-urlencoded
  2. Authorization: ${basicAuth64}

payload:

var data = {
    "To": execution.getVariable("toSMS"),
    "From": execution.getVariable("fromSMS"),
    "Body": execution.getVariable("bodySMS"),
    };

// Converts the JSON into a Twilio friendly params string
smsContent = Object.keys(data).map(function(k) {
    return encodeURIComponent(k) + '=' + encodeURIComponent(data[k])
}).join('&')

smsContent;

In the Input/Output we setup the Base64 for the BasicAuth that the Twilio requires:

Script:

// Base64 encoder object; used because Nashorn does not provide window.btoa() 

var Base64={_keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",encode:function(e){var t="";var n,r,i,s,o,u,a;var f=0;e=Base64._utf8_encode(e);while(f<e.length){n=e.charCodeAt(f++);r=e.charCodeAt(f++);i=e.charCodeAt(f++);s=n>>2;o=(n&3)<<4|r>>4;u=(r&15)<<2|i>>6;a=i&63;if(isNaN(r)){u=a=64}else if(isNaN(i)){a=64}t=t+this._keyStr.charAt(s)+this._keyStr.charAt(o)+this._keyStr.charAt(u)+this._keyStr.charAt(a)}return t},decode:function(e){var t="";var n,r,i;var s,o,u,a;var f=0;e=e.replace(/[^A-Za-z0-9+/=]/g,"");while(f<e.length){s=this._keyStr.indexOf(e.charAt(f++));o=this._keyStr.indexOf(e.charAt(f++));u=this._keyStr.indexOf(e.charAt(f++));a=this._keyStr.indexOf(e.charAt(f++));n=s<<2|o>>4;r=(o&15)<<4|u>>2;i=(u&3)<<6|a;t=t+String.fromCharCode(n);if(u!=64){t=t+String.fromCharCode(r)}if(a!=64){t=t+String.fromCharCode(i)}}t=Base64._utf8_decode(t);return t},_utf8_encode:function(e){e=e.replace(/rn/g,"n");var t="";for(var n=0;n<e.length;n++){var r=e.charCodeAt(n);if(r<128){t+=String.fromCharCode(r)}else if(r>127&&r<2048){t+=String.fromCharCode(r>>6|192);t+=String.fromCharCode(r&63|128)}else{t+=String.fromCharCode(r>>12|224);t+=String.fromCharCode(r>>6&63|128);t+=String.fromCharCode(r&63|128)}}return t},_utf8_decode:function(e){var t="";var n=0;var r=c1=c2=0;while(n<e.length){r=e.charCodeAt(n);if(r<128){t+=String.fromCharCode(r);n++}else if(r>191&&r<224){c2=e.charCodeAt(n+1);t+=String.fromCharCode((r&31)<<6|c2&63);n+=2}else{c2=e.charCodeAt(n+1);c3=e.charCodeAt(n+2);t+=String.fromCharCode((r&15)<<12|(c2&63)<<6|c3&63);n+=3}}return t}};

// Twilio Account SID and AuthToken which are used as the Username and Password for BasicAuth.
var accountSID = "YOUR_ACCOUNT_SID_GOES_HERE";
var accountAuthToken = "YOUR_ACCOUNT_AUTH_TOKEN_GOES_HERE";

var encoded = Base64.encode(accountSID + ':' + accountAuthToken);
"Basic " + encoded;

In this BPMN example we use a generated task form on the Start Event to generate the To Number, From Number, and SMS Body.

Make sure to use your Twilio phone number in the From SMS Number Field.

BPMN File:
twilioSMS.bpmn (7.6 KB)

Enjoy.

5 Likes

Awesome, thanks for sharing.

Hi @StephenOTT,

thanks for sharing, would you be interested to prepare a blog post about that?

Cheers,
Askar

Now all thats left is to create a modeller template! :wink:

1 Like

With the work i did with Jsoup as part of

I rewrote and simplified the Twilio code:

So now:

//Requires Jsoup in classpath: https://jsoup.org

function sendSms(to, from, body, accountSid, authToken)
{
  function buildBase64AuthString(accountSid, authToken)
  {
    var mergedAuthString = accountSid + ':' + authToken
    var base64 = Java.type('javax.xml.bind.DatatypeConverter').printBase64Binary(mergedAuthString.getBytes("UTF-8"))
    return base64.toString()
  }

  with (new JavaImporter(org.jsoup))
  {
    var doc = Jsoup.connect('https://api.twilio.com/2010-04-01/Accounts/' + accountSid + '/Messages.json')
                   .method(Java.type('org.jsoup.Connection.Method').POST)
                   .header('Content-Type', 'application/x-www-form-urlencoded')
                   .header('Authorization', 'Basic ' + buildBase64AuthString(accountSid, authToken))
                   .data("To", to,
                         "From", from,
                         "Body", body
                        )
                   .timeout(30000)
                   .ignoreContentType(true)
                   .execute()

    var resBody = doc.body()
  }
  return resBody
}

function spinify(body)
{
  var parsed = JSON.parse(body)
  var stringified = JSON.stringify(parsed)
  var spin = S(stringified)
  return spin
}

var twilioSms = sendSms('+1 123-123-1234',
                        '+1 555-555-5555',
                        'This is a test message',
                        'myAccountSid',
                        'myAuthToken'
                        )

execution.setVariable('responseBody', spinify(twilioSms))



Update for storing your secrets using Docker Secrets: Ref: Using secrets/passwords securely in processes (Using Docker Secrets, not storing passwords in process variables)

Use Case: Rather than storing the Account SID and Auth Token in code/in the process, you can store it in a in-memory encrypted Docker Secrets location.

Take the following code snippet from Send SMS through Service Task (http-connector) and Twilio (Script Based)

//Requires Jsoup in classpath: https://jsoup.org

function sendSms(to, from, body, accountSid, authToken)
{
  function buildBase64AuthString(accountSid, authToken)
  {
    var mergedAuthString = accountSid + ':' + authToken
    var base64 = Java.type('javax.xml.bind.DatatypeConverter').printBase64Binary(mergedAuthString.getBytes("UTF-8"))
    return base64.toString()
  }

  with (new JavaImporter(org.jsoup))
  {
    var doc = Jsoup.connect('https://api.twilio.com/2010-04-01/Accounts/' + accountSid + '/Messages.json')
                   .method(Java.type('org.jsoup.Connection.Method').POST)
                   .header('Content-Type', 'application/x-www-form-urlencoded')
                   .header('Authorization', 'Basic ' + buildBase64AuthString(accountSid, authToken))
                   .data("To", to,
                         "From", from,
                         "Body", body
                        )
                   .timeout(30000)
                   .ignoreContentType(true)
                   .execute()

    var resBody = doc.body()
  }
  return resBody
}

function spinify(body)
{
  var parsed = JSON.parse(body)
  var stringified = JSON.stringify(parsed)
  var spin = S(stringified)
  return spin
}

var twilioSms = sendSms('+1 123-123-1234',
                        '+1 555-555-5555',
                        'This is a test message',
                        'myAccountSid',
                        'myAuthToken'
                        )

execution.setVariable('responseBody', spinify(twilioSms))

and modify it to:

/**
 * Load Secrets Json File and returns a specific secret
 *
 * @param string secretKey The json property key that the secret is stored. Currently only single level json is supported.
 * @param string secretEnvName The Env Variable name that the JSON is stored in using Docker Secrets
 * @return string The secret. Assumes that all secrets are strings and therefore returns a string.
 */
function getSecret(secretKey, secretEnvName)
{
  with (new JavaImporter(java.lang.System, java.nio.file))
  {
    var secretPath = System.getenv(secretEnvName)
    var secretFileBytes = Files.readAllBytes(Paths.get(secretPath))
  }

  var String = Java.type('java.lang.String')
  var secretFileString = new String(secretFileBytes)

  var secretJson = JSON.parse(secretFileString)
  var secret = secretJson[secretKey]

  return secret.toString()
}

//Requires Jsoup in classpath: https://jsoup.org

function sendSms(to, from, body, accountSid, authToken)
{
  function buildBase64AuthString(accountSid, authToken)
  {
    var mergedAuthString = accountSid + ':' + authToken
    var base64 = Java.type('javax.xml.bind.DatatypeConverter').printBase64Binary(mergedAuthString.getBytes("UTF-8"))
    return base64.toString()
  }

  with (new JavaImporter(org.jsoup))
  {
    var doc = Jsoup.connect('https://api.twilio.com/2010-04-01/Accounts/' + accountSid + '/Messages.json')
                   .method(Java.type('org.jsoup.Connection.Method').POST)
                   .header('Content-Type', 'application/x-www-form-urlencoded')
                   .header('Authorization', 'Basic ' + buildBase64AuthString(accountSid, authToken))
                   .data("To", to,
                         "From", from,
                         "Body", body
                        )
                   .timeout(30000)
                   .ignoreContentType(true)
                   .execute()

    var resBody = doc.body()
  }
  return resBody
}

function spinify(body)
{
  var parsed = JSON.parse(body)
  var stringified = JSON.stringify(parsed)
  var spin = S(stringified)
  return spin
}

var twilioSms = sendSms('+16137961155',
                        '+1 514-600-6160',
                        'This is a test message',
                        getSecret('twilioAccountSid', 'MY_SECRETS_FILE'),
                        getSecret('twilioAuthToken', 'MY_SECRETS_FILE')
                        )

execution.setVariable('responseBody', spinify(twilioSms))

See the Using secrets/passwords securely in processes (Using Docker Secrets, not storing passwords in process variables) for further details on how to setup your Camunda Docker container with a Docker Secrets configuration (included are docker examples ready to use)