Quantcast
Channel: Python – Twilio Cloud Communications Blog
Viewing all 78 articles
Browse latest View live

Warm Phone Call Transfers with Python, Flask and Twilio Voice

$
0
0

Have you ever had your call disconnect while you were on hold with customer service and waiting for a transfer from one department to another? You probably felt like this when the call dropped:

A warm phone call transfer, where a caller is on the phone with an agent then the agent brings on another agent and introduces them to the customer, shouldn’t be so difficult. There are only a few steps that need to be done properly for a warm phone call transfer to work, as we can visualize in the following diagram.

As the diagram shows, a customer, who in this post we’ll refer to as Trinity, dials into a typical phone number for service. An agent, known as Agent Smith, answers the calls. However, Agent Smith discovers he can’t solve the problem and needs to connect Trinity to Agent Johnson, who’s better equipped to handle the issue. Agent Smith dials in Agent Johnson, introduces Agent Johnson to the customer, then hangs up so Trinity and Agent Johnson can solve the problem and finish the support call.
 
In this blog post, we’ll use Twilio’s Voice API and a bit of Python code to implement the above scenario and help rid the world of warm transfer failures so that this problem never happens to your callers.

What We’ll Need

Let’s walk through how to perform a warm transfer with Python and a simple web application built with the Flask framework. The following dependencies will be used to create and run the application:

We’ll also need three phones with phone numbers to represent Trinity, Agent Smith and Agent Johnson. If you don’t have multiple phones lying around for testing, you can use Twilio Client to turn three browser tabs into different phones. Use this Twilio Client quickstart project with the deploy to Heroku button to create phones as you need them.

There is also a GitHub repository with the code we’ll be creating in this post. It’s a handy reference in case you make a typo or get stuck somewhere along the way.

Also, if Python isn’t your jam and Java is more your speed, check out my colleague’s post that handles a warm transfer in that language instead.

Now that we know our dependencies, let’s get our coding environment established.

Python Environment Setup

It’s time to get our Python development environment ready for this application. In this section we’ll handle the following steps to make sure you’re ready to run the code:

  1. Check the Python installation
  2. Set up a new virtualenv then activate it
  3. Install a couple of dependencies via pip
  4. Fire up Ngrok to handle localhost tunneling
  5. Obtain a phone number for our customer service line
  6. Export a few critical environment variables for our application to use

Setup Step 1: Check the Python installation

If you’re on Mac OS X or Linux, you’ve already got Python installed on your system. Check the version by going into the terminal and typing

python --version
. You’ll see the precise version number, something like
Python 2.7.6
. If you’re on Python 2.6 or greater, you’re good to go.

For Windows, make sure you download and install the Python .exe installation package. Either Python 2 or 3 is fine. My recommendation is to use Python 3.5 unless you have a pressing reason to use an earlier version, because the Python community is now migrating en masse to the Python 3.

Once Python is installed on your system and you can run it with the

python
command, we’re ready to set up a virtualenv.
 

Setup Step 2: Set up a new virtualenv and activate it

Virtualenv is a dependency isolation library that makes it much easier to switch between Python versions and various code packages you’re using on different projects. Create a virtualenv in a location you’ll remember using the following command. In my case, I have a folder called

Envs
under my home directory that keeps my virtualenv organized.

virtualenv ~/Envs/warmtransfer
source ~/Envs/warmtransfer/bin/activate

Now we have our virtualenv activated and should see our command prompt show the name of our virutalenv, such as

(warmtransfer)$
. With the virtualenv in place and activated, we can use
pip
to install our dependencies.

Setup Step 3: Install dependencies with pip

pip
handles Python library installations. With your virtualenv still activated, run the following command to install our Flask and Twilio helper library dependencies.

(warmtransfer)$ pip install flask==0.10.1 twilio==4.4.0

You can likely install the newest versions of the Flask and Twilio packages but it’s preferable to peg dependencies to a specific version number just in case there are future backwards-incompatible modifications. With those libraries installed, next we need to launch Ngrok to create a localhost tunnel so that our application will be reachable by Twilio.

Setup Step 4: Fire up Ngrok

Download and install the Ngrok application. From the command line, run Ngrok to create a localhost tunnel to the port our application will run on using the following command:

./ngrok http 5000

We should see a screen like the following that shows Ngrok started properly. Take note of the second line that begins with “Forwarding”. That’s the URL we need to set for the BASE_URL environment variable as well as our Twilio webhook.

ngrok.png

With Ngrok up and running, we can turn our attention to setting up a phone number for Trinity  to call into for customer service.

Setup Step 5: Obtain a number for our customer service line

Where do we get the phone number to use as a customer service line for our warm phone call transfer example? That phone number comes from our Twilio account.

If you don’t already have a Twilio account, you can sign up for a free trial account. After signing up, upgrade to a paid account as we’ll need its ability to call multiple telephone numbers other than our own verified number.

Obtain a Twilio phone number with calling capabilities that we can configure for our warm call transfer example. Go to the Twilio numbers page which should look like the following screenshot:

numbers-screen.png

Click on the “Buy a Number” button and purchase a phone number with calling capabilities in the location of your choice. After obtaining the phone number, you’ll get to set up voice calling on the numbers configuration page:

webhook.png

As shown above, within the phone number’s screen there is a voice webhook we need to configure. WIthin the Request URL text box, type in the Ngrok Forwarding URL, such as “https://5341e8cd.ngrok.io” plus “/call” because that is the route we are going to write in the next section to respond to the webhook with appropriate TwiML. Make sure to scroll down and click the “Save” button at the bottom of the screen so our changes take effect.

With our Python development environment established, Ngrok started and a Twilio phone number in hand, we just need to use that information to set environment variables our application will need.

Setup Step 6: Set environment variables

How to set environment variables depends on your operating system. Here are some handy guides if you’re on Windows, Mac OS X or Ubuntu Linux that’ll help with the specific steps.

Note that if you don’t set these environment variables now there are spots within the application code to set them. However, in Python it’s a good practice to use environment variables instead of hardcoding these sensitive values.

There are six variables that need to be set:

  • TWILIO_ACCOUNT_SID
     – found on your Twilio account dashboard
  • TWILIO_AUTH_TOKEN
     – also found on your Twilio account dashboard
  • CUSTOMER_SERVICE_NUMBER
     – a number you’ve purchased on Twilio
  • AGENT1_NUMBER
     – the phone number of the agent who will first answer the call. For testing purposes this is most likely your own phone number, specified in the +12025551234 format
  • AGENT2_NUMBER
     – the phone number of the agent who will be transferred in, in the +12025551234 format
  • BASE_URL
     – the forwarding URL given by Ngrok when it starts up, for example “https://5341e8cd.ngrok.io”

On Mac OS X and Linux, you can set environment variables with the

export
shell command. For example, you’d run
export
six times to set the following variables:

export TWILIO_ACCOUNT_SID="ACxxxxxxxxxxxxxxxxxxxxxx"
export TWILIO_AUTH_TOKEN="yyyyyyyyyyyyyyyyyyyyyyyyyyy"
export CUSTOMER_SERVICE_NUMBER="+12025551234" # this will be a Twilio number
export AGENT1_NUMBER="+19735551234" # your phone number
export AGENT2_NUMBER="+14155551234" # a second phone number for testing
export BASE_URL="https://5341e8cd.ngrok.io"

Now that our environment is ready to go, let’s write some code to handle inbound customer service phone calls.

Calling Agent Smith

Our environment is established and we have a Twilio phone number, so it’s time to write some Python code to handle an incoming phone call.

write-code.gif

We’ll start out by importing our Flask and Twilio dependencies that we installed earlier. We also need a bit of boilerplate to initialize the Flask application and the Twilio helper library. This code will run the application in Flask’s debug mode in case we run into any issues while building our application.

import os
from flask import Flask, Response, request
from twilio import twiml
from twilio.rest import TwilioRestClient

app = Flask(__name__)
client = TwilioRestClient()

if __name__ == '__main__':
    app.run(debug=True)

Next our application needs to obtain the environment variables we exported to our shell. The

os
module is used to get the environment variables for customer service number, the agent phone numbers and the base URL that Ngrok gives us when we fire it up.

import os
from flask import Flask, Response, request
from twilio import twiml
from twilio.rest import TwilioRestClient


app = Flask(__name__)
client = TwilioRestClient()

# this should be your Twilio number, format: +14155551234
CUSTOMER_SERVICE_NUMBER = os.environ.get('CUSTOMER_SERVICE_NUMBER', '')
# your cell phone number (agent's number), format: +14155551234
AGENT1_NUMBER = os.environ.get('AGENT1_NUMBER', '')
# second person's phone or Twilio number if testing, format: +14155551234
AGENT2_NUMBER = os.environ.get('AGENT2_NUMBER', '')
# ngrok URL, such as "https://17224f9e.ngrok.io", no trailing slash
BASE_URL = os.environ.get('BASE_URL', 'https://143e6ab2.ngrok.io')

if __name__ == '__main__':
    app.run(debug=True)

We could run our Flask web application at this point but without any routes it doesn’t have any pages to load. Create a couple routes now, named

inbound_call
and
conference_line
, as shown below.

@app.route('/call', methods=['POST'])
def inbound_call():
    call_sid = request.form['CallSid']
    response = twiml.Response()
    response.dial().conference(call_sid)
    call = client.calls.create(to=AGENT1_NUMBER,
                               from_=CUSTOMER_SERVICE_NUMBER,
                               url=BASE_URL + '/conference/' + call_sid)
    return Response(str(response), 200, mimetype="application/xml")


@app.route('/conference/', methods=['GET', 'POST'])
def conference_line(conference_name):
    response = twiml.Response()
    response.dial(hangupOnStar=True).conference(conference_name)
    return Response(str(response), 200, mimetype="application/xml")

The above code in

inbound_call
handles an inbound call by generating and returning a TwiML response when Twilio performs an HTTP POST request to our application. We also use our instantiated
TwilioRestClient
to dial an outbound phone call to Agent Smith, our first agent who will take the customer’s call.

The second function,

conference_line
, creates a TwiML response that looks like the following XML. We don’t need to hand code the TwiML because the Python helper library generates this when we call the
Response
,
dial
and
conference
functions.



  
    callSid
  
  

Our application is now a simple conference call application. Let’s give the initial version of our warm call transfer application a spin to make sure everything works so far. Run the Flask app from the command line with the following command.

python app.py

With the application running and exposed to Twilio using Ngrok, we can test the application by calling the customer service Twilio number.

Now we’re connected to our initial customer service agent. However, let’s assume the agent can’t answer the question and needs to connect the caller to another person who knows what they’re talking about.

Bringing in Agent Johnson

We need to slightly modify our application to allow for the warm transfer. The

gather
function in our response will generate a TwiML response for us that instructs Twilio how to handle the phone call.

@app.route('/conference/', methods=['GET', 'POST'])
def conference_line(conference_name):
    response = twiml.Response()
    response.dial(hangupOnStar=True).conference(conference_name)
    response.gather(action=BASE_URL + '/add-agent/' + conference_name,
                    numDigits=1)
    return Response(str(response), 200, mimetype="application/xml")

@app.route('/add-agent/', methods=['POST'])
def add_second_agent(conference_name):
    client.calls.create(to=AGENT2_NUMBER, from_=CUSTOMER_SERVICE_NUMBER,
                        url=BASE_URL + '/conference/' + conference_name)
    response = twiml.Response()
    response.dial(hangupOnStar=True).conference(conference_name)
    return Response(str(response), 200, mimetype="application/xml")

In the modified

inbound_call
route we create TwiML for a conference line and then dial our first agent into the conference call. A conference is actually the easiest way to handle adding and removing people from a call. Our customer calling in doesn’t even need to know it’s a conference call, it’s seamless to her as Agent Smith and Agent Johnson add and remove themselves from the line.

The

conference_line
route creates the TwiML to put Agent Smith in the conference call. The
hangupOnStar=True
parameter allows Agent Smith to put the call on hold by pressing the
*
key on the dial pad. Then Agent Smith gets back into the call by pressing any number key on the dial pad, which causes the
gather
TwiML to add the agent back to the conference with our customer.

We’re ready to test out our application one more time to see it all put together. Here is a review of the entire

app.py
file we wrote together throughout this post.

import os
from flask import Flask, Response, request
from twilio import twiml
from twilio.rest import TwilioRestClient


app = Flask(__name__)
client = TwilioRestClient()

# this should be your Twilio number, format: +14155551234
CUSTOMER_SERVICE_NUMBER = os.environ.get('CUSTOMER_SERVICE_NUMBER', '')
# your cell phone number (agent's number), format: +14155551234
AGENT1_NUMBER = os.environ.get('AGENT1_NUMBER', '')
# second person's phone or Twilio number if testing, format: +14155551234
AGENT2_NUMBER = os.environ.get('AGENT2_NUMBER', '')
# ngrok URL, such as "https://17224f9e.ngrok.io", no trailing slash
BASE_URL = os.environ.get('BASE_URL', 'https://143e6ab2.ngrok.io')


@app.route('/call', methods=['POST'])
def inbound_call():
    call_sid = request.form['CallSid']
    response = twiml.Response()
    response.dial().conference(call_sid)
    call = client.calls.create(to=AGENT1_NUMBER,
                               from_=CUSTOMER_SERVICE_NUMBER,
                               url=BASE_URL + '/conference/' + call_sid)
    return Response(str(response), 200, mimetype="application/xml")


@app.route('/conference/', methods=['GET', 'POST'])
def conference_line(conference_name):
    response = twiml.Response()
    response.dial(hangupOnStar=True).conference(conference_name)
    response.gather(action=BASE_URL + '/add-agent/' + conference_name,
                    numDigits=1)
    return Response(str(response), 200, mimetype="application/xml")


@app.route('/add-agent/', methods=['POST'])
def add_second_agent(conference_name):
    client.calls.create(to=AGENT2_NUMBER, from_=CUSTOMER_SERVICE_NUMBER,
                        url=BASE_URL + '/conference/' + conference_name)
    response = twiml.Response()
    response.dial(hangupOnStar=True).conference(conference_name)
    return Response(str(response), 200, mimetype="application/xml")


if __name__ == '__main__':
    app.run(debug=True)

With the above code written and running, call into our customer service phone number and the app will use the Twilio Voice API to call Agent Smith. When Agent Smith picks up, press

*
plus a number on the keypad to “hang up” which hits the
/add-agent
route and calls out to Agent Johnson.

Agent Smith will be placed back into the call. Agent Smith then transfers to Agent Johnson, they’re both on the line, then Agent Smith drops off while Agent Johnson continues on with Trinity.

Warm Transfer Complete

Now that’s how a warm call transfer should be done!

In this post, we solved the problem of poorly handled call transfers. Now when your customers call in, Agent Smith will be ready to hand off to Agent Johnson when the need arises.

matrix-3.gif

If you’re looking for more tutorials and Python code to do additional voice communications to your application, check out one of my favorite tutorials named Click to Call that uses Python and Flask to allow visitors on the web to easily call your support or sales lines on the phone.

That’s all for now! If you run into any issues throughout the tutorial feel free to drop a comment below or contact me via:

  • Email: makai@twilio.com
  • GitHub: Follow makaimc for repository updates
  • Twitter: @mattmakai

Warm Phone Call Transfers with Python, Flask and Twilio Voice


Getting Started with OpenCV and Python: Featuring The Martian

$
0
0

If you’re curious to find out how to launch yourself into outer space and land on Mars, you’ve come to the right place. With the help of an open source image processing library called OpenCV, along with Twilio MMS, Python, and Flask we are limited only by our imagination.

And if you haven’t seen The Martian yet, seriously, what are you waiting for? The movie is really good, filled with scienceMatt Damon and potatoes.

When Twilio decided to host a private screening of The Martian as a way for the community to come together, it seemed like the perfect opportunity to make something fun for people to play with as they filed into the theater.

Instead of giving you a hipster mustache, I opted to remix Rob Spectre’s Mustachify for The Martian, Martianify! Because what’s a better way to kill time than to take selfies? You know you were going to do it anyway. So why not send one to this Twilio number and see what happens? Go on, give it a try.

United States: (347) 537-3850

 

 

GETTING STARTED

To kick things off, let’s take a look at our dependencies.

  • A Twilio account. Sign up for your free account if you don’t have one already.
  • A Twilio phone number with MMS capability. These are only available in the United States and Canada.
  • A publicly accessible URL to configure Twilio webhook (try ngrok)
  • Twilio Python helper library to create our TwiML
  • Python 2.x (Python 3.x is compatible with OpenCV; however the setup instructions are different)
  • Pip, the friendly Python Package Installer
  • OpenCV 2.4.12 (Don’t worry, we’ll walk through the setup below)
  • Flask, the Python web microframework
  • The Martian Helmet Image

With that, let’s get started. If you are using a Twilio trial account for this example, you will only be able to send messages to phone numbers that you have verified with Twilio. Phone numbers can be verified via your Twilio Account’s Phone Numbers Page.

THE GREAT INSTALL

We’ll kick things off by getting started with OpenCV on Mac. If you’re on Windows, try following this tutorial. Unfortunately, OpenCV is not available via pip. That means we have to install it manually. For this project I am using OpenCV 2.4.12.  If OpenCV is already installed on your computer, lucky you! You can check to see if you have it installed or double check your version in a python shell like this:

>>> import cv2
>>> cv2.__version__
'2.4.12'

If you were unable to import cv2, don’t worry – I’ll walk you through the steps you need to take using brew on OS X to get OpenCV set up. You’ll also want to follow these instructions if your version is below OpenCV 2.0. If you’re running OpenCV 3.0.0 this code should be compatible.

brew tap homebrew/science
brew install opencv

That brew install could take a while. But when it’s finished, find your OpenCV at the following path ending in your installed version number. Be mindful of your version and running into permissions problems on homebrew folders.

cd /usr/local/Cellar/opencv/2.4.12/

You’ll also need to configure OpenCV with Python. If you don’t know where your Python is installed, you can find it in .bash_profile:

cat ~/.bash_profile | grep PYTHONPATH

Inside of the Python path we will link our compiled OpenCV files with a symlink.

ln -s /usr/local/Cellar/opencv/2.4.12/lib/python2.7/site-packages/cv.py cv.py
ln -s /usr/local/Cellar/opencv/2.4.12/lib/python2.7/site-packages/cv2.so cv2.so

Now retry your import in that Python shell.

>>>import cv2
>>>cv2.__version__
'2.4.12'

3, 2, 1… BLAST OFF!

Just a little primer on OpenCV. It’s an open source library, which is fantastic. Along with Python, there are also  C , C++, and Java bindings. There is a pretty large community surrounding the library and it has many applications. We’re going to use something called Haar Feature-based Cascade Classifiers to detect things like faces, eyes, noses, and so on. Using a machine learning approach, the classifiers detect features based upon the data it has been pre-trained with. OpenCV contains many of these pre-trained classifiers stored as XML files in /usr/local/Cellar/opencv/2.4.12/share/OpenCV/haarcascades/. You can also train classifiers for any object like a car or an airplane using OpenCV to create one. We’re going to use the haarcascade_frontalface_default.xml file.

import cv2

face_cascade = cv2.CascadeClassifier('/path/to/haarcascade_frontalface_default.xml')
original_image_path = '/path/to/your/selfie'

image = cv2.imread(original_image_path)

faces = face_cascade.detectMultiScale(image,
                                      scaleFactor=1.1,
                                      minNeighbors=3,
                                      minSize=(30, 30),
                                      flags=cv2.cv.CV_HAAR_SCALE_IMAGE)

for (x, y, w, h) in faces:
    cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 2)

cv2.imshow('Picture', image)
cv2.waitKey()
cv2.destroyAllWindows()

This is a good chunk of code, let’s break it down line by line.

Line 1: Import the OpenCV library.

Line 3: Load the classifier from file

Lines 4-6: Create an object of our selfie by providing the full path (be sure to change this to your own) and load the image into cv2.

Lines 8-11: Use the cv2.CascadeClassifier.detectMultiScale function on an image to find objects of different sizes in the input image. The detected objects are returned as a list of rectangles. To learn more about what each parameter does check out the docs on Cascade Classification.

Lines 12-13: Iterate through the corners of the faces detected and draw a rectangle in that pattern.

Lines 15-17: imShow() will display the image on screen and we’ll see how we did. waitKey() allows you to press any key to execute closing the image on destroyAllWindows().

Facial detection is more art than science and will vary depending on the image provided. Play around with the parameters of cv2.detectMultiScale to find what works for you. Once you feel comfortable with this step, go on.

OH, LOOK AT ALL THE LOVELY FACES

Great! We’re seeing faces. Now, let’s continue on our millions of miles journey to Mars.

We’ll reuse some of the code from above and add what we need to overlay The Martian helmet image on just the face. Make a file called martianify.py. This is what’s happening:

  • Load Martian and Selfie images
  • Create masks for both
  • Sizing and padding of selfie image
  • Facial detection
  • Merge the two images

In this first block, we’ll load the foreground image with cv2.imread.

Then, we’ll create masks which are used to identify the pixels that should be used when applied to an image. Read more about how we use cv2.bitwise_not here.

In the final prep of the foreground, we’ll declare size and ratio for The Martian that we will use to fit our selfie into the hole.

import cv2


def martianify(original_image_path):

    # Load the face detection cascade file.
    face_cascade = cv2.CascadeClassifier('/path/to/haarcascade_frontalface_default.xml')

    # Load our Martian as foreground image with alpha transparency.
    # The -1 reads the alpha transparency of our image otherwise
    # known as the face hole.
    foreground_image = cv2.imread('/path/to/martian/image/.png', -1)

    # Create foreground mask from alpha transparency.
    foreground_mask = foreground_image[:, :, 3]

    # Create inverted background mask.
    background_mask = cv2.bitwise_not(foreground_mask)

    # Convert foreground image to BGR.
    foreground_image = foreground_image[:, :, 0:3]

    # Declare foreground size.
    foreground_size = 630
    foreground_ratio = float(foreground_size)

On the command line, python martianify.py should run without errors but will not display any results.

Next, we’ll configure our background image. Declare the size of background image to be 1100 pixels. Then we’ll calculate the padding for the background or selfie image.

# Declare background size and padding.
    background_size = 1100

    padding_top = ((background_size - foreground_size) / 3) * 2
    padding_bottom = background_size - padding_top
    padding_left = (background_size - foreground_size) / 2
    padding_right = (background_size - foreground_size) / 2

Now, we’re going to reuse most of the code from earlier that loads the selfie image into OpenCV and finds faces in the image. This time when we iterate over the faces we declare the region of interest on the face on line 54.

# Capture selfie image in OpenCV.
    cv_image = cv2.imread(original_image_path)

    # Find that face.
    faces = face_cascade.detectMultiScale(
        cv_image,
        scaleFactor=1.1,
        minNeighbors=3,
        minSize=(30, 30),
        flags=cv2.cv.CV_HAAR_SCALE_IMAGE
        )

    # Iterate over each face found - roi: region of interest
    for (x1, y1, w, h) in faces:

        # Extract image of face.
        x2 = x1 + w
        y2 = y1 + h

        face_roi = cv_image[y1:y2, x1:x2]

Add the following lines to the bottom of the file to show the face.

cv2.imshow('Face Only', face_roi)
        cv2.waitKey()
        cv2.destroyAllWindows()

martianify('/path/to/selfie/image')

Change the image path to match your own, then run python martianify.py within your project directory to see the face. Remove them when you are ready to go on. I know the anticipation is killing you, but we will bring The Martian home y’all!

Now we resize the face to make sure it is fixed using cv2.resize. We use the copyMakeBorder function to add the padding we defined earlier around the background image.Then save the background image, which now has a border, as background_src that is the size and location of where we will place The Martian.

# Resize image of face.
        ratio = foreground_ratio / face_roi.shape[1]
        dimension = (foreground_size, int(face_roi.shape[0] * ratio))
        face = cv2.resize(face_roi, dimension, interpolation=cv2.INTER_AREA)

        # Add padding to background image
        background_image = cv2.copyMakeBorder(face,
                                              padding_top,
                                              padding_bottom,
                                              padding_left,
                                              padding_right,
                                              cv2.BORDER_CONSTANT)

        # Region of interest for Martian from background proportional to martian size.
        background_src = background_image[0:background_size, 0:background_size]

You can check that the padding is added by inserting those 4 lines with cv2.imshow(‘Face Only’, background_image). And again remove it when you wish to move on.

Finally, use the cv2.bitwise_and function with inverted mask to select the pixels in the background where The Martian is not. That’s a fancy way to say the face. Do the same thing with mask and The Martian image to select the pixels from The Martian that make up The Martian.

Lastly, merge the background region of interest with the foreground region of interest using cv2.add. As the masks are an inverse of each other, the pixels selected in the bitwise_and operations will not overlap but will make one image when combined. For the curious, check roi_bg, roi_fg, and dst  respectively with cv2.imshow.

# roi_bg contains the original image only where the martian is not
        # in the region that is the size of the Martian.
        roi_bg = cv2.bitwise_and(background_src, background_src, mask=background_mask)

        # roi_fg contains the image of the Martian only where the Martian is
        roi_fg = cv2.bitwise_and(foreground_image, foreground_image, mask=foreground_mask)

        # Join the roi_bg and roi_fg.
        dst = cv2.add(roi_bg, roi_fg)

        cv2.imshow("Look mom, I'm the Martian!", dst)
        cv2.waitKey()
        cv2.destroyAllWindows()

martianify('/path/to/selfie/image')

Once that’s done, replace the last 4 lines using cv2.imwrite, cv2.imwrite(original_image_path, dst) to write the new image back to the file path, overwriting the original image. You can also grab the code in full from this gist.

THERE’S AN APP FOR THAT

With the power of Twilio MMS we can get people to text us a selfie which we will martianify. Be sure to pip install twilio and create a new file called app.py where we will use Flask to build a route to handle incoming MMS/SMS.

You’ll want to configure your Twilio number with a publicly accessible URL. We’ll use the localhost tunneling service ngrok. If you’ve never used it before, check out Phil’s ‘6 awesome reasons to use ngrok.’ Once you have ngrok set up, run it to listen to the port that we will run our Flask application on: 5000.

You should see a screen that looks like this with a link that we can visit to access our Flask app:

 

Grab that ngrok URL to configure your Twilio number. We’ll also use it in the code that follows.

 

 

Be sure to add ‘/sms’ to your request URL path in the box above.

import requests
from flask import Flask
from flask import request
from flask import send_from_directory
from twilio import twiml

from martianify import martianify

UPLOAD_FOLDER = '/path/to/your/project/'

# App declaration and configuration
app = Flask(__name__)
app.config


# SMS/MMS Request URL
@app.route('/sms', methods=['POST', 'GET'])
def sms():
    response = twiml.Response()

    response.message("Please wait for launch 3, 2, 1...")

    if request.form['NumMedia'] != '0':
        filename = request.form['MessageSid'] + '.jpg'
        f = open(filename, 'wb')
        f.write(requests.get(request.form['MediaUrl0']).content)
        f.close()
        martianify('{}/{}'.format(UPLOAD_FOLDER, filename))

        with response.message() as message:
            message.body = "{0}".format("Welcome to Mars.")
            message.media('http://YourNgrokURL/uploads/{}'.format(filename))
    else:
        response.message("Face forward and text me a selfie!")

    return str(response)


# Martian Media URL
@app.route('/uploads/<filename>', methods=['GET', 'POST'])
def uploaded_file(filename):
    return send_from_directory(UPLOAD_FOLDER,
                               filename)

if __name__ == "__main__":
    app.run()

The /sms route responds to an incoming text message with some Twilio flavored XML called TwiML. Notice that the ‘NumMedia’ parameter is not zero, meaning we received an MMS. Twilio does not return an image itself, they return a URL to the image.

We create a string called filename using the MessageSid parameter to maintain a unique identifier for each image. Then we open the file to write the content of Twilo’s image to the file. Once The Martian is made, we respond with some more TwiML, including a message body and the media or image.

The second route, /uploads/<filename> handles the delivery of the message.media TwiML. We use that URL to retrieve the newly martianified image.

Make sure that you’ve changed the file paths and the URL to reflect your own. Again, you can grab the full code from this gist. With that, you should now be able to text a selfie to your Twilio number and get back The Martian.

NEXT STEPS

We’ve only scratched the surface of what we can do with OpenCV. But there are many other things you could do with this.

  1. Set up Redis to more eloquently handle all the action this app is going to receive.
  2. Remix The Martian and create your own characters.
  3. Handle more than one face in an image.
  4. Use other cascades to detect eyes, noses.
  5. Create your own cascade to detect whatever you want!

I’m excited to see what you build with OpenCV. If you want to check out more tutorials I would recommend these. If you have any questions, want to share your build, or you’re already doing cool stuff with OpenCV feel free to leave a comment or reach out:

  • Email: mspeir@twilio.com
  • GitHub: meganspeir
  • Twitter: @meganspeir

Getting Started with OpenCV and Python: Featuring The Martian

International Space Station Notifications with Python, Redis-Queue and Twilio Copilot

$
0
0

International Space Station Notifications with Python, Redis-Queue and Twilio Copilot

With the new Star Wars trailer coming out, I’ve been really excited about space lately. This could be pretty obvious based on what I wore during my API demo at BostonHacks last weekend. Twilio also had private screenings of The Martian for community members recently in several different cities.

twilioastronaut.jpg

It’s not surprising that I think the International Space Station is awesome. Naturally I thought it would be cool to know when it is hovering over me. Once I found out about the ISS Open Notify API, I knew I had to build a notification system that allows subscribers to receive an SMS whenever the ISS flies over their area.

You can check out the app I built and look at the code if you want. Or I can show you step by step how to build this, and how you can scale it to send from a pool of Twilio numbers rather than just one using new Twilio Copilot features.

ISS Tracking Tools

We will build a web application for users to subscribe to notifications. To do this we’ll use Flask for our web server and the ISS Open Notify API to find out when the ISS is passing overhead for a given latitude and longitude.

Before you start coding, make sure you have Python installed as well as pip, which should come with most versions of Python. This will work in either Python 2.7 or Python 3 . You’ll also need to install Redis, which can be done using the following terminal commands:

wget http://download.redis.io/releases/redis-3.0.5.tar.gz
tar xzf redis-3.0.5.tar.gz
cd redis-3.0.5
make

Running the Redis server now would be helpful because we will be using it later.  Run it in the background using this command:

src/redis-server &

Next, create a virtualenv so we can cleanly install all of our python libraries with pip. If you are unfamiliar with pip and virtualenv you can check out this guide.

Let’s install Flask for our web server and the Twilio python module for sending text messages:

pip install twilio==4.8.0
pip install flask==0.10.1

In order to send text messages at the time that the ISS is scheduled to fly by, we’ll need to use a task queue such as RQ and a scheduler to add tasks to the queue at certain times. RQ uses Redis as a backend for a task queue, and RQ Scheduler allows us to schedule tasks to be added to the queue. Installing RQ Scheduler will also take care of the RQ and redis-py dependencies:

pip install rq-scheduler==0.5.1

The last dependency to install is requests which will let us send a GET request to the Open Notify API:

pip install requests==2.8.1

Finally, before we start writing the code, make sure you have at least one SMS-enabled Twilio phone number in your Twilio account. Don’t have a Twilio account yet? No problem because it only takes a couple of minutes to sign up. I’ll wait here gazing at the stars while you get one.

Scheduling with Python-RQ

With all of our dependencies in place we can get on with building the app.

The ISS Open Notify API takes a GET request with the latitude and longitude of the user’s location, and returns a list of timestamps for the next scheduled passes over that location. Let’s write a function that will return a datetime object of the next ISS flyby for a given latitude and longitude.

Open a file called iss.py and add the following code:

import requests
from datetime import datetime
import pytz


def get_next_pass(lat, lon):
    iss_url = 'http://api.open-notify.org/iss-pass.json'
    location = {'lat': lat, 'lon': lon}
    response = requests.get(iss_url, params=location).json()

    if 'response' in response:
        next_pass = response['response'][0]['risetime']
        next_pass_datetime = datetime.fromtimestamp(next_pass, tz=pytz.utc)
        print('Next pass for {}, {} is: {}'
              .format(lat, lon, next_pass_datetime))
        return next_pass_datetime
    else:
        print('No ISS flyby can be determined for {}, {}'.format(lat, lon))

Try calling this function with some test values to check that it actually works. I’m going to use the location of Twilio’s HQ for this example. Open your Python shell and try the following:

import iss
iss.get_next_pass(37.7833, -122.4167)

Next, using the datetime returned by the API, we can use rq-scheduler to queue up a function to be called to send a text message at the time of the next flyby.

Let’s write a function adding a task to our queue to send a text message to the user that has just subscribed.

Go back to iss.py and update your code to add this new function and the required imports:

import requests
from redis import Redis
from rq_scheduler import Scheduler

from datetime import datetime
import pytz

# Open a connection to your Redis server.
redis_server = Redis()

# Create a scheduler object with your Redis server.
scheduler = Scheduler(connection=redis_server)


def get_next_pass(lat, lon):
    iss_url = 'http://api.open-notify.org/iss-pass.json'
    location = {'lat': lat, 'lon': lon}
    response = requests.get(iss_url, params=location).json()

    if 'response' in response:
        next_pass = response['response'][0]['risetime']
        next_pass_datetime = datetime.fromtimestamp(next_pass, tz=pytz.utc)

        print('Next pass for {}, {} is: {}'
              .format(next_pass_datetime, lat, lon))
        return next_pass_datetime
    else:
        print('No ISS flyby can be determined for {}, {}'.format(lat, lon))


def add_to_queue(phone_number, lat, lon):
    # Add this phone number to Redis associated with "lat,lon"
    redis_server.set(phone_number, '{},{}'.format(lat, lon))

    # Get the datetime object representing the next ISS flyby for this number.
    next_pass_datetime = get_next_pass(lat, lon)

    if next_pass_datetime:
        # Schedule a text to be sent at the time of the next flyby.
        scheduler.enqueue_at(next_pass_datetime,
                             notify_subscriber, phone_number)

        print('{} will be notified when ISS passes by {}, {}'
              .format(phone_number, lat, lon))


def notify_subscriber(phone_number):
    print('Look up! You may not be able to see it, but the International'
          ' Space Station is passing above you right now!')

In the code above, we are adding a phone number to Redis for logging purposes associated with the latitude and longitude of where they were when they subscribed. Then we are grabbing the time of the next ISS flyby and scheduling a call to the notify_subscriber function. This function only prints a message for now, but we will configure it to send an SMS later.

In order for this code to work, we first have to start the RQ Schedule processes.

Hop back into your terminal and run an rqworker in the background. This process polls Redis and runs the currently queued tasks:

rqworker &

Now run the rqscheduler. This process polls Redis once every minute and moves scheduled jobs to the relevant queues when they need to be executed:

rqscheduler &

Our current code prints to the console whenever the ISS flies by a given latitude and longitude. Let’s have more fun by configuring our notify_subscriber function to send text messages.

Sending a text message with Twilio in Python

Now we’ll use the TwilioRestClient to send an SMS using the Twilio REST API. Make sure you have your Twilio phone number purchased. While this app will work just fine with a trial account, upgrading will let you remove the trial account text from the messages being sent, and will allow you to buy additional phone numbers to scale the app.

NewBuyANumber.gif

Let’s finish writing our notify_subscriber function to send an actual text message. Head back over to iss.py and add the following highlighted code:

import requests
from redis import Redis
from rq_scheduler import Scheduler
from twilio.rest import TwilioRestClient

from datetime import datetime
import pytz

# Open a connection to your Redis server.
redis_server = Redis()

# Create a scheduler object with your Redis server.
scheduler = Scheduler(connection=redis_server)

client = TwilioRestClient()


def get_next_pass(lat, lon):
    iss_url = 'http://api.open-notify.org/iss-pass.json'
    location = {'lat': lat, 'lon': lon}
    response = requests.get(iss_url, params=location).json()

    if 'response' in response:
        next_pass = response['response'][0]['risetime']
        next_pass_datetime = datetime.fromtimestamp(next_pass, tz=pytz.utc)

        print('Next pass for {}, {} is: {}'
              .format(next_pass_datetime, lat, lon))
        return next_pass_datetime
    else:
        print('No ISS flyby can be determined for {}, {}'.format(lat, lon))


def add_to_queue(phone_number, lat, lon):
    # Send a text thanking the user for subscribing if they haven't before.
    if not redis_server.exists(phone_number):
        client.messages.create(to=phone_number,
                               from_='TWILIO_PHONE_NUMBER',
                               body='Thanks for subscribing to ISS alerts!')

    # Add this phone number to Redis associated with "lat,lon"
    redis_server.set(phone_number, '{},{}'.format(lat, lon))

    # Get the datetime object representing the next ISS flyby for this number.
    next_pass_datetime = get_next_pass(lat, lon)

    if next_pass_datetime:
        # Schedule a text to be sent at the time of the next flyby.
        scheduler.enqueue_at(next_pass_datetime,
                             notify_subscriber, phone_number)

        print('{} will be notified when ISS passes by {}, {}'
              .format(phone_number, lat, lon))


def notify_subscriber(phone_number):
    msg_body = 'Look up! You may not be able to see it, but the International' \
               ' Space Station is passing above you right now!'

    # Retrieve the latitude and longitude associated with this number.
    lat, lon = redis_server.get(phone_number).split(',')

    # Send a message to the number alerting them of an ISS flyby.
    client.messages.create(to=phone_number,
                           from_='TWILIO_PHONE_NUMBER',
                           body=msg_body)

    # Add the subscriber back to the queue to receive their next flyby message.
    add_to_queue(phone_number, lat, lon)

    print('Message has been sent to {}'.format(phone_number))

Notice that we are adding the subscriber back into the scheduler queue at the end. This allows us to continue sending notifications each time the ISS comes back. We are also now sending them a text message when they are initially added to the queue. Don’t forget to replace where it says TWILIO_PHONE_NUMBER with an actual phone number that you can send messages from.

In order for this code to work, you will need to set your Twilio authentication credentials as environment variables. These values can be found on your account dashboard:

export TWILIO_ACCOUNT_SID=''
export TWILIO_AUTH_TOKEN=''

If you want to see this code in action, add the following two lines to your file to call the add_to_queue function. You can call this with your actual lat and lon, but I’m going to include dummy values for now:

add_to_queue('your_phone_number', 37.7833, -122.4167)

You should receive a message for subscribing, and you can try checking out where the ISS currently is if you want to test to see if the notifications work.

Building a Flask application for subscribers

Flask is a really simple, lightweight web framework for Python. We only need very basic functionality, and Flask gives us exactly what we need.

Create a basic app.py file for our Flask app. We’ll just return an index.html page for now:

import iss

from flask import Flask, request, render_template

app = Flask(__name__)


@app.route('/', methods=['GET'])
def index():
    return render_template('index.html')

app.run(host='0.0.0.0', debug=True)

In the above code, we are setting a route / that takes GET requests. This route only returns an index.html which will act as our signup page. This HTML doesn’t exist yet, so let’s fix that. This will have a form with an input element for their phone number and a submit button. Create a directory called templates, open a file called index.html and add the following code:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>International Space Station Notifications</title>
  </head>
  <body>
    <div id="app">
      <form action="/subscribe" method="POST">
        <h3>You will receive a text message whenever the ISS flies by where you are right now.</h3>
        <h4>Enter your phone number: </h4>
        <input type="text" placeholder="ex:  19999999999" name="number">
        <input type="hidden" name="latitude" id="latitude">
        <input type="hidden" name="longitude" id="longitude">
        <button type="submit">Subscribe</button>
      </form>
    </div>
  </body>
</html>

Notice that we created two hidden elements to hold values for latitude and longitude. We will get these using HTML5’s GeoLocation API. Create a directory called static and write the following code to a file called getLocation.js inside your new directory:

function successFunction(position) {
  var lat = position.coords.latitude;
  var lon = position.coords.longitude;
  document.getElementById('latitude').value = lat;
  document.getElementById('longitude').value = lon;

  console.log('Your latitude is :' + lat + ' and longitude is ' + lon);
}

if (navigator.geolocation) {
  navigator.geolocation.getCurrentPosition(successFunction);
} else {
  alert('It seems like Geolocation, which is required for this page, is not enabled in your browser. Please use a browser which supports it.');
}

Now include this script in your index.html at the bottom of the <body>:

 <!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>International Space Station Notifications</title>
    </head>
  <body>
    <div id="app">
      <form action="/subscribe" method="POST">
        <h3>You will receive a text message whenever the ISS flies by where you are right now.</h3>
        <h4>Enter your phone number: </h4>
        <input type="text" placeholder="ex:  19999999999" name="number">
        <input type="hidden" name="latitude" id="latitude">
        <input type="hidden" name="longitude" id="longitude">
        <button type="submit">Subscribe</button>
      </form>
    </div>
    <script type="text/JavaScript" src="/static/getLocation.js"></script>
  </body>
</html>

Check to make sure this works by running your Flask app:

python app.py

And visit http://localhost:5000/ to see it in action before heading back to the code.

Now create a route for our form to POST to. Open up app.py again and add a /subscribe route that adds the user to the queue with their phone number, latitude and longitude. Open your app.py file again and add this route:

import iss
from flask import Flask, request, render_template

app = Flask(__name__)


@app.route('/', methods=['GET'])
def index():
    return render_template('index.html')


@app.route('/subscribe', methods=['POST'])
def subscribe():
    number = request.form['number']
    lat = request.form['latitude']
    lon = request.form['longitude']
    iss.add_to_queue(number, lat, lon)
    return 'Thanks for subscribing. ' \
           'Expect to receive notifications whenever the ISS flies over you!'

app.run(host='0.0.0.0', debug=True)

We now have an app for people to subscribe to ISS notifications!

Scaling with Twilio Copilot

This is all awesome, but the International Space Station is international after all. What if we wanted to notify friends from all over the world using a pool of Twilio numbers? This is where Twilio Copilot comes into the picture, giving you several new features that make adding international capabilities to our application really easy.

In particular we’ll use Geo-Match to send notifications from a number in the subscriber’s country, and Sticky Sender to make sure this notification always comes from the same Twilio number.

Head over to your Twilio account and create a messaging service in your dashboard. You will be able to add any pre-bought phone numbers to your messaging service or buy new ones as well. Buy multiple phone numbers from different countries and add them to your messaging service.

MessagingService.gif

The Geo-Match and Sticky Sender features are enabled by default when you create a new messaging service.

Changing our code to send from a messaging service instead of a specific phone number is actually fairly easy. Head back over to iss.py and update the notify_subscriber function:

def notify_subscriber(phone_number):
    msg_body = 'Look up! You may not be able to see it, but the International' \
               ' Space Station is passing above you right now!'
    # Retrieve the latitude and longitude associated with this number.
    lat, lon = redis_server.get(phone_number).split(',')

    # Send a message to the number alerting them of an ISS flyby.
    client.messages.create(to=phone_number,
                           messaging_service_sid='MESSAGING_SERVICE_SID',
                           body=msg_body)

    # Add the subscriber back to the queue to receive their next flyby message.
    add_to_queue(phone_number, lat, lon)

    print('Message has been sent to {}'.format(phone_number))

You’ll also want to update the code in the add_to_queue function where a subscription notification is sent:

def add_to_queue(phone_number, lat, lon):
    # Send a text thanking the user for subscribing if they haven't before.
    if not redis_server.exists(phone_number):
        client.messages.create(to=phone_number,
                               messaging_service_sid='MESSAGING_SERVICE_SID',
                               body='Thanks for subscribing to ISS alerts!')

    # Add this phone number to Redis associated with "lat,lon"
    redis_server.set(phone_number, '{},{}'.format(lat, lon))
    
    # Get the datetime object representing the next ISS flyby for this number.
    next_pass_datetime = get_next_pass(lat, lon)

    if next_pass_datetime:
        # Schedule a text to be sent at the time of the next flyby.
        scheduler.enqueue_at(next_pass_datetime,
                             notify_subscriber, phone_number)

        print('{} will be notified when ISS passes by {}, {}'
              .format(phone_number, lat, lon))

You can find your messaging service SID in your Account dashboard as well:

Screen Shot 2015-11-03 at 2.43.40 PM.png

As you can see once you have some phone numbers configured, using the new Twilio Copilot features is as easy as changing one line of code.

Returning to Earth

You were just able to experience how easy it is to incorporate the new features that shipped with Twilio Copilot. All we did was create a messaging service and change one line of code to send from this messaging service as opposed to implementing an entire system ourselves. Thanks to Copilot, we can experience the joy of having a friend in the front seat with the navigation system when scaling SMS delivery systems.

Now anybody can sign up to receive notifications whenever the ISS flies over them. An awesome hacker from Temple University built something similar at a hackathon, except it also involved pizza parties.

PizzaTMNT.png

Feel free to reach out if you have any questions or if you want to show off how you used Copilot features in your Twilio app.

International Space Station Notifications with Python, Redis-Queue and Twilio Copilot

Sending Selfies Without Servers: How To Use Twilio MMS, Amazon Lamba, and Amazon’s API Gateway

$
0
0

We’d be in a much different world if setting up backend servers was as easy as taking a selfie. In that world, you’d be watching your favorite band play as someone hoists their computer in the air, blocks the band, and configures the backend of their app. Of course, that world is fantasy. But, serverless MMS apps are a reality thanks to some slick work from Matt Auerbach.

With the newly announced Amazon Lambda, you can let your code run free without setting up servers. Using his newly found freedom, Matt set up a Twilio MMS app that writes to an S3 bucket, and then triggers a response from Twilio to the selfie sender.

Without further adieu let’s get to building. You can view Matt’s post originally published on GitHub here.

Here’s what the architecture looks like.
Architecture

Lambda + API Gateway Example

This example uses Twilio to save an image from your mobile phone to the AWS cloud. A user sends an image using MMS to a Twilio phone number which sends a request to an Amazon API Gateway endpoint that triggers a Lambda function. The app then returns a publicly accessible link to the image in AWS S3. This app uses AWS Lambda, API Gateway, DynamoDB & S3. It is also 100% serverless!

AWS Lambda

Lambda is a compute service that runs your code in response to events. Events are triggered or invoked by resources in your AWS environment or via API Gateway. Here our Lambda function is triggered by an API Gateway endpoint that Twilio hits after an MMS is received. The Lambda function is responsible for writing user info to DynamoDB, writing the image to S3 with meta data and returning a response to Twilio.

Amazon API Gateway

API Gateway is a fully managed API as a service where you can create, publish, maintain, monitor, and secure APIs at any scale. In this app, we use API Gateway to create an endpoint for Twilio to make a GET request. API Gateway transforms Twilio’s URL encoded request into a JSON object, so that Lambda can process it. Lastly, API Gateway takes Lambda’s response and builds an XML object for Twilio.

Amazon DynamoDB & Amazon S3

DynamoDB is Amazon’s non-relational database service. This app leverages DynamoDB to store user data. S3 provides developers with object level storage that is endlessly scalable. We use S3 to store images received via MMS.

Usage

MattATry it by sending an MMS to (650) 200-1944.

S3 Link

Building the App

Step-by-step on how to configure, develop & deploy this app on AWS.

Housekeeping

  1. Sign-in to AWS or Create an Account.
  2. Pick a region in the console and be consistent throughout this app. Use either
    us-east-1
    ,
    us-west-2
    &
    eu-west-1
    .
  3. Create a table in DynamoDB with a single Hash for primary key of type String. We don’t need any additional indexes and you can keep the read/write capacity at 1 for this example. Screenshot
  4. Create an S3 bucket to ingest MMS images. Ex. mauerbac-ingest
  5. Create an IAM role with access to the S3 bucket & the DynamoDB table.
  6. Create/login to a Twilio account & create a phone number with MMS capability.

 

Lambda

  1. Create a new Lambda function. I’ve provided the function, so we can skip a blueprint.
  2. Give it a name and description. Use Python 2.7 for runtime.
  3. Use the given Lambda function,
    lambda_function.py
    . Read through the module and provide a few variables: Twilio credentials, DynamoDB table name & region and S3 ingest bucket. We will upload as a .zip because our function requires a few external libraries, such as Twilio Python SDK. Compress httplib2, pytz, twilio & lambda_function.py and upload as a .zip file.
  4. The Lambda function handler tells Lambda what .py module to use and corresponding method. Ex.
    main_function.lambda_handler
    would call the method
    def lambda_handler()
    inside
    main_function.py
    . Let’s call it
    lambda_function.lambda_handler
    to match
    lambda_function.py
    .
  5. Select the role we created earlier in the housekeeping steps.
  6. In advanced settings, I recommend changing Timeout to 10 seconds (httplib2 is a bit needy). Currently, max timeout is 60 seconds.
  7. Review & create function.

 

API Gateway

  1. Create a new API. Give it a name and description. This will be our RESTful endpoint.
  2. Create a resource. The path should be
    /addphoto
    , for example.
  3. We need to add a method to this resource. Create a GET method with Lambda integration and select the function we created earlier. API Gateway isn’t able to process POST requests that are URL encoded, so we are using GET as a workaround.
  4. Now let’s setup the Integration Request. Twilio’s GET request will be of type
    application-x-www-form-urlencoded
    . This Integration step will map this type to a JSON object, which Lambda requires. In the Integration Requests page create a mapping template. Content-type is
    application/json
    and template:

 

{
    "body" : "$input.params('Body')",
    "fromNumber" :  "$input.params('From')",
    "image" : "$input.params('MediaUrl0')",
    "numMedia" : "$input.params('NumMedia')"
}

More on Intergration Requests.

$input.params()
parse the request object for the corresponding variable and allows the mapping template to build a JSON object. Screenshot
5. Let’s ensure the response is correct. Twilio requires valid XML. Change the response model for 200 to Content-type:
application/xml
. Leave models empty. Screenshot
6. Lambda cannot return proper XML, so API Gateway needs to build this. This is done in Integration response as another mapping template. This time we want to create Content-type: application/xml and template:
#set($inputRoot = $input.path('$'))
<?xml version="1.0" encoding="UTF-8"?>
<Response>
    <Message>
        <Body>
            $inputRoot
        </Body>
    </Message>
</Response>

Our Lambda function solely returns a string of the SMS body. Here we build the XML object and use

$inputRoot
as the string. Here’s what that looks like.
responseModel

7. Now let’s deploy this API, so we can test it! Click the Deploy API button.

Connecting The Dots & Testing

    1. We should now have a publicly accessible GET endpoint. Ex:
      https://xxxx.execute-api.us-west-2.amazonaws.com/prod/addphoto
    2. Point your Twilio number to this endpoint. twilioselfie
    3. Our app should now be connected. Let’s review: Twilio sends a GET request with MMS image, fromNumber and body to API Gateway. API Gateway transforms the GET request into a JSON object, which is passed to a Lambda function. Lambda processes the object and writes the user to DynamoDB and writes the image to S3. Lambda returns a string which API Gateway uses to create an XML object for Twilio’s response to the user.
    4. First, let’s test the Lambda function. Click the Actions dropdown and Configure sample event. We need to simulate the JSON object passed by API Gateway. Example:

{ 
  "body" : "hello",
  "fromNumber" : "+19145554224" ,
  "image" : "https://api.twilio.com/2010-04-01/Accounts/AC361180d5a1fc4530bdeefb7fbba22338/Messages/MM7ab00379ec67dd1391a2b13388dfd2c0/Media/ME7a70cb396964e377bab09ef6c09eda2a",
  "numMedia" : "1"
}

Click Test. At the bottom of the page you view Execution result and the log output in Cloudwatch logs. This is very helpful for debugging.

  1. Testing API Gateway requires a client that sends requests to the endpoint. I personally like the Chrome Extension Advanced Rest Client Send the endpoint a GET request and view its response. Ensure the S3 link works. You can also test by sending an MMS to phone number and checking the Twilio logs.

Troubleshooting

  1. Ensure your Lambda function is using the correct IAM role. The role must have the ability to write/read to DynamoDB and S3.
  2. All Lambda interactions are logged in Cloudwatch logs. View the logs for debugging.
  3. Lambda/API Gateway Forums

Architecture

Architecture

Sending Selfies Without Servers: How To Use Twilio MMS, Amazon Lamba, and Amazon’s API Gateway

Building A Twilio Endpoint Load Tester with Python, Go and A Developer’s Empathy

$
0
0

Here are a few bits of advice: Test code in production. Skip the code reviews. Ship the app before load testing it. With these helpful tips, you’ll be on your way to certain doom in no time.

Sai Yerram once had a brush with the perils of not load testing, and he’s built a Twilio load tester to help his fellow developers avoid the danger he once faced. He built the app with Python, Go and empathy.

Sure there’s frameworks to use, and languages Sai prefers. But. The motivation behind the load tester is Sai’s desire to give back to developers. “It’s essential that every developer possess empathy for their users and other developers.”

In that same vein, Sai empathizes with a teacher who bore the brunt of consequence from a, shall we say, impromptu load test gone wrong. Sai ran an education startup that allowed teachers to log student attendance via Twilio SMS. When the teacher gave out the wrong code for students to text, the impromptu load test was off and running.

An Impromptu Load Test

“One time, a professor gave an incorrect code to the classroom and the system responded with invalid code. Students thought they mistyped and would send multiple texts. Imagine 60+ students sending 3-4 times in a flash of 1-5 seconds,” said Sai. “[Each] invalid code sent via SMS, the system would log 10 lines in log files, create 4 records in database, reply via SMS and one email to the professor.”

With a few hundred texts in his inbox, and a few hundred emails, the professor was not very pleased. It was a learning opportunity for Sai, one he’s made the best of.

“We had comprehensive unit/integration testing but no good way to test flash loads with valid/invalid data and observe resource utilizations.” Now Sai has that tool at the ready, thanks to his recent love affair with Go, his Python expertise, and of course, his empathy.

The Learning Experience

“I was learning Go recently. I was drawn to its simplicity of writing concurrent programs and its ability to distribute binaries. It took me two days to write this program. I have been Python developer for some time, so I envisioned the entire thing in Python first, then searched for Go counterparts and stitched it together.”

Try it out yourself right here, or read about it below. Learn more about Sai, and his new company Tradewave, an automated trading platform for bitcoin exchanges.

The Load Tester
 
The goal of the script is to load test your twilio endpoint, just like a simple

ab
tester. You can pass json data to the endpoint (both valid and invalid) and see how it utilizes resources and get an estimate of costs of using twilio with high load. For e.g. You can send 5000 invalid requests and see how your server utilizes its resources (does it flood the log files, emails, database, costs of receiving/replying etc).

In order to run the script, make a copy of the sample

ini
files and then run
$ go build tw_load.go
$ ./tw_load -f ./tw_load.ini -d ./tw_data.ini

Once completed, it will print the results of the load testing along with costs of sending/receiving messages (sample response below)
Sample

Config INI

Contains information such as account token, load factors. Check ini files for more documentation.

Data INI

Contains data that needs to be sent to the endpoint. Check ini files for more documentation.

Tests

To run tests, make a copy of the sample ini files and then adjust values. Then run

go test -v

TODO

Remove the data ini file and instead choose a simple file format (txt) by having each line as a json data row. We can then distribute data across the requests

Building A Twilio Endpoint Load Tester with Python, Go and A Developer’s Empathy

Pound For Pound, Text for Text: FitMeal Tracks Your Calories via Twilio SMS

$
0
0

The culmination of Georges Duverger’s quest to lose weight took place on Vice’s Snapchat feed.

Georges gained weight the way many tech employees gain weight. He was working long, stressful hours and traditionally went for the easiest food option, which is rarely the healthiest option. After doing this for years, he decided he needed to find a way to lose weight using a tool he knew well – code. A few years later, FitMeal was born, and featured on Vice.

fitmealFitMeal lets you record what you eat via SMS, and texts you back nutritional information about your meal – calories, grams of fat etc.  Vice puts what FitMeal does quite simply in their headline “Text This Number To See How Unhealthy Your Lunch Was”. When Georges noticed a massive uptick in registrations he thought it was from the article. But when he checked his Twilio SMS logs, he realized that wasn’t the case.

In his logs Georges saw a text along the lines of “saw you on Vice’s snapchat!” He replied to the user directly. But that process wasn’t scalable as users kept rolling in. Eventually he had to build a waitlist for FitMeal. The validation felt like it was a long time coming for Georges.

Here’s what the Vice-induced traffic spike looked like in Georges’ Twilio logs.
fitmeal-twilio

 

FitMeal was built using Python, deployed on Heroku and uses a Django Database. But, it started on pen and paper. When Georges was looking for a good way to track his eating habits he struggled to find something that was both easy to use, and private. He tried posting his meals to a private blog, but he had to remember to do that after each meal, and often missed a few. Texting seemed like the easiest and most reasonable medium for Georges.

“I can do it in 30 seconds, with no hands. I just tell Siri what to text my Twilio number,” says Georges. “Naturally, I ended up with SMS because it’s the simplest entry method.”

For Georges, the initial success of FitMeal pays off in double. He lost the weight, and he found success with a product he built himself. The sensation of personal and professional success is not lost upon him, but he prefers to keep it professional.

“After a few experiments, I found something that resonates with people. That feels good.”

Here’s a little portion of George’s code that shows how FitMeal replies with the calorie count of your meal.
 

def reply(request):
"""
Reply to a text message with nutrition facts (edited snippet)
"""
args = {}
if request.method == 'POST':
# Get the text message
text = request.POST.get('text')
# Find the food
features = app_process.extract_features(text)
# Find the nutrition facts
nutrients = app_process.compute_nutrients(features)
# Write the response
response = app_process.compose_response(nutrients)
if response:
args.update({'response': response})
else:
logger.error(text)
return render_to_response('reply.xml', args, context_instance=RequestContext(request), content_type='application/xml')

Learn more about FitMeal here.

Pound For Pound, Text for Text: FitMeal Tracks Your Calories via Twilio SMS

City Chat with Python, Django and Twilio IP Messaging

$
0
0

Looking for a new apartment in your city? Is it election day and you want to remind people to get out and vote? Want to poll residents for the best restaurant in their neighborhood?

In this blog post, we’ll build a messaging application where messages are broadcast to recipients based on the city reported by their browser location. Having everyone in your city join together in a chat application may seem crazy, but buckle up, because we’re going to give it a try. By combining Python, Django, Twilio IP Messaging and the Google Maps API we’ll take our best shot at making it easier to find an apartment, remind your neighbors to vote or poll your fellow city residents.

Tools We’ll Need

Before we dive into building our neighborhood-based chat application, let’s take a look at the tools we’ll be using throughout this blog post.

  1. The 1.9 version of the Django web framework
  2. Twilio Python helper library >= version 5.0.0
  3. A free Twilio account and the Twilio IP Messaging public beta API
  4. Google’s Maps API for determining what city a user is located in

Don’t worry about installing these tools just yet – we’ll handle that in the sections below as we go through this tutorial. If you want to see the completed code from this blog post at any time check out the GitHub repository with the finished project. Before we start writing our code though let’s walk through preparing our Python environment for development.

Python Environment Setup

Our prep work starts with getting our Python development environment ready to build our neighborhood-based chat application. In this section we’ll handle the following steps to make sure you’re ready to run the code:

  1. Check that Python is installed correctly
  2. Set up and activate a new virtualenv
  3. Install required Python dependencies into our virtualenv using pip

Setup Step 1: Check the Python installation

If you’re on Mac OS X or Linux, you’ve already got Python installed on your system. Check the version by going into the terminal and typing python —version. You’ll see the precise version number, something like Python 2.7.6. If you’re on Python 2.6 or greater, you’re good to go.

For Windows, make sure you download and install the Python .exe installation package. Either Python 2 or 3 is fine. If you want further information about the 2 versus 3 version debate, there’s plenty more information written by experienced Python developers on the topic. My recommendation is to use Python 3.5 unless you have a pressing reason to use an earlier version, because the Python community is now migrating to Python 3.
Once Python is installed on your system and you can run it with the python command, we’re ready to set up a virtualenv.

Setup Step 2: Set up a new virtualenv and activate it

Virtualenv is a dependency isolation library that makes it much easier to switch between Python versions and various code packages you’re using on different projects. Create a virtualenv in a location you’ll remember using the following command. In my case, I have a folder called Envs under my home directory that keeps my virtualenvs organized.

virtualenv ~/Envs/citychat
source ~/Envs/citychat/bin/activate

We have our virtualenv activated and we should see our command prompt show the name of our virtualenv, such as (citychat)$. With the virtualenv in place and activated, we can use pip to install our dependencies.

Setup Step 3: Install dependencies with pip

pip handles Python library installations. With your virtualenv still activated, run the following command to install our Django and Twilio library dependencies.

(citychat)$ pip install django==1.9 twilio==5.0.0

You may be able to install the very latest versions of the Django and Twilio packages but it’s preferable to peg dependencies to specific version numbers just in case there are future backwards-incompatible modifications. With those libraries installed, we can start building our Django project that will run the chat application.

Starting Our Django Project

We now have Django installed along with the django-admin command. django-admin helps get the boilerplate directories and code created for our project. Run the following two commands to start the project and change into the newly created directory.

(neighborchat)$ django-admin startproject citychat
(neighborchat)$ cd citychat

We also need to create an app within the project with the following command.

(neighborchat)$ python manage.py startapp chat

Django’s just created a whole bunch of files for us. To make sure we’ve got the right set up, ensure the created folders look like the directory and subdirectories in the following image.

citychat.png

We’ll also have __init__.py, settings.py, urls.py and wsgi.py files created for us in the citychat/citychat subdirectory. Our tutorial will modify most of these files along the way. If you’re unfamiliar with why Django created these folders and files be sure to go through the Django quickstart for more context.

We need to modify a few lines in settings.py and set up our environment variables to connect our Django project to our Twilio settings.

Add the following line to the INSTALLED_APPS list in settings.py to add the chat app to our Django project.

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'chat'
]

Before you close settings.py, append the following lines to the file so we can establish the necessary Twilio IP Messaging settings we’ll use a bit later in the post.

TWILIO_ACCOUNT_SID = os.environ.get('TWILIO_ACCOUNT_SID', None)
TWILIO_API_KEY = os.environ.get('TWILIO_API_KEY', None)
TWILIO_API_SECRET = os.environ.get('TWILIO_API_SECRET', None)
TWILIO_IPM_SERVICE_SID = os.environ.get('TWILIO_IPM_SERVICE_SID', None)

Next we need to set up our environment variables so the os module can get them when our Django project starts up. How to set environment variables depends on your operating system. Here are some handy guides if you’re on Windows, Mac OS X or Ubuntu Linux that’ll help with the specific steps.

Five environment variables will need to be set:

  • TWILIO_ACCOUNT_SID – found on your Twilio account dashboard
  • TWILIO_AUTH_TOKEN – also found on your Twilio account dashboard; not required by our Django app but will be used to create the IP Messaging Service SID later
  • TWILIO_API_KEY – an API key created specifically for this application
  • TWILIO_API_SECRET – a secret string just for this API key
  • TWILIO_IPM_SERVICE_SID – a unique identifier registered with Twilio for our IP Messaging application

On Mac OS X and Linux, you can set environment variables with the export shell command. Typically, I store these environment variables in a .env file at the root directory of the project during development. We haven’t yet created the TWILIO_API_KEY, TWILIO_API_SECRET or TWILIO_IPM_SERVICE_SID credentials yet though, so let’s walk through how to create those now.
In order to get chat working we have to handle our remaining environment variables.

Let’s create and then set the TWILIO_API_KEY and TWILIO_API_SECRET now. Head into the Twilio portal and click on Dev Tools in the navigation bar.
click-dev-tools.jpg
Click the Dev Tools link in the top navigation bar. We’re taken to the API Explorer but we want to go to the API Keys page. Click the API Keys link in the secondary nav bar.

api-key-nav.jpg

Click the “Create an API Key” button. We’re taken to the following page where we can given our new API key a friendly name. Call it “City Chat” and click the “Create API Key” button.

new-api-key.jpg

After pressing the “Create API Key” button you’ll be taken to a page with the new Sid and Secret tokens are presented. Make sure to copy the Secret token as it will not be displayed again after you exit from this page.
save-api-key.png

There’s one more environment variable called TWILIO_IPM_SERVICE_SID that we’ll need to create via the command line before we have everything ready to modify our .env file.

There are two ways to create this IPM Service and grab the SID. One way is to go to the IP Messaging Services page, click the “Create an IP Messaging Service” button, enter a friendly name and let Twilio produce the new service. The second way is to programmatically generate the service via an API call. Let’s use the second method and create the IPM Service from the command line using the following cURL command. The IPM Service we create will include a SID value we need to specify in our TWILIO_IPM_SERVICE_SID environment variable.

$(citychat) curl -XPOST https://ip-messaging.twilio.com/v1/Services -d "FriendlyName=CityChat" -u $TWILIO_ACCOUNT_SID:$TWILIO_AUTH_TOKEN

Make sure your $TWILIO_ACCOUNT_SID and $TWILIO_AUTH_TOKEN environment variables are still set if you receive an error. If not, your response should look something like the following JSON string.

{"sid": "IS0beb86d261a0918jl123b00476d5e5e43", 
"account_sid": "ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "friendly_name": "CityChat", 
"date_created": "2015-11-25T06:19:17Z", "date_updated": "2015-11-25T06:19:17Z", 
"default_service_role_sid": "RLffcd09f7df674bcd82cdcbbc4362e257", 
"default_channel_role_sid": "RL2f13b41du1i2ohoi12412iu40a3d98", 
"default_channel_creator_role_sid": "RL2f4b001l2k3jl21kj3lk12k123lfc1", 
"typing_indicator_timeout": 5, "webhooks": {}, 
"url": "https://ip-messaging.twilio.com/v1/Services/IS0beb86d261a0918jl123b00476d5e5e43", 
"links": {"channels": "https://ip-messaging.twilio.com/v1/Services/IS0beb86d261a0918jl123b00476d5e5e43/Channels", 
"roles": "https://ip-messaging.twilio.com/v1/Services/IS0beb86d261a0918jl123b00476d5e5e43/Roles", 
"users": "https://ip-messaging.twilio.com/v1/Services/IS0beb86d261a0918jl123b00476d5e5e43/Users"}}

We need the string that is the value to the “sid” key for our TWILIO_IPM_SERVICE_SID environment variable. With the final value created, we can set all of the environment variables for our application.

export TWILIO_ACCOUNT_SID='ACxxxxxxxxxxxxxxxxxxxxxx'
export TWILIO_AUTH_TOKEN='yyyyyyyyyyyyyyyyyyyy'
export TWILIO_API_KEY='{ your new API key }'
export TWILIO_API_SECRET='{ your new API secret }'
export TWILIO_IPM_SERVICE_SID='{ sid passed back from curl request }'

It’s time to test out our application to make sure our environment settings are in place and that we’re on a solid foundation to keep coding. From the root directory of our project where manage.py is located, run the Django development server.

$(neighborchat) python manage.py runserver

With your favorite web browser go to http://127.0.0.1:8000 and you should see the following default success page.
django-it-worked.jpg
Our basic application files and environment variables are set up. Now let’s build a web page with a map that’ll serve our chat application.

Mapping Our Location

Now we can flesh out the code for our chat app within the citychat Django project. Start by updating the chat/views.py file with the following two highlighted lines. This function will look for the chat.html template and render the template after we create the file.

from django.shortcuts import render


def chat(request):
    return render(request, 'chat.html')

Create a new file named chat/urls.py with the following code to route our application’s default endpoint to the chat view function we just wrote.

from django.conf.urls import url
from . import views


urlpatterns = [
    url(r'$', views.chat, name="chat"),
]

The urls.py file maps routes for our application to views in the views.py file. In our case, there is a single url for the chat function that matches the root url for the server.

In order for the chat/urls.py to be found, we need to update the citychat/citychat/urls.py file, which handles url mappings for every app in a Django project. Update the following two lines so this urls.py file can match with the chat/urls.py routes.

from django.conf.urls import url
from django.conf.urls import include
from django.contrib import admin


urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^', include('chat.urls', namespace='chat')),
]

One more file needs to be created then we can give our application a quick test. Create a directory under citychat/chat named templates. Under templates create a file that matches up with the name of the Django template used in the chat function, which in our case is chat.html. Write or copy the following markup into the new chat.html file.

{% load staticfiles %}
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>City Chat</title>
    <link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">
    <style>
      #map {width: 100%; height: 400px; background: #eee;}
    </style>
    <script src="//maps.googleapis.com/maps/api/js"></script>
  </head>
  <body>
    <div class="container">
      <h1>City Chat</h1>
      <div id="map"></div>
      <p>
         Latitude: <input type="text" id="lat" readonly=true />,
         Longitude: <input type="text" id="long" readonly=true />
      </p>
      <section>
        <div id="messages"></div>
        <input id="chat-input" type="text" placeholder="say anything" autofocus class="form-control" />
      </section>
    </div>

    <script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js">
    </script>
    <script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
    <script src="//media.twiliocdn.com/sdk/rtc/js/ip-messaging/v0.8/twilio-ip-messaging.min.js"></script>
    </script>
    <script src="{% static 'js/chat.js' %}"></script>
  </body>
</html>

In the above template, we’re setting the stage to display a map showing our current location with a chat box below the map.

There is a referenced JavaScript file, chat.js, that is not hosted externally so it doesn’t exist yet in our Django project. Since that file won’t load our page would look like the screenshot below if we accessed it now. If you pull up http://localhost:8000 in Chrome you can see in the Chrome DevTools that a couple of files are not loading.
city-chat-first-loaded.png

We need to fix that missing file to get our map displayed and determine the city in which we should be chatting.

Create a directory under citychat/chat named static that will hold our static files. Under citychat/chat/static create one more subdirectory named js. Create the file named chat.js under citychat/chat/static/js with the following code.

function positionFound(position) {
  document.getElementById('lat').value = position.coords.latitude;
  document.getElementById('long').value = position.coords.longitude;
}

if (navigator.geolocation) {
  navigator.geolocation.getCurrentPosition(positionFound);
} else {
  alert('It appears that required geolocation is not enabled in your browser.');
}

Head back to your web browser and refresh http://localhost:8000/. Make sure to click “Allow” or “Accept” when asked by the browser whether or not you want to share your current location. For example, in Chrome you’d click accept to the little dropdown from the URL bar.

click-allow.jpg

Okay, now we’ve got the user’s latitude and longitude displayed on the page! However, there’s still that ugly gray box taking up space where our map should be.
lat-long-browser.png
Let’s initialize a map using the user’s location and translate the latitude and longitude into a marker on our page. Modify the citychat/chat/static/js/chat.js JavaScript file we were just working on that obtained the user’s browser location.

function positionFound(position) {
  document.getElementById('lat').value = position.coords.latitude;
  document.getElementById('long').value = position.coords.longitude;
  mapAndChat();
}

// creates the map based on user's browser location 
function drawMap() {
  var mapCanvas = document.getElementById('map');
  var latLng = new google.maps.LatLng(document.getElementById('lat').value, document.getElementById('long').value);

  var mapOptions = {
    center: latLng,
    zoom: 12,
    mapTypeId: google.maps.MapTypeId.ROADMAP
  }
  var map = new google.maps.Map(mapCanvas, mapOptions);
  var marker = new google.maps.Marker({
    position: latLng,
    map: map,
    title: 'Your location'
  });
}

function mapAndChat() {
  drawMap();
  // chat initialization will go here
}

if (navigator.geolocation) {
  navigator.geolocation.getCurrentPosition(positionFound);
} else {
  alert('It appears that required browser geolocation is not enabled.');
}

city-chat-second-loaded.jpg

Hey now, that looks a little bit better! Unfortunately, chat is still not yet working.

Adding Chat via IP Messaging

Our embedded map may look slick but chat has yet to land in our app. Earlier in this post we set all the environment variables we need to hook Twilio IP Messaging into our application. With those credentials in place we can now create JSON Web Tokens (JWT) on the server that will be requested by the browser and sent as soon as they are generated. Our application will need a new endpoint though for the browser to request the token by so open up citychat/chat/urls.py and add the single line highlighted below.

from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'token$', views.token, name="token"),
    url(r'$', views.chat, name="chat"),
]

The new line added to urls.py creates a /token endpoint for our application and references a views.token function. We need to write that token function in the citychat/chat/views.py file as highlighted below.

from django.conf import settings
from django.http import JsonResponse
from django.shortcuts import render

from twilio.access_token import AccessToken, IpMessagingGrant


def chat(request):
    return render(request, 'chat.html', {})


def token(request):
    device_id = request.GET.get('device', 'unknown')
    identity = request.GET.get('identity', 'guest').encode('utf-8')
    endpoint_id = "NeighborChat:{0}:{1}".format(device_id, identity)
    token = AccessToken(settings.TWILIO_ACCOUNT_SID, settings.TWILIO_API_KEY,
                        settings.TWILIO_API_SECRET, identity)
    grant = IpMessagingGrant()
    grant.service_sid = settings.TWILIO_IPM_SERVICE_SID
    grant.endpoint_id = endpoint_id
    token.add_grant(grant)
    response = {'identity': identity, 'token': token.to_jwt()}
    return JsonResponse(response)

In the above code, we include a few new imports for our project’s settings and the JsonResponse from the Django library. The AccessToken and IpMesssagingGrant are specific to Twilio IP Messaging. They allow us to create the appropriate JWT token to return to the requesting client based on our Twilio account credentials stored in our environment variables.

What about the front end browser code that calls for the access token? We need to take care of that now by modifying citychat/chat/static/js/chat.js with the following highlighted lines. These two functions, print and printMessage are just to help us attach the information messages and chat messages that need to be displayed on the page.

// Helper function to print info messages to the chat window
function print(infoMessage, asHtml) {
    var $msg = $('
'); if (asHtml) { $msg.html(infoMessage); } else { $msg.text(infoMessage); } $chatWindow.append($msg); } // Helper function to print chat message to the chat window function printMessage(fromUser, message) { var $user = $('').text(fromUser + ': '); if (fromUser === username) { $user.addClass('me'); } var $message = $('').text(message); var $container = $('
'); $container.append($user).append($message); $chatWindow.append($container); } function positionFound(position) { document.getElementById('lat').value = position.coords.latitude; document.getElementById('long').value = position.coords.longitude; mapAndChat(); } // creates the map based on user's browser location function drawMap() { var mapCanvas = document.getElementById('map'); var latLng = new google.maps.LatLng(document.getElementById('lat').value, document.getElementById('long').value); var mapOptions = { center: latLng, zoom: 12, mapTypeId: google.maps.MapTypeId.ROADMAP } var map = new google.maps.Map(mapCanvas, mapOptions); var marker = new google.maps.Marker({ position: latLng, map: map, title: 'Your location' }); } function mapAndChat() { drawMap(); // chat initialization will go here } if (navigator.geolocation) { navigator.geolocation.getCurrentPosition(positionFound); } else { alert('It appears that required browser geolocation is not enabled.'); }

With our helper printing functions in place, let’s begin adding the JavaScript chat code that connects our application with the IP Messaging service. Begin by adding several variables to store a reference to the chat window, the Twilio IP Messaging service, the messaging channel for our city, the user’s location and the name of the city based on the latitude and longitude from the user’s browser.

Make these changes by continuing to add the following highlighted lines of JavaScript to citychat/chat/static/js/chat.js.

var $chatWindow = $('#messages');
var accessManager;
var messagingClient;
var cityChannel;
var username;
var userLocation;
var city;

// Helper function to print info messages to the chat window
function print(infoMessage, asHtml) {
    var $msg = $('
'); if (asHtml) { $msg.html(infoMessage); } else { $msg.text(infoMessage); } $chatWindow.append($msg); } // Helper function to print chat message to the chat window function printMessage(fromUser, message) { var $user = $('').text(fromUser + ': '); if (fromUser === username) { $user.addClass('me'); } var $message = $('').text(message); var $container = $('
'); $container.append($user).append($message); $chatWindow.append($container); } function positionFound(position) { document.getElementById('lat').value = position.coords.latitude; document.getElementById('long').value = position.coords.longitude; mapAndChat(); } // creates the map based on user's browser location function drawMap() { var mapCanvas = document.getElementById('map'); var latLng = new google.maps.LatLng(document.getElementById('lat').value, document.getElementById('long').value); var mapOptions = { center: latLng, zoom: 12, mapTypeId: google.maps.MapTypeId.ROADMAP } var map = new google.maps.Map(mapCanvas, mapOptions); var marker = new google.maps.Marker({ position: latLng, map: map, title: 'Your location' }); } function mapAndChat() { drawMap(); chatBasedOnCity(); } function chatBasedOnCity() { var latitude = $('#lat').val(); var longitude = $('#long').val(); $.getJSON('http://maps.googleapis.com/maps/api/geocode/json?latlng=' + latitude + ',' + longitude + '&sensor=true', {}, function(locationData) { userLocation = locationData.results[0]["formatted_address"]; username = userLocation.replace(/\s/g, '_'); city = locationData.results[0].address_components[3].long_name; createChat(); }); } function createChat() { $.getJSON('/token', {identity: username, device: 'browser'}, function(data) { print('It looks like you are near: ' + '' + userLocation + '', true); accessManager = new Twilio.AccessManager(data.token); messagingClient = new Twilio.IPMessaging.Client(accessManager); var promise = messagingClient.getChannelByUniqueName(city); promise.then(function(channel) { cityChannel = channel; if (!cityChannel) { // If channel does not exist then create it messagingClient.createChannel({ uniqueName: city, friendlyName: city }).then(function(channel) { console.log('Created channel:'); console.log(channel); cityChannel = channel; }); } else { console.log('Found channel:'); console.log(cityChannel); } }); }); } if (navigator.geolocation) { navigator.geolocation.getCurrentPosition(positionFound); } else { alert('It appears that required browser geolocation is not enabled.'); }

The new function chatBasedOnCity takes the latitude and longitude of the user’s browser and looks up the location with the Google Maps API. Google Maps returns JSON with an approximate address for the latitude and longitude along with the city name. We use the city name and the address in the createChat function. createChat obtains the a JWT access token created by our /token endpoint then uses that to authenticate with Twilio IP Messaging. We then try to create create a channel for our city, but if one already exists we don’t need to create a new channel, we simply log that it exists.

We can test out the above code, but it won’t yet join us to the city messaging channel we need to chat with other users in the same city. Time to finish our JavaScript by joining the new or existent chat channel. Add the highlighted lines below that join our city chat channel and listen for new messages that are sent on it.

var $chatWindow = $('#messages');
var accessManager;
var messagingClient;
var cityChannel;
var username;
var userLocation;
var city;

// Helper function to print info messages to the chat window
function print(infoMessage, asHtml) {
    var $msg = $('
'); if (asHtml) { $msg.html(infoMessage); } else { $msg.text(infoMessage); } $chatWindow.append($msg); } // Helper function to print chat message to the chat window function printMessage(fromUser, message) { var $user = $('').text(fromUser + ': '); if (fromUser === username) { $user.addClass('me'); } var $message = $('').text(message); var $container = $('
'); $container.append($user).append($message); $chatWindow.append($container); } // creates the map based on user's browser location function drawMap() { var mapCanvas = document.getElementById('map'); var latLng = new google.maps.LatLng(document.getElementById('lat').value, document.getElementById('long').value); var mapOptions = { center: latLng, zoom: 12, mapTypeId: google.maps.MapTypeId.ROADMAP } var map = new google.maps.Map(mapCanvas, mapOptions); var marker = new google.maps.Marker({ position: latLng, map: map, title: 'Your location' }); } function positionFound(position) { document.getElementById('lat').value = position.coords.latitude; document.getElementById('long').value = position.coords.longitude; mapAndChat(); } function mapAndChat() { drawMap(); chatBasedOnCity(); } function chatBasedOnCity() { var latitude = $('#lat').val(); var longitude = $('#long').val(); $.getJSON('http://maps.googleapis.com/maps/api/geocode/json?latlng=' + latitude + ',' + longitude + '&sensor=true', {}, function(locationData) { userLocation = locationData.results[0]["formatted_address"]; username = userLocation.replace(/\s/g, '_'); city = locationData.results[0].address_components[3].long_name; createChat(); }); } function createChat() { $.getJSON('/token', {identity: username, device: 'browser'}, function(data) { print('It looks like you are near: ' + '' + userLocation + '', true); accessManager = new Twilio.AccessManager(data.token); messagingClient = new Twilio.IPMessaging.Client(accessManager); var promise = messagingClient.getChannelByUniqueName(city); promise.then(function(channel) { cityChannel = channel; if (!cityChannel) { // If channel does not exist then create it messagingClient.createChannel({ uniqueName: city, friendlyName: city }).then(function(channel) { console.log('Created channel:'); console.log(channel); cityChannel = channel; setupChannel(); }); } else { console.log('Found channel:'); console.log(cityChannel); setupChannel(); } }); }); function setupChannel() { // Join the general channel cityChannel.join().then(function(channel) { print('Joined channel "' + channel.uniqueName + '" as ' + '' + username + '.', true); }); // Listen for new messages sent to the channel cityChannel.on('messageAdded', function(message) { printMessage(message.author, message.body); }); } // Send a new message to the general channel var $input = $('#chat-input'); $input.on('keydown', function(e) { if (e.keyCode == 13) { cityChannel.sendMessage($input.val()) $input.val(''); } }); } if (navigator.geolocation) { navigator.geolocation.getCurrentPosition(positionFound); } else { alert('It appears that required browser geolocation is not enabled.'); }

The above setupChannel function joins the channel based on our current city’s name and listens for new messages sent on the channel. We also enable chat input to send messages on our channel so other clients listening for messages on the channel will see and display them on the screen.

Go ahead and refresh the localhost:8000 page in your browser to test out the results.

City Chatting Away

Congratulations!  Our application is now running on our localhost machine on port 8000. When you want to open up the app to other folks without access to your system, you can use a localhost tunneling tool like Ngrok or deploy it to a server with a fixed IP address.

If you’re looking for more Python code to implement IP messaging into your application, check out the IP Messaging JavaScript Quickstart that includes a Python with Flask web application tutorial.

We can now use our application to chat with other residents that join in the same IP Messaging channel based on the city name gleaned from our browser location. Have a few folks from your city join the app and try it out to find an apartment, get out the vote or poll fellow city residents. What other situations do you think IP messaging could be applied to? I’d like to hear from you if you run into any issues throughout the post so feel free to drop a comment below or contact me via:

City Chat with Python, Django and Twilio IP Messaging

How to Verify Phone numbers in Python with the Twilio Lookup API

$
0
0

Twilio Lookup is a simple REST API with a ton of utility. You can use Lookup to check whether a number exists, format international numbers to local standards, determine whether a phone is a landline or can receive text messages, and even discover information about the carrier associated with that phone number.

In this post, we’re going to learn how to deal with valid and invalid numbers using the Twilio Python library. This code could work in any context whether you use it to look up customer numbers in your production Django app or just have a basic script you want to run to check numbers in a local database. This should also work regardless of whether you are using Python 2 or 3.

Before getting started you will need to have Python installed as well as pip, which should come with most versions of Python. You will also need to sign up for free Twilio account.

Getting started

First off you should run a virtualenv in order to cleanly install python libraries with pip. If you are unfamiliar with pip and virtualenv you can check out this guide.

Head over to your terminal and install the Twilio Python module:

pip install twilio>=5.0.0

You will also need to have your Account SID and Auth Token handy, which you can grab from your Twilio account dashboard.

twilio-credentials.gif

Now take those values and set them as environment variables:

export TWILIO_ACCOUNT_SID='Replace this with your Twilio Account SID*'
export TWILIO_AUTH_TOKEN='*Replace this with your Twilio Auth Token*'

With these set as environment variables, the Twilio Python module will be able to access them so you don’t have the problem of accidentally committing your account credentials to version control. You can also check out this guide on managing your environment variables.

Looking up valid phone numbers

You can check out the Lookup page if you want to play around with the API and see what type of data a request will return. Try entering a phone number and take a look at the JSON response object.
lookup-number.gif

Here’s a quick code sample for that type of lookup without getting the carrier information:

from twilio.rest.lookups import TwilioLookupsClient

client = TwilioLookupsClient()
number = client.phone_numbers.get("15108675309")
print(number.national_format)  # => (510) 867-5309

This basic functionality is free, but you can get more information by doing a carrier lookup, which will not cost much. Carrier lookups are commonly used to determine if a number is capable of receiving SMS/MMS messages.

Carrier lookups contain much more useful information, but require an extra parameter:

# Download the Python helper library from twilio.com/docs/python/install
from twilio.rest.lookups import TwilioLookupsClient

# Your Account Sid and Auth Token from twilio.com/user/account
# Store them in the environment variables:
# "TWILIO_ACCOUNT_SID" and "TWILIO_AUTH_TOKEN"
client = TwilioLookupsClient()

number = client.phone_numbers.get("15108675309", include_carrier_info=True)
print(number.carrier['name'])  # => Sprint Spectrum, L.P.

Looking up invalid phone numbers

You can think about Twilio Lookup as a phonebook REST API. In this “online phonebook” the phone numbers serve as a unique ID. When you try to look up a phone number that does not exist you will get a 404 response.

Head over to the Lookup homepage again to see this in action when you check a number that doesn’t exist:

invalid-phone-lookup.gif

When the Twilio Python library encounters a 404 it will throw a “TwilioRestException”. If you change our current code to look up an incorrect phone number you will see this in action:

Screen Shot 2016-02-01 at 4.42.10 PM.png

The error code for this is 20404, so if we want to write a function that validates phone numbers we can check to see if an exception was raised with code 20404.

Create a file called “lookup.py” and try this code out:

# Download the Python helper library from twilio.com/docs/python/install
from twilio.rest.lookups import TwilioLookupsClient
from twilio.rest.exceptions import TwilioRestException

# Your Account Sid and Auth Token from twilio.com/user/account
# Store them in the environment variables:
# "TWILIO_ACCOUNT_SID" and "TWILIO_AUTH_TOKEN"
client = TwilioLookupsClient()


def is_valid_number(number):
    try:
        response = client.phone_numbers.get(number, include_carrier_info=True)
        response.phone_number  # If invalid, throws an exception.
        return True
    except TwilioRestException as e:
        if e.code == 20404:
            return False
        else:
            raise e

print(is_valid_number('19999999999'))  # False
print(is_valid_number('15108675309'))  # True

You now have a function you could use anywhere in your code for validating phone numbers.

Looking ahead

So now you know how to use the REST API phone book that is Twilio Lookup. You’ve used it for validating phone numbers, but you can also do cool things like phone number formatting and checking to see whether a number can receive text messages or not.

You might want to use one of these more common cases, but I’m always excited to see what other crazy ideas people think of with new APIs. I cannot wait to see what you build with Twilio Lookup.

If you have any questions or want to show off what you built, feel free to reach out:

How to Verify Phone numbers in Python with the Twilio Lookup API


Introducing Twilio’s Next Generation Helper Libraries

$
0
0

At Twilio, 77% of all requests to our REST API are made by a helper library user-agent. It’s the most common touch point between Twilio and you, the developer.

That’s why we’re so excited to announce early access to the next generation of our Python and Ruby helper libraries. These next-gen libraries are built on a new API auto-generation tool, homegrown within Twilio, designed to make all future helper libraries a whole lot better.

As Twilio has grown, introducing new products like Video, IP Messaging and SIP Trunking, our API surface has grown as well. Twilio’s first helper libraries were written in only a few languages, and supported only 12 endpoints. We now support 8 subdomains and over 200 endpoints in 7 languages: C#, Java, Python, PHP, Ruby, Node.js and Salesforce. Manually generating all of these libraries at the level of excellence developers have come to expect from us has become a daunting challenge.

We found that the best way to address this challenge was to introduce a healthy dose of automation into the process. We were inspired by projects like boto3, raml-codegen and swagger, which auto-generate API code and documentation. All of these projects have shown that generating helper libraries off of declarative definitions can be effective and maintainable.

Following their lead, our in-house solution allows us to almost completely auto-generate our helper libraries. We’ve kept in a few layers of manual configuration and optimization. This approach allows us to continually improve our helper libraries at scale while maintaining the human-friendly, “hand-crafted” feel that developers come to know and love as a distinctive mark of Twilio’s helper libraries.

Advantages Of Automation

In a nutshell, automating our library generation allows us to improve the developer experience and increase the pace at which we iterate. It means we can bring you new features and languages much faster than when all of our helper libraries were manually generated. Additionally, we’ve taken the opportunity of re-architecting our libraries to make several enhancements including:

  •   Optimized network efficiency (Make fewest number of REST calls necessary)
  •   Configurable page sizes for memory efficiency on constrained devices
  •   More consistent feature coverage across different languages
  •   Language idiomatic ways of expressing constructs (So C# feels like C#)
  •   Streaming Auto-Paging (my personal favorite new feature)
  •   Proper type serialization/de-serialization
  •   BYOHC (Bring Your Own HTTP Client)

With every engineering decision there are always tradeoffs. In order to gain these advantages we’ve had to trade backwards compatibility and simplicity in our community contribution model. Today, with this early release, we’re simply providing a summary of what to expect when our new auto-generated libraries become official later this year. Over the coming weeks you can expect more detailed blogs posts on specific features and how to contribute updates to the new auto-generated libraries. We’ll also soon be publishing release candidates for Node.js and PHP, followed by C# and Java.

We Need You

To get started visit the installation and migration guides linked below. We’d love to hear your thoughts on the release candidates of our Python and Ruby libraries. You can get support and provide feedback by opening an issue on Github and tagging the issue with “release-candidate”. Our engineering team monitors the issue and is looking forward to hearing from you.

Ruby

Python

Introducing Twilio’s Next Generation Helper Libraries

iOS and Web Browser Video Calls with Python and Swift

$
0
0

Twilio Video makes it easy for you connect the people you care about via video on the devices they already own by coding with the programming language you already know. In this post, we’ll use the JavaScript and iOS SDKs to do the heavy lifting so that you can quickly add video to your applications. Follow along and you’ll be up and running with video calls between web browsers and iOS mobile apps.

Tools We’ll Need

Python and Swift will serve as the two main programming languages we’ll use to get our web and mobile apps running. We’re going to set up the Python application first so let’s take a look at those resources we will need along the way:

The other half of our project will run on iOS, so we will also need the following Swift resources in this post:

There is a GitHub repository named video-calls-python-swift with the completed project code in case you want to try the end result before going through the rest of the tutorial.

Creating Our Twilio Video Credentials

There are several Twilio credentials that we need to create to run our application. These four environment variables are named TWILIO_ACCOUNT_SID, TWILIO_API_KEY, TWILIO_API_SECRET and TWILIO_CONFIGURATION_SID in the Python quickstart. Here is what each of those variables represents and where to grab them from your Twilio account:

  • TWILIO_ACCOUNT_SID: Your primary Twilio account identifier found on the account dashboard
  • TWILIO_API_KEY: An API account identifier for authenticating between servers, generated along with the API Secret here in the console
  • TWILIO_API_SECRET: A shared secret key used to authenticate between servers, generated with the API key
  • TWILIO_CONFIGURATION_SID: Set of configuration values for webhooks and other options for Video, generated in the console.

Grab the TWILIO_ACCOUNT_SID from the Video dashboard by clicking the “Show API Credentials” link.

account_sid.png

After grabbing the Account SID, click the “Configuration Profiles” link next to “Getting Started”. You’ll come to a page like the following screenshot.

configuration-profiles.png

Click the “Create a Configuration Profile” button to get to the next screen, shown below.

new-config-profile.png

Enter a name for the configuration profile, like “My Video App Config” and scroll down to the bottom of the page. Check “Enable Network Traversal” to ensure devices on a non-accessible IP address behind Network Address Translation are still reachable.

enable-stun-save.png

Click the “Save” button then copy the new Configuration Profile SID shown at the top of the page.

We just need to create the API key and secret then we can get our app up and running. In the navigation bar, click “Dev Tools”, then “API Keys”.

create-api-key.png

Press the “Create an API Key” button to continue. Enter a name for your new API key. Typically, the name should remind you of which application is granted permission to your account.

new-api-key.png

Press the “Create API Key” button and snag the API Key and API Secret from the screen that appears. Make sure to keep the API Secret somewhere safe as it will cannot be shown via the dashboard again.

Now that we have the four credentials in hand, we need to set them in the terminal before we run our Python app. Use the export command to set the environment variables as shown below. Make sure to use your credentials instead of the placeholders for the actual values.

export TWILIO_ACCOUNT_SID='ACxxxxxxxxxxxxxxxxxxxx'
export TWILIO_API_KEY='yyyyyyyyyyyyyyyyyyyyyyyyy'
export TWILIO_API_SECRET='zzzzzzzzzzzzzzzzzzzzzzz'
export TWILIO_CONFIGURATION_SID='aaaaaaaaaaaaaaaaaa'

The credentials for our account are now exposed as environment variables for applications we run on the command line. Let’s move ahead to the Python code that will use those credentials to run our video application.

Web Browser Video Calling with Python

Now that our environment variables are set, it’s time to get started with the browser-based version of our video call application. First we’ll need a Python virtualenv to store our application dependencies.

Virtualenv is a dependency isolation library that makes it much easier to switch between Python versions and various code packages you’re using on different projects. Create a virtualenv in a location you’ll remember using the following command. In my case, I have a folder called Envs under my home directory that keeps my virtualenv organized.

virtualenv ~/Envs/videocalls
source ~/Envs/videocalls/bin/activate

Now we have our virtualenv activated and should see our command prompt show the name of our virtualenv, such as (videocalls)$. With the virtualenv in place and activated, we can use pip to install our dependencies.
Head to the JavaScript Video quickstart page and click the “Download for Python” button. Extract the files from the quickstart and change into that directory from the commandline.

The Python code library dependencies for this quickstart are listed in the requirements.txt file. Those dependencies are:

Flask==0.10.1
twilio==6.2.dev0
fake-factory==0.5.3

Install the dependencies into our virtualenv with the following pip command:

pip install -r requirements.txt

Open the app.py file in the Python quickstart project. Let’s break down what’s in the source code file and why we need it to run our application.

import os
from flask import Flask, jsonify, request
from faker import Factory
from twilio.access_token import AccessToken, ConversationsGrant

The first four lines in app.py are imports. The os module is used to grab environment variables that we just set. flask is a Python web micro framework that allows us to serve a web application. In this application, faker is used to generate random usernames. The twilio library is used to generate temporary access tokens based on our credentials specified in the environment variables.

app = Flask(__name__)
fake = Factory.create()

@app.route('/')
def index():
    return app.send_static_file('index.html')

After the import statements, we instantiate the Flask application and create a variable to hold the Faker library’s factory. We then define a route for the root URL to serve up a webpage for our application.

@app.route('/token')
def token():
    # get credentials for environment variables
    account_sid = os.environ['TWILIO_ACCOUNT_SID']
    api_key = os.environ['TWILIO_API_KEY']
    api_secret = os.environ['TWILIO_API_SECRET']
    
    # Create an Access Token
    token = AccessToken(account_sid, api_key, api_secret)

    # Set the Identity of this token
    token.identity = fake.user_name()
    
    # Grant access to Conversations
    grant = ConversationsGrant()
    grant.configuration_profile_sid = os.environ['TWILIO_CONFIGURATION_SID']
    token.add_grant(grant)

    # Return token info as JSON
    return jsonify(identity=token.identity, token=token.to_jwt())

In the above code we define another route that can be accessed at the /token URL. This token function generates a temporary JSON web token (JWT) as an access token that can be passed to the client. The client will then have access to our Twilio account only for the grants that were added to the token in this function.

if __name__ == '__main__':
    app.run(debug=True)

Finally, we have a simple conditional that runs the app when this file is directly run through the python command.

Now that we understand what the code is doing, it is time to start up the server by running app.py with Python.

python app.py

When the server starts, pull up http://localhost:5000 in a browser to try the application.
running-localhost-5000.png
This is the simple user interface for our browser-based video application. You can click the “Preview My Camera” button then give the browser permission to access your camera to try it out.

However, we really want to connect two users in two separate windows together. Pull up http://localhost:5000/ in two browsers, such as Chrome and Firefox. You’ll be able to use the name shown in one browser window to create a video call to the other browser window.

two-browsers-localhost-5000.png

Alright, we just made a video call between two browsers!

Connecting people together using video in a browser is interesting, but the real excitement starts when you mix devices, say a browser and an iPhone. Let’s move on to the iOS app to see how we can make cross-device video calling work.

iOS Mobile App Video Calling with Swift

It’s time to set up the other side of our video application that will run on iOS. Go to the iOS video quickstart page and download the Swift version of the quickstart.

Twilio uses CocoaPods to distribute the dependencies necessary to work with video on iOS. We need to install the necessary pods to properly run the iOS app. However, we need to make sure we have the latest pods which may not be in the quickstart files. Open Podfile within the Swift quickstart and ensure the file matches the following lines.

source 'https://github.com/CocoaPods/Specs.git'
pod 'TwilioConversationsClient', :podspec => 'https://media.twiliocdn.com/sdk/ios/conversations/latest/TwilioConversationsClient.podspec'
pod 'TwilioCommon', :podspec => 'https://media.twiliocdn.com/sdk/ios/common/latest/TwilioCommon.podspec'

Head back to the terminal and run the following commands to install the pods and then open the VideoQuickStart app once.

cd video-quickstart-swift-master/
pod install
open VideoQuickStart.xcworkspace

The open command brings up Xcode with our Swift video quickstart project along with the installed pods. In the Project Navigator on the left side, open VideoQuickStart -> VideoQuickStart -> Source -> ViewController.swift.

swift-video-quickstart.jpg

Within ViewController.swift look for the following lines at the top of the file. Modify the tokenUrl so it matches the highlighted line below.

import UIKit

class ViewController: UIViewController {
  // MARK: View Controller Members
  
  // Configure access token manually for testing, if desired! Create one manually in the console 
  // at https://www.twilio.com/user/account/video/dev-tools/testing-tools
  var accessToken = "TWILIO_ACCESS_TOKEN"
  
  // Configure remote URL to fetch token from
  var tokenUrl = "http://localhost:5000/token"
  
  // Video SDK components
  var accessManager: TwilioAccessManager?
  var client: TwilioConversationsClient?
  var localMedia: TWCLocalMedia?

Our Python server running on localhost:5000 already generates access tokens, including ones that will allow our iOS app to use Twilio Video. With that change in place, we can run the simulator and start up our iOS app. Click the little ‘ ‘ icon in the top right corner of the iOS app and invite the randomly generated username presented in the browser application. You can now connect the two devices together.

ios-to-browser.jpg

Woohoo! Now we have the iOS Video app running, but there’s something strange going on. Why is there no video going from the iOS app to the web browser? Unfortunately, the iOS simulator is limited and does not have camera functionality. We need to test with an external device.

What happens though if we want to test our iOS app on a device that can’t connect to localhost because it’s only available in our development environment? We’ll handle that in the next section.

Browser, Meet iOS App, iOS App, Meet Browser

Our simulator and devices connected to your development computer can access the web application running on localhost port 5000, but what about when we want to test our video calling on the iOS app outside the development environment? We’ll use a localhost tunneling tool named ngrok to create a URL that can be access by anyone on the Web. Our ngrok set up is quick, but if you want to learn more about the tool check out this handy detailed ngrok post by my colleague Phil Nash.

Download the appropriate ngrok file for your operating system. In the terminal, change into the directory where ngrok is located. Start ngrok up with an HTTP (and HTTPS) connection to your Python server running locally on port 5000. Use the the following command:

./ngrok http 5000

You should see ngrok start up like in this screenshot:

ngrok.jpg

Highlight and copy the HTTPS forwarding URL, for example in the above screenshot you’d copy https://6940e7da.ngrok.io. We’re going to use that URL in our Swift application.

In Xcode, update the same line we modified earlier. This time change the tokenUrl variable to the ngrok URL by pasting in our HTTPS Forwarding URL we just copied above.

import UIKit

class ViewController: UIViewController {
  // MARK: View Controller Members
  
  // Configure access token manually for testing, if desired! Create one manually in the console 
  // at https://www.twilio.com/user/account/video/dev-tools/testing-tools
  var accessToken = "TWILIO_ACCESS_TOKEN"
  
  // Configure remote URL to fetch token from
  var tokenUrl = "https://6940e7da.ngrok.io/token"
  
  // Video SDK components
  var accessManager: TwilioAccessManager?
  var client: TwilioConversationsClient?
  var localMedia: TWCLocalMedia?

Restart your application on the iOS device. Now we can test the iOS app when it’s not connected to our development environment, as shown in the following screenshot.

ios.jpg
We can also test against the iOS simulator, knowing that the simulator won’t be able to broadcast video back. Another iOS device with a camera will be able to do video calls for a full test.

Video All the Things!
Nice work completing this tutorial! Connecting people on the devices they already use is way easier now that you know how to Twilio Video.

Our quickstart web and mobile apps are just a taste of what you can build with the Video SDKs. What else do you want to create with it? Drop a comment below or contact contact me via:

iOS and Web Browser Video Calls with Python and Swift

Tutorials from Twilio: Live Coding on Twitch This Week

$
0
0

The launch celebration of Tutorials from Twilio continues as the Twilio developer evangelists take you through building their favorites live on Twitch. Join us all week long as we use these amazing new tutorials to equip you with the knowledge you need to add chat to an existing iOS mobile app, implement two-factor authentication in a Rails application, make phone calls from a browser using Python and Django and much more.

Find the schedule of the weeks streams below and we hope you join Team Twilio on Twitch.

Tuesday, March 22nd, 9:30am – 11:30am EDT
Greg Baugues – @gregbaugues
Adding Two Factor Authentication to a Laravel App with Authy

 

Tuesday, March 22nd, 10:30am – 12:30pm EDT (Archived video here)
Phil Nash – @phil_nash
Two Factor Authentication (2FA) in Ruby on Rails Apps

 

Wednesday, March 23rd, 9am-12pm EDT
Brent Schooley – @BrentSchooley
Building a chat app with Swift using Twilio IP Messaging

Wednesday, March 23rd, 11:30am-2pm EDT
Sam Agnew – @sagnewshreds
Web Browser Phone Calls with Python and Django

Thursday, March 24th, 2:30pm – 5:00pm EDT
Eddie Zaneski – @eddiezane
Building an Interactive Answering Machine with JavaScript and node.js

Friday, March 25th, 4:30pm – 6:30pm EDT
Matt Makai – @mattmakai
Masking Phone Numbers and Calls with Python

Tutorials from Twilio: Live Coding on Twitch This Week

Engineering The Simple Postcard

$
0
0

Jason Strauss’ debugging took a week. His input was familiar — code. The output was uncharted analog territory – a postcard. Unlike your 500 error, a postcard takes a week to get back to you. Jason was debugging in week-long chunks.

After a few weekends of work, discovering the perfect NLP library for him, and gluing together three APIs, The Simple Postcard was born. Send a photo you want made into a postcard, enter an address, and pay all via text.

It’s all text-based So don’t ask Jason if he’ll build an app. He’ll answer your question with another, “Why should people have to install something?”

Home Is Where Your Localhost Is, Or Wherever You Can Send Postcards

When Jason and his girlfriend were long distance they sent letters to each other. Having a tactile message as opposed to a digital one was alluring. But, the “user interface” for sending letters, The United States Postal Service, does not offer the most enjoyable experience.

The Simple Postcard was a product of equal parts intrigue and necessity. In building the service, Jason learned how to use a few new tools. The stack behind The Simple Postcard is Django, PostgreSQL, VQL, Lob, Stripe, Twilio, USAddress, and Bootstrap.

Unraveling Strings

A key component of The Simple Postcard’s stack is a Python library called US Address Parser that uses NLP to convert the raw address strings to the components of the address. This separates a block of text into city, street, zipcode etc. Then Jason’s app passes that info to Lob, a print API, who takes care of the postcard manufacturing. The app uses the Twilio MMS API to store and pass that image that’s printed on the card.

Here’s the code that TSP executes when someone sends in a photo to start a new postcard.

# Sample code from TheSimplePostcard.com
# TechStack: Django, PostgreSQL, VQL, Twilio, Lob, Stripe, Bootstrap, usaddress

# If theres an image, then start a new postcard
if num_media > 0:
  img = params.get("MediaUrl"+str(int(num_media)-1)) # get the last picture

  new_postcard = Postcard(
    from_number = from_number,
    raw_img_src = img,
    postcard_status = 'image_added'
  )
  new_postcard.save()

  # and ask them for the address
  response_text = 'Got it! Who would you like to send that to, name and address? (eg. %s)' % RandomSampleAddress()
  return RespondToText(response_text)

And when Jason is feeling gracious, this is the code he uses to send a random card for free.

# Sample code from TheSimplePostcard.com
# TechStack: Django, PostgreSQL, VQL, Twilio, Lob, Stripe, Bootstrap, usaddress

# Occasionally, we choose to ship a card for free
import datetime, random
def FreeCard():
  die_1 = random.randint(1, 6)
  die_2 = random.randint(1, 6)
  current_month = datetime.datetime.now().month
  return (die_1 + die_2) == current_month

 

Getting A Reply

Jason’s already gotten some great feedback after hitting the top spot on HackerNews this week. “TSP received really great feedback on HN. I’ve also received really nice feedback from mothers and girlfriends, like ‘literally made my whole day,’ which I rarely hear about my code.”

So now when the postcard you made, ordered, and paid for via text arrives at your door, you might think a week turnaround time is pretty fast after all.

Check out Jason’s full-time project GetVQL, right here. Grab his code on GitHub here

Engineering The Simple Postcard

Bulk Delete your Twilio Recordings with Python

$
0
0

We’ve all heard it. “This call may be recorded for quality assurance purposes.” These recordings are used by Customer Service Managers to improve quality of service and coach agents they care for. If you’ve built a Twilio-powered contact center, chances are you are recording these calls. Your service works great, business is booming, and one day you look at your Twilio account and you see this:

That's a lot of recording minutes!
It turns out you’ve been recording every single call over months of service and you haven’t cleaned it up! To optimize your operations and lower cost, you want to delete the ones you don’t need. In this blog post we’ll use Twilio’s Recordings API and some of Python’s built-in library to delete your recordings quickly and efficiently.

What We’ll Need

Let’s walk through how to create a recording deletion script with Python. You need the following things to create this script:

Now that we know our dependencies, let’s get our coding environment established.

Python Environment Setup

Setup Step 1: Check the Python installation

If you’re on Mac OS X, you probably have Python 2.x installed on your system. You will need to get Python 3.x installed. The easiest way to do it is to install the Homebrew package manager for Mac OS X and once you have it installed, run brew install python3 to get it on your system. You can then use python3 in your terminal to run Python 3.x, and python to run Python 2.x for your other projects.

For Windows, make sure you download and install the Python.exe installation package. My recommendation is to use Python 3.5 unless you have a pressing reason to use an earlier version, because the Python community is now migrating en masse to Python 3.

Setup Step 2: Install the Twilio Helper library

pip
  handles Python library installations and a simple
pip install twilio
  in your terminal will install the latest version of the Twilio Helper library. If you are running parallel versions of Python on your system,
pip install
 
 targets your 2.x installation, while
pip3 install
 
 targets your 3.x installation, so use
pip3
 
 accordingly.

Setup Step 3: Set environment variables

How to set environment variables depends on your operating system. Here are some handy guides if you’re on Windows, Mac OS X or Ubuntu Linux that’ll help with the specific steps.

Note that if you don’t set these environment variables now there are spots within the application code to set them. However, in Python it’s a good practice to use environment variables instead of hardcoding these sensitive values.

There are only two variables that need to be set:

  • TWILIO_ACCOUNT_SID
      – found on your Twilio account dashboard
  • TWILIO_AUTH_TOKEN
      – also found on your Twilio account dashboard

On Mac OS X and Linux, you can set environment variables with the export shell command. For example, you’d run export twice to set the following variables:

  • export TWILIO_ACCOUNT_SID="ACxxxxxxxxxxxxxxxxxxxxxx"
  • export TWILIO_AUTH_TOKEN="yyyyyyyyyyyyyyyyyyyyyyyyyyy"

Alternatively, you can go directly into your .bash_profile and edit it from there as described here.

Now that we’re ready, let’s cover the ways we can delete recordings.

Recording.. Begone!

Let’s say we want to keep recordings made after April 1st, 2016 and we want to delete everything before that. So we first get a list of all recordings created before April 1st, 2016 and then extract the RecordingSID from the object and tell Twilio to delete it. To kick things off, open a new file titled

delete_recordings.py
 . You can copy and paste the code below into the file you’ve just created. Where specified, we will be rewriting specific lines and the line numbers will tell you what to replace.

from twilio.rest import TwilioRestClient
from datetime import date
import os
 
# Your Account Sid and Auth Token from twilio.com/user/account
account_sid = os.environ["TWILIO_ACCOUNT_SID"]
auth_token = os.environ["TWILIO_AUTH_TOKEN"]
client = TwilioRestClient(account_sid, auth_token)
 
# A list of recording objects with the properties described above
recordings = client.recordings.list(before=date(2016, 4, 1))

You can then tell Twilio to delete the recordings in the list and print to the console.

for recording in recordings:
    client.recordings.delete(recording.sid)
    print("Deleted Recording:", recording.sid)

At this point

python delete_recordings.py
  will run the script, and you will slowly see your recordings dissappear! This is a great first solution, but it has a couple of problems. First, the
list()
 
 method only pulls 50 results at a time, which means that we’ve only really deleted 50 recordings, not very useful if you have 100,000! You can increase this to the maximum of 1000 by adding the attribute
page_size
 
 so you have this:

recordings = client.recordings.list(before=date(2016, 4, 1), page_size = 1000)

1000 times over and over

So you now have a script that you can run continuously and delete 1,000 recordings at a time. However, if you have hundreds of thousands of recordings, this can still take a while. The major bottleneck here is that you have run the list query again or deal with paging through the results. An alternative is to use Python’s

iter()
  method instead of the
list()
 
 method, which creates a generator (i.e. data stream) that you can act on while it is still being created. You should delete line 11 and modify the line 12 as shown below.

recordings = client.recordings.list(before=date(2016, 4, 1), page_size=1000) # Delete this line
for recording in client.recordings.iter(before=date(2016, 4, 1), page_size=1000):
    client.recordings.delete(recording.sid)

This eliminates the pain of getting huge lists recordings that need deletion, but we’re still bottlenecked by one more thing. When we do our HTTP Delete request to the Twilio API, we’re waiting for a response back from Twilio that it was successful before the code moves on to the next item on the list. This can average a 200ms round trip time. When added over the many recordings it can amount to hours! We must find a way around this.

Enter: Multi-Threading

We can mitigate the request-response round trip time by spinning up multiple threads, each one talking to Twilio independently. Python’s built-in libraries will make our lives simpler here, as there is a multi-threading library that we can leverage. We can create a centralized queue that we fill with Recordings to delete, and create threads (workers) that will drain the queue. While we’re at it, we might as well write our results to a CSV so we have a record of what we did. So let’s start again from scratch. First we import all the modules we need and get our

TwilioRestClient
  instance running:

from twilio.rest import TwilioRestClient
import csv
import threading
from queue import Queue
from datetime import date
import os

# Ensure your environmental variables have these configured
acct = os.environ["TWILIO_ACCOUNT_SID"]
auth = os.environ["TWILIO_AUTH_TOKEN"]

# Initialize Twilio Client
client = TwilioRestClient(acct, auth)

We now need to create a lock that will allow only one thread to access the console output at a time so we can make sense of any print statements we include. We also create the worker, which will perform our tasks for us by draining from the queue.

# Create a lock to serialize console output
lock = threading.Lock()


# The work method includes a print statement to indicate progress
def do_work(recording_sid):
    client.recordings.delete(recording_sid)
    # Make sure the whole print completes or
    # threads can mix up output in one line.
    with lock:
        print(threading.current_thread().name, "has deleted", recording_sid)


# The worker thread pulls an item from the queue and processes it
def worker():
    while True:
        item = que.get()
        do_work(item)
        que.task_done()


# Create the queue and thread pool.
# The range value controls the number of threads you run.
que = Queue()
for idx in range(20):
    thread = threading.Thread(target=worker)
    # thread dies when main thread (only non-daemon thread) exits.
    thread.daemon = True
    thread.start()

Now that we have everything prepared, this next section is where the magic happens. We open up a .csv file to record our results, spin up our generator, and start placing recordings for deletion into our queue.

# Open up a CSV file to dump the results of deleted recordings into
with open('recordings.csv', 'w') as csvfile:
    record_writer = csv.writer(csvfile, delimiter=',')
    # Let's create the header row
    record_writer.writerow(["Recording SID", "Duration", "Date", "Call SID"])
    # You can use a date filter if needed. e.g. before=date(2016, 4, 18)
    for recording in client.recordings.iter(before=date(2016, 4, 1)):
        record_writer.writerow([recording.sid, recording.duration,
                                recording.date_updated, recording.call_sid])
        que.put(recording.sid)
    que.join()  # block until all tasks are done

print("All done!")

In this implementation you have at least a 20x improvement over the original example, all with Python’s “batteries included” approach. A final word of caution would be that since this technique deletes records incredibly quickly, you want to be very careful with your query and not accidentally delete recordings you want to keep.

Some free DLC

We can take this one last step further. Maybe you need to hold on to recordings for seven years because of compliance reasons but would rather have them in cold storage on magnetic tape somewhere. We can easily modify the deletion function to include a download component as well. First, we need to get the Python requests package with a

pip install requests
 . This makes life easier for handling HTTP requests and we will also need the BasicAuth package it provides. Just plop the two lines below after where you already have all your imports.

import requests
from requests.auth import HTTPBasicAuth

We first have to put the whole recording object in the queue, instead of just the sid, by changing what we put into the queue:

que.put(recording)

Our do_work method above is hanged to download the recording object it was just given and then delete it on the Twilio side. Lines 28 to 32 above should be replaced with the following lines below:

def do_work(recording):
    data = requests.get(recording.uri, auth=HTTPBasicAuth(acct, auth),
                        stream=True)
    # Create a .wav file and stream the recording to improve performance.
    with open(recording.sid + '.wav', 'wb') as fd:
        for chunk in data.iter_content(1):
            fd.write(chunk)
    client.recordings.delete(recording.sid)
    # Make sure the whole print completes or threads
    # can mix up output in one line.
    with lock:
        print(threading.current_thread().name,
              "has downloaded to the local folder and "
              "has been deleted off Twilio", recording_sid)

Next Steps

In this post, you’ve learned how to delete your recordings en masse and also download them if you wanted to. By using Python’s built-in libraries, we’ve created a multi-threaded system to interact with Twilio’s API. Some next steps for this project could include modifying the script where you can toggle downloading on/off, or even create an interface for a system admin to use. You could also use multi-threading in other Twilio projects you have, like sending text messages or pulling log data.

And there you have it. Feel free to reach out if you have any questions and we can’t wait to see what you build next!

  • Email: ptan@twilio.com

Bulk Delete your Twilio Recordings with Python

How to Build an SMS Slack Bot in Python

$
0
0

Bots can be a super useful bridge between Slack channels and external applications. Let’s code a simple Slack bot as a Python application that combines the Slack API with the Twilio SMS API so a user can send and receive Slack messages via SMS.

Tools We Need

Our bot, which we’ll call “twiliobot”, requires Python, Slack and Twilio APIs and libraries. To write and run our Python code we need:

Here’s a handy step-by-step guide to setting up Python, pip, virtualenv and Flask.

The Slack dependencies are:

Our Twilio requirements include:

Make sure Python version 2 or 3 is installed now. We will configure everything else throughout the remainder of this tutorial.
You can follow along by writing the code in this post or skip ahead to the finished project by cloning the companion GitHub repository.

Setting Our Environment

Now that we know what tools we need to use, go to the terminal (or Command Prompt on Windows) and change into a directory where you want to store this project. Within that directory, create a new virtualenv to isolate our application dependencies from other Python projects you’re working on.

virtualenv twiliobot

Activate the virtualenv:

source twiliobot/bin/activate

Depending on how your virtualenv and shell are set up, your prompt should look like this.

twiliobot-activate.png

We’ll use the official slackclient API helper library built by Slack to access their API to send and receive messages from a Slack channel. Install the slackclient and Twilio helper libraries along with Flask into your virtualenv with the pip command:

pip install slackclient twilio flask

We next need to obtain an access token for our Slack team and our Twilio API credentials.

Slack Web API

Slack provides programmatic access to their chat application through a web API. Open up the landing page for the Slack Web API and sign up to create a Slack team or sign into your existing account.  You can create a new team for free if you don’t have admin privileges on an existing team.
slack-api-sign-in.png

After you have signed in scroll down on the web API page to where you see a button to generate test tokens.
generate-test-tokens.png

Generate a test token for a Slack team on which you have administrative privileges.

We need that test token so our Python code is authorized to call the Slack API. A common practice for Python developers is to export secret tokens like our Slack token as environment variables. Export the token with the name SLACK_TOKEN:

export SLACK_TOKEN='your slack token pasted here'

Switch into our Python environment set up so we can try out the API. With your virtualenv still active, fire up the Python REPL with the python command:

(twiliobot)$ python
Python 3.5.0 (v3.5.0:374f501f4567, Sep 12 2015, 11:00:19)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>

Let’s ensure our API token is working with a test – type the following code at the REPL prompt.

import os
from slackclient import SlackClient
slack_client = SlackClient(os.environ.get('SLACK_TOKEN', None))
slack_client.api_call("api.test")

The REPL should return back something like the following dictionary if your API test with the token was successful:

{u'args': {u'token': u'xoxp-361113305843-7621238052-8691112296227-d0d4824abe'}, u'ok': True}

If you get back {u'ok': False, u'error': u'invalid_auth'} then double check that you copied the Slack token correctly into the second line entered on the REPL.

Enter one more quick test for our authentication with another line of code in the REPL:

slack_client.api_call("auth.test")

You should see another dictionary like this one:

{u'user_id': u'U0S77S29J', u'url': u'https://fullstackguides.slack.com/', u'team_id': u'T0S8V1ZQA', u'user': u'matt', u'team': u'Full Stack Python, u'ok': True}

Awesome, we’re authorized to start using the Slack API through our account. We just need a Twilio account and credentials to create our bot!

Sending SMS Messages to Slack

We need access to the Twilio API to send and receive SMS messages. Sign up for a free Twilio account or log into your existing account if you already have one. When you sign up for a new account you’ll be given a Twilio phone number. If you already have an account you can use your existing Twilio phone number or upgrade your account to buy a new number. Click the phone number you want to use for the bot to configure it.

slack-sms-bot.png

Twilio needs a reachable URL to send an HTTP POST request to when an SMS is sent to our Twilio phone number. During development our application is typically running on our local server which cannot be reached from anywhere except our local machine.

To expose my local web server via an externally-accessible domain I typically use ngrok since it’s easy, free and awesome.

Download ngrok and run it with this command:

./ngrok http 5000

You will get a subdomain that forwards requests to that subdomain to your localhost server.

ngrok.png

Copy and paste the https version of the Forwarding URL plus “/twilio” into our phone number configuration under “A messages comes in” as shown here:

configure-number.png

Keep a note of your Forwarding URL, in this case https://9e6d3d38.ngrok.io as we’ll also need that for our Slack outgoing webhook in a future step.

Next, go to the Console Dashboard screen and look for your Account SID and Auth Token:

console-account-sid.png

Copy and paste the Account SID and Auth Token to export them as environment variables.

If you’re still in the Python REPL exit it with a quick CTRL-d or use the exit() command. Back on the command line, export Twilio credentials as an environment variables:

(twiliobot)$ export TWILIO_ACCOUNT_SID='your twilio account sid'
(twiliobot)$ export TWILIO_AUTH_TOKEN='your twilio auth token'
(twiliobot)$ export TWILIO_NUMBER='your twilio phone number, for example +12025551234'
(twiliobot)$ export USER_NUMBER='your phone number, for example +14151230987'

As we did earlier with the SLACK_TOKEN, we will use the newly-exported environment variables in our Python script.

Coding Our Python-Powered Bot

Dive into your favorite text editor such as Vim, Emacs or Sublime Text so we can cut some new Python code. Create a new file named twiliobot.py and start it with the following import statements.

import os
from flask import Flask, request, Response
from slackclient import SlackClient
from twilio import twiml
from twilio.rest import TwilioRestClient

The os module will be used to pull the SLACK_TOKEN, TWILIO_ACCOUNT_SID and TWILIO_AUTH_TOKEN environment variables we exported on the command line. From the Flask framework we import the standard Flask class to instantiate our app, along with the request and Response objects to obtain HTTP input and return appropriate HTTP responses.

The SlackClient import should look familiar as the same line we wrote earlier on the REPL. We’ve also got a couple of new Twilio helper library imports so we can generate TwiML and send outgoing text messages.

With our dependencies imported we can use them to grab those environment variable values and instantiate the Flask app along with Slack and Twilio clients.

TWILIO_NUMBER = os.environ.get('TWILIO_NUMBER', None)
USER_NUMBER = os.environ.get('USER_NUMBER', None)

app = Flask(__name__)
slack_client = SlackClient(os.environ.get('SLACK_TOKEN', None))
twilio_client = TwilioRestClient()

Instantiating TwilioRestClient automatically pulls the TWILIO_ACCOUNT_SID and TWILIO_AUTH_TOKEN from environment variables with those exact names.

Add a function named twilio_post to the Python file to handle an incoming HTTP POST request. This POST request will come from Twilio

@app.route('/twilio', methods=['POST'])
def twilio_post():
    response = twiml.Response()
    if request.form['From'] == USER_NUMBER:
        message = request.form['Body']
        slack_client.api_call("chat.postMessage", channel="#general",
                              text=message, username='twiliobot',
                              icon_emoji=':robot_face:')
    return Response(response.toxml(), mimetype="text/xml"), 200

Add a convenience function so we can test whether app works properly and a main function to start the Flask development server when we invoke the script with the python command.

if __name__ == '__main__':
    app.run(debug=True)

Nice! Fire up the Flask app to test it out. Start the Flask app with the `python twiliobot.py` command. We'll see debugging output that indicates the development server is running.

* Running on http://127.0.0.1:5000/ (Press CTRL C to quit)
* Restarting with stat
* Debugger is active!
* Debugger pin code: 144-609-426

Time to give our app a try. Send a text message to your Twilio phone number. The SMS will go to Twilio, Twilio will send an HTTP POST request to the ngrok URL you configured in the phone number configuration screen. Then ngrok will forward the POST request to your local machine, which will hit your running twiliobot.py Flask development server. Our Flask web app then uses slackclient to post a message to our Flask channel as twiliobot, just like we see here:

twiliobot.png

Cool, we can send messages to Slack! Next let’s get important messages out of Slack and sent to our phone via SMS.

Receiving Slack Messages via SMS

Our bot can take SMS messages coming to our Twilio phone number and post them as messages to a Slack channel. However, what if we want to see when someone “@” mentions “twiliobot” in a channel? We can set up an outgoing Slack webhook that’ll alert our Python application via an HTTP POST request.

Stop the Flask development server with “Ctrl-C” and modify the existing twiliobot.py file. Add the new highlighted line and the highlighted function shown below.

import os
from flask import Flask, request, Response
from slackclient import SlackClient
from twilio import twiml
from twilio.rest import TwilioRestClient

SLACK_WEBHOOK_SECRET = os.environ.get('SLACK_WEBHOOK_SECRET', None)
TWILIO_NUMBER = os.environ.get('TWILIO_NUMBER', None)
USER_NUMBER = os.environ.get('USER_NUMBER', None)

app = Flask(__name__)
slack_client = SlackClient(os.environ.get('SLACK_TOKEN', None))
twilio_client = TwilioRestClient()


@app.route('/twilio', methods=['POST'])
def twilio_post():
    response = twiml.Response()
    if request.form['From'] == USER_NUMBER:
        message = request.form['Body']
        slack_client.api_call("chat.postMessage", channel="#general",
                              text=message, username='twiliobot',
                              icon_emoji=':robot_face:')
    return Response(response.toxml(), mimetype="text/xml"), 200


@app.route('/slack', methods=['POST'])
def slack_post():
    if request.form['token'] == SLACK_WEBHOOK_SECRET:
        channel = request.form['channel_name']
        username = request.form['user_name']
        text = request.form['text']
        response_message = username   " in "   channel   " says: "   text
        twilio_client.messages.create(to=USER_NUMBER, from_=TWILIO_NUMBER,
                                      body=response_message)
    return Response(), 200


@app.route('/', methods=['GET'])
def test():
   return Response('It works!')


if __name__ == '__main__':
    app.run(debug=True)

Go to the Slack Outgoing Webhooks page then click the “outgoing webhook integration” link as shown below.

Scroll down to the Integration Settings section. Select “#general” as the channel to listen on. Enter “@twiliobot” within the “Trigger Word(s)” value. Copy your ngrok Forwarding URL plus “/slack” into the URL(s) text box.

slack-webhook-secret.png

Copy the generated Token in the field below the URL(s) section. Scroll down and press the “Save Settings” button. In case you are wondering, yes, I regenerated my own token after this screenshot. So, no, you can’t use it.

Export the Slack token as a new environment variable:

(twiliobot)$ export SLACK_WEBHOOK_SECRET = 'generated outgoing webhook token here'

Restart your Flask server because it’s time to test out receiving messages via SMS!

Go to your Slack #general channel. You should see that the outgoing webhook integration has been added to the channel.

outgoing-to-twilio.png

Send a message mentioning twiliobot like “@twiliobot hola!” and hit enter. Check your phone:

hola.png

Sweet! That’s pretty simple output but we have a basic way to receive messages from one or more channels and can add whatever Python code we want to handle the input. This is a great hook for expanding our bot or sending messages to another service for processing.

Wrapping it up

Woohoo, all done! Well actually, there’s a whole lot more you can do with the Slack and Twilio APIs. Here are several more ideas to try out now that you’ve got the basics down:

  1. Add natural language processing (NLP) to the Python Flask web app
  2. Try a different Slack client or ditch the helper library entirely and use the Requests library to implement retry logic
  3. Write and customize a more complicated Slack bot

If you want to learn even more about the Slack API and bots, come out to SIGNAL in San Francisco on May 24th and 25th where I’ll be giving a talk called “R2-D2 or Skynet? Combining Slack Bots with the Twilio API” with one of Slack’s developers, Don Goodman-Wilson. You can use the promo code makai20 when you register for 20% off the ticket price.

Questions? Drop a comment below or contact me on these channels:

Twitter: @mattmakai
GitHub: makaimc
Email: makai@twilio.com
Twitch (live coding along with other Twilio Developer Evangelists): Team Twilio

How to Build an SMS Slack Bot in Python

How to Add Phone Calling Bots to Slack with Python

$
0
0

Slack is awesome for text and emoji-based conversations with colleagues. However, sometimes it’s far easier to quickly answer a question over the phone. While Slack is just starting to add voice calling between users, there is no way to patch someone in by their good old telephone number. Let’s add phone calls to Slack by creating a bot with Python, Twilio and the Slack Real Time Messaging API.

Tools We Need

Our bot, which we’ll name callbot, requires a few libraries and APIs. To build our bot we need:

Here’s a handy step-by-step guide to setting up Python, pip and virtualenv.

Our Twilio requirements are:

The Slack dependencies are:

Ensure that Python version 2 or 3 is installed. We’ll configure everything else throughout the remainder of this tutorial.

You can follow along by writing the code in this post or skip ahead to the finished project by cloning the companion GitHub repository.

Configuring Our Environment

Now that we know what tools we need, go to the terminal (or Command Prompt on Windows) and change into a directory where you want to store this project. Within that directory, create a new virtualenv to isolate our application dependencies from other Python projects you’re working on.

virtualenv callbot

Activate the virtualenv:

source callbot/bin/activate

Depending on how your virtualenv and shell are set up, your prompt should look like something like this screenshot.

virtualenv.png

We’ll use the official slackclient API helper library to access their API to send and receive messages from a Slack channel. Install the slackclient and Twilio helper libraries along with phonenumbers into your virtualenv with the pip command:

pip install slackclient twilio phonenumbers

We next need to obtain an access token for the Slack Bot and our Twilio API credentials.

Slack Real Time Messaging API

Slack provides programmatic access to their chat application through a web API. Open up the landing page for the Slack Web API and sign up to create a Slack team or sign into your existing account. You can create a new team for free if you don’t have admin privileges on an existing team.
slack-api-sign-in.png

After you have signed in go to the Bot Users page.
custom-bot-users.jpg

Give your bot the name “callbot” and click the “Add bot integration” button.
callbot.png

The page will reload and you’ll see a new generated access token. You can also change the logo to a custom design, like I did with this bot by giving it the Twilio logo.

slack-token.png

Scroll down and click the “Save Integration” button. Your bot is now ready to access the Slack API.

A common practice for Python developers is to export secret tokens like our Slack token as environment variables. Export the token with the name SLACK_BOT_TOKEN:

export SLACK_BOT_TOKEN='your slack token pasted here'

Awesome. We’re authorized to use the Slack API as a bot. Now we just need a Twilio account and credentials to start handling phone calls.

Twilio Phone Numbers

We need access to the Twilio API to make phone calls from our application. Sign up for a free Twilio account or log into your existing account if you already have one. Our Slack bot will only dial outbound phone calls. Therefore, nothing needs to change on the number configuration screen.

twilio-phone-number.png

With our phone number in hand, go to the Console Dashboard screen and look for your Twilio Account SID and Auth Token:

console-account-sid.png

As we did earlier with the SLACK_BOT_TOKEN, we will use the newly-exported environment variables in our Python script.

On the command line, export Twilio credentials as an environment variables:

(callbot)$ export TWILIO_ACCOUNT_SID='your twilio account sid'
(callbot)$ export TWILIO_AUTH_TOKEN='your twilio auth token'
(callbot)$ export TWILIO_NUMBER='your twilio phone number, for example  12025551234'

There is one more bit of information we need: our bot’s ID in Slack. Next we’ll write a short script to snag that from the Slack API.

Obtaining Our Bot’s ID

Time to write some Python code! We’re going to get warmed up by writing a quick helper Python script to get callbot’s ID because it varies based on the Slack team. We need the callbot ID because it will allow our application code to determine if messages parsed from the Slack Real Time Messaging API are directed at our bot.

This script will also help test that our SLACK_BOT_TOKEN environment variable is set properly. Create a new file named get_bot_id.py with the following code.

import os
from slackclient import SlackClient


BOT_NAME = 'callbot'

slack_client = SlackClient(os.environ.get('SLACK_BOT_TOKEN'))


if __name__ == "__main__":
    api_call = slack_client.api_call("users.list")
    if api_call.get('ok'):
        # retrieve all users so we can find our bot
        users = api_call.get('members')
        for user in users:
            if 'name' in user and user.get('name') == BOT_NAME:
                print("Bot ID for '" + user['name'] + "' is " + user.get('id'))
    else:
        print("could not find bot user with the name " + BOT_NAME)

The above code imports the SlackClient and instantiates it with our SLACK_BOT_TOKEN. When the script is executed by the python command we hit the API for a list of Slack users and get the ID for the one that matches the name callbot.

We only need to run this script once to obtain our bot’s ID.

python get_bot_id.py

When we run the script, we’ll get a single line of output with our Bot’s ID.
get-callbot-id.png

Copy the ID and export it as an environment variable named BOT_ID.

(callbot)$ export BOT_ID='bot id returned by script'

Again, the script only needs to be run once to make sure we have the appropriate bot ID for our Slack team. Now we’re ready to code up our Python application that’ll run our callbot.

Coding Our CallBot

We have all the appropriate environment variables set for our Python code to appropriately use the Twilio and Slack APIs. Create a new file named callbot.py and add the following imports.

import os
import phonenumbers
import time
import uuid
from slackclient import SlackClient
from twilio.rest import TwilioRestClient

The os and SlackClient imports should look familiar because we used them earlier in the get_bot_id.py script.

With our dependencies imported we can use them to grab those environment variable values and instantiate the Slack and Twilio clients.

# environment variables
BOT_ID = os.environ.get("BOT_ID")
TWILIO_NUMBER = os.environ.get("TWILIO_NUMBER")

# constants
AT_BOT = "<@" + BOT_ID + ">:"
CALL_COMMAND = "call"
TWIMLET = "https://twimlets.com/echo?Twiml=%3CResponse%3E%0A%20%20%3CDial%3E%3CConference%3E{{name}}%3C%2FConference%3E%3C%2FDial%3E%0A%3C%2FResponse%3E&"

# instantiate Slack & Twilio clients
slack_client = SlackClient(os.environ.get('SLACK_BOT_TOKEN'))
twilio_client = TwilioRestClient()

Our code instantiates the SlackClient with our SLACK_BOT_TOKEN from an environment variable. TwilioRestClient automatically pulls the TWILIO_ACCOUNT_SID and TWILIO_AUTH_TOKEN from environment variables with those exact names during its declaration. Continue the Python script with the following lines of code that will handle starting the bot.

if __name__ == "__main__":
    READ_WEBSOCKET_DELAY = 1 # 1 second delay between reading from firehose
    if slack_client.rtm_connect():
        print("CallBot connected and running!")
        while True:
            command, channel = parse_slack_output(slack_client.rtm_read())
            if command and channel:
                handle_command(command, channel)
            time.sleep(READ_WEBSOCKET_DELAY)
    else:
        print("Connection failed. Invalid Slack token or bot ID?")

The SlackClient connects to the Slack Real Time Messaging API WebSocket connection then continuously loops and parses messages from the Messaging firehose. If any of those messages are directed at our bot, a function named handle_command will determine what to do with the command.

Above the Python code we just wrote, add two new functions to parse Slack output and handle commands.

def handle_command(command, channel):
    """
        Receives commands directed at the bot and determines if they
        are valid commands. If so, then acts on the commands. If not,
        returns back what it needs for clarification.
    """
    response = "Not sure what you mean. Use the *" + \ 
               CALL_COMMAND + "* command with numbers, delimited by spaces."
    if command.startswith(CALL_COMMAND):
        response = "calling stub..."
    slack_client.api_call("chat.postMessage", channel=channel,
                          text=response, as_user=True)


def parse_slack_output(slack_rtm_output):
    """
        The Slack Real Time Messaging API is a firehose of data, so
        this parsing function returns None unless a message is
        directed at the Bot, based on its ID.
    """
    output_list = slack_rtm_output
    if output_list and len(output_list) > 0:
        for output in output_list:
            if output and 'text' in output and AT_BOT in output['text']:
                # return text after the @ mention, whitespace removed
                return output['text'].split(AT_BOT)[1].strip(), 
                       output['channel']
    return None, None

The parse_slack_output function takes messages from Slack and determines if they are directed at our Slack CallBot. If a message starts with a direct message to our bot ID, then we know our bot needs to handle a command. handle_command function is currently a stub function that either passes back a generic help message or has a stub within a condition if the command starts with “call”.

With most of our code in place, let’s test our CallBot by using the python callbot.py command.
callbot-running.png
Go into the Slack channel with CallBot and enter “@callbot: call 14045551234 14155550909” (or replace these two numbers with your own test phone numbers). CallBot will answer back but not really dial numbers.

Our CallBot can respond to commands but it doesn’t place calls yet. We can fix that by adding two new functions named call_command and validate_phone_numbers. handle_command can then invoke call_command instead of just serving as a stub. Change your code to match the entire callbot.py application below. Code changes from our previous version are highlighted.

import os
import phonenumbers
import time
import uuid
from slackclient import SlackClient
from twilio.rest import TwilioRestClient


# environment variables
BOT_ID = os.environ.get("BOT_ID")
TWILIO_NUMBER = os.environ.get("TWILIO_NUMBER")

# constants
AT_BOT = "<@" + BOT_ID + ">:"
CALL_COMMAND = "call"
TWIMLET = "https://twimlets.com/echo?Twiml=%3CResponse%3E%0A%20%20%3CDial%3E%3CConference%3E{{name}}%3C%2FConference%3E%3C%2FDial%3E%0A%3C%2FResponse%3E&"

# instantiate Slack & Twilio clients
slack_client = SlackClient(os.environ.get('SLACK_BOT_TOKEN'))
twilio_client = TwilioRestClient()


def handle_command(command, channel):
    """
        Receives commands directed at the bot and determines if they
        are valid commands. If so, then acts on the commands. If not,
        returns back what it needs for clarification.
    """
    response = "Not sure what you mean. Use the *" + \
               CALL_COMMAND + "* command with numbers, delimited by spaces."
    if command.startswith(CALL_COMMAND):
        response = call_command(command[len(CALL_COMMAND):].strip())
    slack_client.api_call("chat.postMessage", channel=channel,
                          text=response, as_user=True)


def call_command(phone_numbers_list_as_string):
    """
        Validates a string of phone numbers, delimited by spaces, then
        dials everyone into a single call if they are all valid.
    """
    # generate random ID for this conference call
    conference_name = str(uuid.uuid4())
    # split phone numbers by spaces
    phone_numbers = phone_numbers_list_as_string.split(" ")
    # make sure at least 2 phone numbers are specified
    if len(phone_numbers) > 1:
        # check that phone numbers are in a valid format
        are_numbers_valid, response = validate_phone_numbers(phone_numbers)
        if are_numbers_valid:
            # all phone numbers are valid, so dial them together
            for phone_number in phone_numbers:
                twilio_client.calls.create(to=phone_number,
                                           from_=TWILIO_NUMBER,
                                           url=TWIMLET.replace('{{name}}',
                                           conference_name))
            response = "calling: " + phone_numbers_list_as_string
    else:
        response = "the *call* command requires at least 2 phone numbers"
    return response


def validate_phone_numbers(phone_numbers):
    """
        Uses the python-phonenumbers library to make sure each phone number
        is in a valid format.
    """
    invalid_response = " is not a valid phone number format. Please " + \
                       "correct the number and retry. No calls have yet " + \   
                       "been dialed."
    for phone_number in phone_numbers:
        try:
            validate_phone_number = phonenumbers.parse(phone_number)
            if not phonenumbers.is_valid_number(validate_phone_number):
                return False, phone_number   invalid_response
        except:
            return False, phone_number   invalid_response
    return True, None


def parse_slack_output(slack_rtm_output):
    """
        The Slack Real Time Messaging API is a firehose of data, so
        this parsing function returns None unless a message is
        directed at the Bot, based on its ID.
    """
    output_list = slack_rtm_output
    if output_list and len(output_list) > 0:
        for output in output_list:
            if output and 'text' in output and AT_BOT in output['text']:
                # return text after the @ mention, whitespace removed
                return output['text'].split(AT_BOT)[1].strip(), 
                       output['channel']
    return None, None


if __name__ == "__main__":
    READ_WEBSOCKET_DELAY = 1 # 1 second delay between reading from firehose
    if slack_client.rtm_connect():
        print("CallBot connected and running!")
        while True:
            command, channel = parse_slack_output(slack_client.rtm_read())
            if command and channel:
                handle_command(command, channel)
            time.sleep(READ_WEBSOCKET_DELAY)
    else:
        print("Connection failed. Invalid Slack token or bot ID?")

The above two new functions, call_command and validate_phone_numbers, do the bulk of the work for CallBot. validate_phone_numbers uses the phonenumbers Python library to ensure each phone number is parsable and conforms to at least one phone number type from around the world. call_command ensures that at least two phone numbers are specified and calls validate_phone_numbers for some additional checks. If every phone number is valid then call_command invokes the Twilio Voice API to place each outbound phone call.

Time to run our bot now that all of our code in place. On the command line, execute python callbot.py.
callbot-running.png

In Slack, start giving CallBot commands. You can start testing it with invalid phone numbers. If we specify an invalid phone number format for one or more of the numbers, we’ll get back a helpful error message.
not-valid-format.png

Now try with two legitimate phone numbers.
calling-success.png
Wait a second for the incoming call…
success.png
Now we’re on a conference call with one or more people we’ve dialed through Slack. Time to hash out those questions over the phone so we can get back to coding.

Wrapping Up

Woohoo, our new callbot is all done! Actually, there so much more that can be done with the Slack and Twilio APIs. Check out these posts to learn even more about what you can do Here are several more ideas to try out now that you’ve got the basics down:

  1. Implement a persistent backend like PostgreSQL and use it to store a phone number for each username
  2. Add SMS capability to the bot
  3. Use Twilio Lookup to determine if a number is truly valid instead of just parseable
  4. Boost the parser with better parsing and natural language processing so it’s more natural for Slack users to interact with

Questions? Drop a comment below or contact me on these channels:

Twitter: @mattmakai
GitHub: makaimc
Email: makai@twilio.com
Twitch (Python & Swift live coding):  Team Twilio

How to Add Phone Calling Bots to Slack with Python


Building A Dad Joke SMS Slack Bot In Python

$
0
0

Did you hear the preamble joke the guy told before showing you how to build a SMS Dadjoke Slackbot in Python?

A phone is lost in the jungle, and gets tangled in a thicket of vines. It sees a python and says “Hey can you cut me some Slack?”

[I’ll pause while you mentally prepare yourself for an onslaught of Dad jokes]

Our Dadbot will text you terribly awesome jokes when you @ mention it in its Slack channel. Dadbot will also drop a quality Dad joke in a Slack channel when you text a Twilio number. This is the power of the Slack API combined with the Twilio SMS API — Dad jokes abound.

What We’ll Need

My dad doesn’t go anywhere without cargo pants, extra napkins stuffed in his pocket, and a map. We will also need a few precious items before heading out to build Dadbot.

Twilio Developer Evangelist and lover of all things Python, Matt Makai has a step-by-step guide on setting up your virtual environment right here on his blog.

For the Slack side of things you’ll need the following:

For all things Twilio, you’ll need these two things:

 

Setting The Stage For Dad Jokes: Your Development Environment

Using Terminal, create a proverbial cargo pant pocket in which you’ll store your Dadbot project. Once you create that directory, create a new virtualenv to separate our application’s dependencies from any other projects you’ve got in the works.

virtualenv dadbot

Activate that environment with the following command.

Source dadbot/bin/activate

Awesome. You should see something like this.
VirtualEnvActivated

It’s time to get the slackclient API helper library installed so we can send and recieve messages from our #dadbot Slack channel. Here’s the pip command you run to install the slackclient, Twilio helper libraries, and Flask.

pip install slackclient twilio flask

Next, we’ll need to get an access token for our Slack team as well as our Twilio API credentials. Get ready to export some tokens.

Cutting Dad Jokes Some Slack: Using The Slack Web API

What’s the good of telling a great Dad joke if there’s no one around to hear it? Slack’s chat app will serve as our vessel for Dad jokes. They offer programmatic access to their chat app through a simple to use WebAPI.

Click here to sign up for a Slack account, create a team, or sign in to your trusty old Slack account. Once you’re all set up, scroll down the API page to Authentication and click “Generate Test Tokens.”

Pro Tip – You’ll need administrative privileges in the Slack team you generate test tokens for.
SlackGenerateTokens

We’ll have to check that our Python code has the green light to call the Slack API. To test that it’s authorized, we need to export our secret tokens (from both Slack and Twilio) as enviornment variables. We’ll export the token with the name

SLACK_TOKEN

export SLACK_TOKEN='your slack token pasted here'

Sending Texts To Your Dadbot

If you don’t have a Twilio account, now would be a good time to sign up for a free account. We need to access the Twilio API to send and receive texts. If you’ve already got a Twilio account, go ahead and sign in. You’ll need to purchase a number you’ll use to send text to and from Dadbot. If you have an existing number you want to use, that works as well.

Buyanumber
Twilio is going to ask for a URL it can hit to send an HTTP POST request once an SMS reaches our Dadbot Twilio number. We have to solve what I call the “Zoolander Problem — our code is only accessible to our local machine. To provide Twilio a URL and solve the Zoolander Problem, we’ll use ngrok to expose our local web server through an accessible domain.

After you download ngrok, get it up and running with this command in terminal.

./ngrok http 5000

You should see something like this.
ngrok
Grab the forwarding URL, go to your Twilio Console, and paste the forwarding url plus “/twilio” in the “A message comes in” field. Keep that Forwarding URL handy because we’ll need it to handle some Slack details shortly.
dropngrokinforwarding

Next, we’re going to export our Twilio Account SID and Auth Token as environment variables. Head into your Twilio Console and copy down these two variables.
accountsidauthtoken

Make sure you’re not still in the Python REPL. Enter CTRL + d if you still are. Once you’re out of REPL, use the following command to export the variables.

import os
from flask import Flask, request, Response
from slackclient import SlackClient
from twilio import twiml
from twilio.rest import TwilioRestClient

Remember all those environment variables we exported earlier? The os module will pull the

TWILIO_ACCOUNT_SID
,
TWILIO_AUTH_TOKEN
and other variables we exported from the command line. The Flask class will instantiate our app, while request Response will give us the HTTP inputs and responses we need.

Let’s grab those environment variables and instantiate the Flask app alongside the Slack and Twilio clients.

@app.route('/twilio', methods=['POST'])
def twilio_post():
    response = twiml.Response()
    if request.form['From'] == USER_NUMBER:
        message = request.form['Body']
        slack_client.api_call("chat.postMessage", channel="#dadbot",
                              text=message, username=dadbot,
                              icon_emoji=':robot_face:')
    return Response(response.toxml(), mimetype="text/xml"), 200

We’ll be using the

python
command to test that our Dadbot is running smoothly, and to start a Flask server. To make this possible, add the following function:
* Running on http://127.0.0.1:5000/ (Press CTRL C to quit)
* Restarting with stat
* Debugger is active!
* Debugger pin code: 144-609-426

It’s that time. Text your Twilio phone number. You should see that your message has posted in the Slack Channel like so.
dadbotsayshi

That simple “hai” is a confirmation that your SMS has hit Twilio, triggered an HTTP Post request to your ngrok URL, ngrok forwarded the POST request to your local machine, and hit the dadbot.py file that’s running on your Flask server. This is the journey of a Dad joke.

Importing Your Favorite Jokes, Exporting Random Dad Jokes

No one wants to hear the same joke over and over again. Use the import random module to allow our app to make a random Dad joke selection from our list of Dad jokes that we’ll contain in myList. We need to edit some code so Twilio knows to print the selected random Dad joke in Slack.

Go back into your Dadbot.py file and add the following lines of code.

# -*- coding: utf-8 -*-

import os
from flask import Flask, request, Response
from slackclient import SlackClient
from twilio import twiml
from twilio.rest import TwilioRestClient

SLACK_WEBHOOK_SECRET = os.environ.get('SLACK_WEBHOOK_SECRET', None)
TWILIO_NUMBER = os.environ.get('TWILIO_NUMBER', None)
USER_NUMBER = os.environ.get('USER_NUMBER', None)
 
app = Flask(__name__)
slack_client = SlackClient(os.environ.get('SLACK_TOKEN', None))
twilio_client = TwilioRestClient()

import random
import random
myList = [“Your dad jokes here”, “even more dad jokes,”] 
random.choice(myList)


@app.route('/twilio', methods=['POST'])
def twilio_post():
    response = twiml.Response()
    if request.form['From'] == USER_NUMBER:
        message = random.choice(myList)
        print message
        slack_client.api_call("chat.postMessage", channel="#dadbot",
                              text=message, username='dadbot',
                              icon_emoji=':robot_face:')
    return Response(response.toxml(), mimetype="text/xml"), 200

We’re using the random.choice module to select a joke from

myList
and telling Twilio to print that joke in our Slack channel.

Save the

dadbot.py
file and try texting your Twilio number. It should print a random joke in your Slack channel.

Dadbotmakesajoke

My Dad thinks slack refers to rope and types emails in blue comic sans. It’s safe to say he’s not logging into Slack on a regular basis. Let’s open up the enjoyment of Dad jokes to him by triggering texts when you @mention dadbot in a Slack channel.

Receiving Slack Messages via SMS

In order to trigger a text when someone summons the dadbot, we need to use Slack’s outgoing webhooks. The outgoing webhook will alert our Python app through an HTTP POST Request.

CTRL+C that Flask server you’ve got up and running. We’re going to modify our dadbot.py file by adding the following highlighted bits of code.

# -*- coding: utf-8 -*-

import os
from flask import Flask, request, Response
from slackclient import SlackClient
from twilio import twiml
from twilio.rest import TwilioRestClient
 
SLACK_WEBHOOK_SECRET = os.environ.get('SLACK_WEBHOOK_SECRET', None)
TWILIO_NUMBER = os.environ.get('TWILIO_NUMBER', None)
USER_NUMBER = os.environ.get('USER_NUMBER', None)
 
app = Flask(__name__)
slack_client = SlackClient(os.environ.get('SLACK_TOKEN', None))
twilio_client = TwilioRestClient()
 
@app.route('/twilio', methods=['POST'])
def twilio_post():
    response = twiml.Response()
    if request.form['From'] == USER_NUMBER:
        message = random.choice(myList)
        print message
        slack_client.api_call("chat.postMessage", channel="#dadbot",
                              text=message, username='dadbot',
                              icon_emoji=':robot_face:')
    return Response(response.toxml(), mimetype="text/xml"), 200

@app.route('/slack', methods=['POST'])
def slack_post():
    if request.form['token'] == SLACK_WEBHOOK_SECRET:
        channel = request.form['channel_name']
        username = request.form['user_name']
        response_message = username + " in " +  channel + " says: " + random.choice(myList)
        twilio_client.messages.create(to=USER_NUMBER, from_=TWILIO_NUMBER,
                                      body=response_message)
    return Response(), 200

@app.route('/', methods=['GET'])
def test():
   return Response('It works!')
 
 
if __name__ == '__main__':
    app.run(debug=True)

Notice that random.choice(myList) addition in the response_message? We’ll be using that function again to trigger random Dad jokes via SMS.

Go to the Slack Outgoing Webhooks page then click the “outgoing webhook integration” link as shown below.
outgoingwebhooksclick

Head down to the Integration Settings section. Click “#dadbot” as the channel to listen on. Type “@dadbot” in the “Trigger Word(s)” value. Copy and paste your ngrok Forwarding URL plus “/slack” into the URL(s) text box.

generatedtoken
After you copy your Slack token, click “Save Settings”. I regenerated the token in the picture so it won’t work if you accidentally copy it. Export your Slack token as an environment variable using this command.

(dadbot)$ export SLACK_WEBHOOK_SECRET = 'generated outgoing webhook token here'

Restart your Flask server and prepare for awful jokes. We’re going to test out receiving messages from Slack via SMS. Head on over to the #dadbot channel you created. You should see the prompt that an outgoing webhook integration has been added to the channel.
outgoingwebhookadded

Try @mentioning dadbot. The message has to start with that @mention. My message to dadbot won’t work because I pre-empted it with “what’s up”. Try saying “@dadbot tell me a joke!” You should get a joke sent to your phone.
thedadjoke

Awesome! Our Dadbot lives and is telling a whole host of random, terrible jokes. Let us know what Dadjokes you equip your Dadbot with. My personal favorite is “What’s a vampire’s favorite fruit? A necktarine.” Yep. It’s awful. Don’t blame me, blame my Dad. It’s his joke. If you’ve lost your appetite for adding more jokes to your app, here are a few things you can do to expand on your Slackbot.

Happy Father’s Day everybody!

Building A Dad Joke SMS Slack Bot In Python

Check Stock Prices with Python and Twilio SMS

$
0
0

In the movie Wall Street, one dimension of the main character Gordon Gecko that the film disappointingly fails to explore is his love of the outdoors.  That’s right, he loves to go camping.  Unexpected, right?

Gordon’s never going to get a data connection out there, but with a bit of Python and Twilio SMS he can still check his stock prices with only a few of those precious reception bars.  Let’s find out how.

Setup and Build

For this simple app all you need is a Twilio account and a Python development environment.  If you are new to Python it’s easy to set up sign up for a free trial.

Start by opening a terminal or command line and create a new virtual environment named ‘twilio-ticker’:

virtualenv twilio-ticker

Activate the virtualenv:

source twilio-ticker/bin/activate

The dependencies we need are requests, an HTTP client, Flask, a Python microframework and the Twilio helper library:

pip install requests flask twilio

Awesome, let’s dig into some code. Create a new file named app.py, open it in your favorite code editor and import the dependencies we just installed:

import requests

from flask import Flask, request
from twilio import twiml

Now add the code needed to create and run a new Flask app:

import requests

from flask import Flask, request
from twilio import twiml

app = Flask(__name__)

if __name__ == "__main__":
    app.debug = True
    app.run()

Next define a new route that Twilio can request via an HTTP POST when it receives an inbound text message.  That route creates a response that containing the TwiML <Message> verb which tells Twilio to send an SMS message back to the sender.

app = Flask(__name__)


@app.route('/sms', methods=['POST'])
def sms():
    response = twiml.Response()
    response.message("Hello World")
    return str(response)

if __name__ == "__main__":
    app.debug = True
    app.run()

This is a good place to make sure the basics of our app are set up and run.  Start the app by running:

python app.py

The app will start and expose the sms route on http://localhost:5000.

flask-running.png

Test the route using an HTTP client like cURL or Postman to make a request to the sms route. The response should contain the generated TwiML.

<?xml version="1.0" encoding="UTF-8"?>
<Response>
    <Message>Hello World</Message>
</Response>

High five! With the route generating TwiML let’s modify it to let someone request the the price of a specific listing by texting in a stock symbol.

Start by grabbing the text of the incoming SMS message which Twilio includes its HTTP request as a form-encoded value named Body.  Using that symbol craft a URL for the Yahoo Finance API that we’ll request to the to get information about the listing:

@app.route('/sms', methods=['POST'])
def sms():
    symbol = request.values.get('Body')
    path = "http://finance.yahoo.com/webservice/v1/symbols/{0}/quote?format=json"
    path = path.format(symbol)

    response = twiml.Response()
    response.message("Hello World")
    return str(response)

Using requests make an HTTP GET request to the Yahoo URL.  From the response parse the price and replace the original TwiML message with a new message that contains the stock symbol and price:

@app.route('/sms', methods=['POST'])
def sms():
    symbol = request.values.get('Body')
    path = "http://finance.yahoo.com/webservice/v1/symbols/{0}/quote?format=json"
    path = path.format(symbol)
 
    response = twiml.Response()
    try:
        result = requests.get(path)
        price = result.json()['list']['resources'][0]['resource']['fields']['price']
        response.message("Current price of {0} is: {1}".format(symbol, price))
    except:
        response.message("Whoopsie doodle - could not find the information.")
  return str(response)

Restart the app and head back to your HTTP client to make a request to the sms route again this time including the form-encoded Body parameter.

<Response>
    <Message>Current price of GOOG is: 695.94</Message>
</Response>

With the app running the final step is to connect a Twilio phone number to it.  Twilio uses an HTTP request to a public URL to tell us that it has received an incoming SMS message.  We can expose the Python app running in own local environment via a public URL by using ngrok.

ngrok http 5000

Once ngrok starts grab the public URL that it assigned:

ngrok.png

Next, head over to the Phone Numbers section of the Twilio Console to select an existing number in your account buy a new number.

Configure the phone number so that when Twilio receives an incoming SMS message Twilio will make an HTTP, or “webhook”, request to your ngrok URL:

Save the phone number and give your app a try by texting your favorite stock symbol to your Twilio phone number.

Boom!  Quick stock symbol info on your phone via single simple SMS message.

Wrapup

By combining a few bits of Python and Twilio, in just a few minutes we were able to build a really fun and useful SMS app.  But the fun does not have to stop there.

The app could be made even more useful by allowing the user to request prices of multiple stocks or by using a task queue library like Celery to allow the user to set up SMS notifications for when a symbol hits a price threshold.

Let me know what you’re building with Python or Twilio.  Shoot me a tweet @devinrader.

Check Stock Prices with Python and Twilio SMS

SMS Sentiment Analysis in Python with Flask and the IBM Watson Twilio Add-on

$
0
0

With Twilio’s new Add-ons marketplace developers can reap the benefits of third party APIs with the flip of a switch. The IBM Watson Message Sentiment Add-on adds sentiment analysis information to every SMS request sent to your web application.

Let’s build a Flask app that will determine the sentiment of text messages sent to your Twilio number.

Getting started

Before writing any code make sure you have:

Now let’s install the necessary third party libraries.

Run the following commands in your terminal to install Flask and the Twilio Python module, preferably in a virtual environment:

pip install flask
pip install twilio

Responding to Incoming text messages

Before being able to respond to messages, you’ll need a Twilio phone number. You can buy a phone number here and configure it as seen in this GIF:

Now let’s create a web app that responds to text messages sent to this number. Open a file called app.py and create a Flask app with one route:

from flask import Flask, request
from twilio import twiml



app = Flask(__name__)


@app.route('/sms', methods=['POST'])
def sms_reply():
    message_received = request.form['Body']
    response_message = 'Your message was {}.'.format(message_received)

    response = twiml.Response()
    response.message(response_message)
    return str(response)



app.run()

When a text comes into Twilio, Twilio makes an HTTP request to your webapp and expects instructions back in the form of TwiML. The single route on this web app uses the Twilio Python library to generate and return TwiML that responds to the incoming text message with another message.

Your Flask app will need to be visible from the Internet in order for Twilio to send requests to it. We will use ngrok for this, which you’ll need to install if you don’t have it.  In your terminal run the following command:

ngrok http 5000

Screen Shot 2016-07-14 at 10.52.54 AM.png

This provides us with a publicly accessible URL to the Flask app. Configure your phone number as seen in this image:
        
Screen Shot 2016-07-14 at 10.54.53 AM.png

You are now ready to send a text message to your new Twilio number.

SMS Sentiment Analysis with IBM Watson

To add sentiment analysis to all of your incoming text messages, you just need to activate the IBM Watson Message Sentiment Add-on by clicking the “Install” button in your console.

Screen Shot 2016-07-14 at 10.57.24 AM.png

In the POST request sent to your application, there will be a new AddOns field, inside of which you can access the data from the IBM Watson API:

{ 
    "status": "REQUEST_STATUS",
        "language": "DOCUMENT_LANGUAGE",
    "docSentiment": {
        "type": "SENTIMENT_LABEL",
        "score": "DOCUMENT_SENTIMENT",
        "mixed": "SENTIMENT_MIXED"
    } 
}

This AddOns object will be JSON that you will need to parse. Open app.py again and add the following line to the top of your code:

import json

To access the sentiment of the incoming messages, you just need to add a few more lines of code. Open app.py again and rewrite your /sms route:

@app.route('/sms', methods=['POST'])
def sms_reply():
    add_ons = json.loads(request.form['AddOns'])
    if add_ons['status'] == 'successful':
        result = add_ons['results']['ibm_watson_sentiment']['result']
        sentiment = result['docSentiment']['type']
        response_message = 'Your response was {}.'.format(sentiment)
    else:
        response_message = 'An error has occured.'

    response = twiml.Response()
    response.message(response_message)
    return str(response)

Now send some happy and sad text messages to your Twilio number!

Looking Ahead

With some configuration and a few extra lines of code, you can get the sentiment of all messages sent to your Twilio phone numbers, whether you’re using it to improve customer experience or to decide which GIFs to send to your users.

U1VADVq.gif

Feel free to reach out if you have any questions or comments or just want to show off the cool stuff you’ve built.

SMS Sentiment Analysis in Python with Flask and the IBM Watson Twilio Add-on

Pokemon Faux: Create Fake Pokemon Go Screenshots with Python, Flask and Twilio MMS

$
0
0

Pokemon Go is awesome and we all want to show off when we catch rare Pokemon. Let’s build a quick hack using Python and Twilio MMS that will allow you to trick your friends into thinking that you’ve encountered legendary Pokemon.

You can continue reading to find out how to build this, or try it out now by texting an image and the name of a legendary Pokemon to:

(646) 760-3289

articuno_spotted.png

Getting started

Before diving into the code, you’ll first need to make sure you have the following:

  • Python and pip installed on your machine
  • A free Twilio account – sign up here
  • The images we will need to use including screenshots of models of the legendary Pokemon and the overlay for the Pokemon encounter screen. Create a new folder called pokemon-go-images in the directory where you want your project to live and save them there.

The dependencies we are going to use will be:

Open your terminal and enter these commands, preferably in the safety of a virtual environment:

pip install twilio==5.4.0 Flask==0.11.1 requests==2.10.0 Pillow==3.3.0

Overlaying images on top of each other

Let’s write some code to take the image we want to manipulate and overlay the Pokemon catching screen over it. We will use the Image module from PIL.

We need a function that takes a path to an image and the name of a Pokemon. Our function will resize the images to be compatible with each other, paste the overlay over the background image, paste the selected Pokemon on the image and then overwrite the original image with the new image.

Open a file called overlay.py and add the following code (comments are included in-line to explain what is happening):

from PIL import Image


def overlay(original_image_path, pokemon):

    overlay_image = Image.open('pokemon-go-images/overlay.png')

    # This is the image the user sends through text.
    background = Image.open(original_image_path)

    # Resizes the image received so that the height is always 512px.
    base_height = 512.0
    height_percent = base_height / background.size[1]
    width = int(background.size[0] * height_percent)

    background = background.resize((width, int(base_height)), Image.BILINEAR)

    # Resize the overlay.
    overlay_image = overlay_image.resize(background.size, Image.BILINEAR)

    # Specify which pokemon sprite is used.
    pokemon_img = Image.open('pokemon-go-images/{}.png'.format(pokemon))

    # Convert images to RGBA format.
    background = background.convert('RGBA')
    overlay_image = overlay_image.convert('RGBA')
    pokemon_img = pokemon_img.convert('RGBA')

    new_img = background
    new_img.paste(overlay_image, (0, 0), overlay_image)

    # Place the pokemon sprite centered on the background + overlay image.
    new_img.paste(pokemon_img,
                  (int(width / 4), int(base_height / 4)),
                  pokemon_img)

    # Save the new image.
    new_img.save(original_image_path,'PNG')

Try running it on your own image. This works best with images taken on phones, but let’s just see if it works for now. Open up your Python shell in the same directory as the file you just created and enter the following two lines:

from overlay import overlay
overlay('path/to/image', 'mewtwo')

Now open the new image and see if you are catching a Mewtwo on a train like I am:

mewtwo_spotted.png

Responding to picture text messages

We need a Twilio phone number before we can respond to messages. You can buy a Twilio phone number here.

Now that we have the image manipulation taken care of, make a Flask app that receives picture messages and responds to them with a Pokemon being captured in that picture.

Open a file called app.py in the same directory as before and add the following code:

import requests
from flask import Flask, request, send_from_directory
from twilio import twiml

from overlay import overlay

UPLOAD_FOLDER = '/Path/to/your/code/directory'
legendary_pokemon = ['articuno', 'zapdos', 'moltres', 'mewtwo', 'mew']

app = Flask(__name__)


@app.route('/sms', methods=['POST', 'GET'])
def sms():
    # Generate TwiML to respond to the message.
    response = twiml.Response()
    response.message("Please wait while we try to catch your Pokemon")

    if request.form['NumMedia'] != '0':

        # Default to Mew if no Pokemon is selected.
        if request.form['Body']:
            # Take the first word they sent, and convert it to lowercase.
            pokemon = request.form['Body'].split()[0].lower()
            if not pokemon in legendary_pokemon:
                pokemon = 'mew'
        else:
            pokemon = 'mew'

        # Save the image to a new file.
        filename = request.form['MessageSid'] + '.png'
        with open('{}/{}'.format(UPLOAD_FOLDER, filename), 'wb') as f:
           image_url = request.form['MediaUrl0']
           f.write(requests.get(image_url).content)

        # Manipulate the image.
        overlay('{}/{}'.format(UPLOAD_FOLDER, filename), pokemon)

        # Respond to the text message.
        with response.message() as message:
            message.body = "{0}".format("Congrats on the sweet catch.")
            message.media('http://{your_ngrok_url}/uploads/{}'.format(filename))
    else:
        response.message("Send me an image that you want to catch a Pokemon on!")

    return str(response)


@app.route('/uploads/', methods=['GET', 'POST'])
def uploaded_file(filename):
    return send_from_directory(UPLOAD_FOLDER, filename)

if __name__ == "__main__":
    app.run()

The /sms route responds to an incoming text message with some Twilio flavored XML called TwiML. Notice that the ‘NumMedia’ parameter is not zero, meaning we received an MMS. Twilio does not return an image itself, they return a URL to the image.

We create a string called filename using the MessageSid parameter to maintain a unique identifier for each image. Then the program opens the file to write the content of Twilio’s image to the file.

The second route, /uploads/<filename> handles the delivery of the message.media TwiML using that URL to retrieve the new image.

Your Flask app will need to be visible from the internet in order for Twilio to send requests to it. We will use ngrok for this, which you’ll need to install if you don’t have it. In your terminal run the following command:

ngrok http 5000

bLqHzdmXrzBH4pEr4ADRoWbU2Kgitkg848ZYE-aRACC0ZGMgC6a_98CmPb9VCzvFSrpbbDc35J_IdTwUShfEqx4zuzJdqthNy9En-RE3-8Ma2h3gXOzA3kErkbqrvjqBZxJg4sZH.png

This provides us with a publicly accessible URL to the Flask app. Configure your phone number as seen in this image:

022N1RISjv9KCUvlhfN4dJtBKJvzP_96q9d558H2YMmpLGOoyl9PSqap29D11yzHgHHJVCY2u6x86xQs37xvSv1ZB3t7vxZEp3rb4n58ZuvwdSkT-ydPBD8DgvCqMmknzPHF3r4w.png

Before testing this out, make sure that you’ve changed the file paths and the URL to reflect your own.

Now try texting an image and the name of a legendary Pokemon to your newly configured Twilio number. Looks like we found a Zapdos in the Twilio New York office!

zapdos_spotted.png

Time to catch ’em all!

Now that you can send messages to make it look like you are catching legendary Pokemon in any arbitrary picture, your quest to making your friends think you are a Pokemon master can truly begin.

The code for this project also lives on this GitHub repository.

For more Pokemon Go-related awesomeness, check out this post that will walk you through setting up SMS alerts when rare Pokemon are nearby.

Feel free to reach out if you have any questions or comments or just want to show off the cool stuff you’ve built.

Thanks to my good friend Shahan Akhter for helping out with image manipulation and tweaking the Python code to make the images look better.

Pokemon Faux: Create Fake Pokemon Go Screenshots with Python, Flask and Twilio MMS

Getting Started with Python, Bottle and Twilio SMS / MMS

$
0
0

Python applications can easily send and respond to text and picture messages using a web API. Outbound messages are sent through Twilio’s RESTful API, while inbound messages are received by your application when Twilio makes an HTTP POST request. We will walk through how to set up a Bottle web app to handle both SMS and MMS messages.

Tools We Need

You’ll need Python 2 or 3. Although, Python 3 is recommended by the developer community for all new applications. Install one of those two Python versions on your system. We also need the following:

All the code for this tutorial can be found in an open source GitHub repository.
If you need help configuring your development environment before we dive into writing our application, check out this handy guide on setting up Python 3, Bottle and Gunicorn on Ubuntu 16.04 LTS.

Installing Application Dependencies

Our application will use the Twilio Python helper code library to send and reply to messages. Bottle and the helper library are installable from PyPI into a virtual environment. Open your terminal and use the virtualenv command to create a new virtual environment:

virtualenv bottlemessages

Invoke the virtualenv’s activate script, which makes it the active Python installation for our shell. Note that you need to do this in every terminal window that you want this virtualenv to be used.

source bottlemessages/bin/activate

The command prompt will change after activation to the name of the virtual environment within parentheses, like this example shown here:

bottle-virtualenv.png
Use the pip command to install the Bottle and Twilio Python packages into your virtual environment.

pip install bottle twilio

We installed the required dependencies. Now the Python code that is run with the virtualenv activated will be able to use those packages. It’s time to buy a phone number that we can use to build our Bottle web app that will handle SMS and MMS messages.

Obtain a Phone Number

Head over to the Twilio website and sign up for a free account or sign into your existing Twilio account.
Twilio sign up screen.
Twilio trial accounts allow you to send and receive messages to your own phone number. Trials are great for initial development before your application goes live. Upgrade your account to to send and receive messages to and from any phone number.

If you need a new phone number or want to see the existing numbers you have, go to the manage phone numbers screen via that link or click the “#” on the left Console navigation bar.

Twilio Console left nav phone numbers screen.

With our accounts credentials and a Twilio phone number in hand, we can write some Python code to send outbound messages.

Sending SMS Messages with Bottle

The Bottle web app will have two routes. One route will allow us to test that the app is running. The other route will handle and respond to incoming HTTP POST requests from Twilio. Create a new file named app.py in your in the directory where you want to store this Python project.
Write the following code in the new app.py file.

from bottle import (post, request, response, route, run, )
from twilio.rest import TwilioRestClient

# copy in your Twilio Account SID and Auth Token from Twilio Console
client = TwilioRestClient("account sid", "auth token")

@route('/')
def check_app():
    # returns a simple string stating the app is working
    return "Bottle web app up and running!"


@route('/send-sms/<to_number>/<from_number>/<message_body>/')
def outbound_sms(to_number, from_number, message_body):
    # use the Twilio helper library to send an outbound SMS
    # via the REST API
    client.messages.create(to=to_number, from_=from_number,
                           body=message_body)
    # this response is sent back to the web browser client
    return "SMS sent to " + to_number


if __name__ == '__main__':
    # use the Bottle framework run function to start the development server
    run(host='127.0.0.1', port=5000, debug=True, reloader=True)

The lines starting with # are comments that give explanations for what the code lines below them are executing. Bottle web apps define URL routes with the @route and @post decorators, depending on the type of HTTP request the route should handle, whether a GET or POST, respectively.
We need to grab our account credentials, the Account SID and Auth Token, from the Twilio Console, to instantiate the client object.

account-sid-auth-token.png

We can start up the Bottle development server now that we have our Twilio account credentials plugged into the app.py code. Make sure your virtual environment remains activated so that the application can use the Bottle and Twilio code libraries we installed earlier. Give the Bottle app a try by running it with python app.py.
python-app-py.png
Open a web browser and go to localhost:5000 (or 127.0.0.1:5000). We should see “Bottle web app up and running!” on the screen.
bottle-app-up.png
We can test the SMS sending function via the /send-sms/ URL. For example, try this URL to send the message “About to take a drive down the I-95” from your Twilio number to another phone number (make sure to replace the “from” and “to” numbers in the URL):

http://localhost:5000/send-sms/<your phone number>/<Twilio phone number>/about to take a drive down the i-95/

A simple success message should tell us that the SMS was sent to the recipient phone number.

send-sms.png
Give it a second for the SMS to arrive and we should see something like the following if you’ve got an iPhone, or the equivalent on an Android:
sms-success.png
We’ve got a skeleton of our Bottle app running that can send outbound SMS. Now we can add some MMS picture messaging into the mix.

Sending MMS Messages

Sending MMS message is very similar to how we just sent SMS, except that we need one more argument for our create_message function. We can specify a media_url with a URL to a picture in .gif, .jpg or .png format to send that as part of our MMS. It’s also possible to send movies and sound clips, which you can read more about in the docs.

Enhance the existing app.py file with the following new highlighted lines.

from bottle import (post, request, response, route, run, )
from twilio import twiml
from twilio.rest import TwilioRestClient


# copy in your Twilio Account SID and Auth Token from Twilio Console
client = TwilioRestClient("account sid", "auth token")

# this URL variable can be dynamic or customized later
MMS_URL = "https://wiki.factorio.com/images/Fast_transport_belt_fulldensity.gif"


@route('/')
def check_app():
    # returns a simple string stating the app is working
    return "Bottle web app up and running!"


@route('/send-sms/<to_number>/<from_number>/<message_body>/')
def outbound_sms(to_number, from_number, message_body):
    # use the Twilio helper library to send an outbound SMS
    # via the REST API
    client.messages.create(to=to_number, from_=from_number,
                           body=message_body)
    # this response is sent back to the web browser client
    return "SMS sent to " + to_number


@route('/send-mms/<to_number>/<from_number>/<message_body>/')
def outbound_mms(to_number, from_number, message_body):
    # uses the Twilio helper library to send an outbound MMS
    # via the REST API
    client.messages.create(to=to_number, from_=from_number,
                           body=message_body, media_url=MMS_URL)
    return "MMS sent to " + to_number


if __name__ == '__main__':
    # use the Bottle framework run function to start the development server
    run(host='127.0.0.1', port=5000, debug=True, reloader=True)

Let’s give our app’s new MMS code a try. Try this URL in the browser, but replace the phone numbers with your Twilio number and the number you want to send the MMS to:

http://localhost:5000/send-mms/<your phone number>/<twilio phone number>/my favorite new game/

send-mms.png

Here’s what it looks like when we successfully receive the MMS:
mms-success.png

What if we want to respond to incoming SMS and MMS messages with our Bottle app? There is one issue with our web app running on our local development environment. Twilio cannot send a the HTTP POST request to the web app server. We can fix that issue during development by using a localhost tunneling tool.

Ngrok Localhost Tunneling

Ngrok is a localhost tunneling tool that bridges your local development environment to an external URL. Download and install the Ngrok version that’s appropriate for your operating system.

We can run Ngrok locally and expose our Bottle app that is running on port 5000. Run this command within the directory where the Ngrok executable is located.

./ngrok http 5000

ngrok.jpg
Cool, now we can use the Forwarding URL so Twilio can send POST requests to our application when there is an inbound SMS. Replace the URL in the text box with your own Forwarding URL, like I did in this screenshot.

ngrok-works.jpg
We can now set up Twilio to send a POST request to our application through Ngrok. Go to the manage phone numbers screen and click on the phone number you want to configure for replying to text messages.

Scroll down and look for the “Messaging” header. Change the “A Message Comes in” text box to input the ngrok Forwarding URL plus the “/twilio” route, as shown in the screenshot below.
Paste the ngrok Forwarding URL into the Twilio webhook configuration text box.
Click the “Save” button so that our changes take effect.
Update the app.py file with the following new highlighted function that will handle Twilio’s POST requests when an SMS or MMS is sent to our phone number.

from bottle import (post, request, response, route, run, )
from twilio import twiml
from twilio.rest import TwilioRestClient


# copy in your Twilio Account SID and Auth Token from Twilio Console
client = TwilioRestClient("account sid", "auth token")

# this URL variable can be dynamic or customized later
MMS_URL = "https://wiki.factorio.com/images/Fast_transport_belt_fulldensity.gif"


@route('/')
def check_app():
    # returns a simple string stating the app is working
    return "Bottle web app up and running!"


@route('/send-sms/<to_number>/<from_number>/<message_body>/')
def outbound_sms(to_number, from_number, message_body):
    # use the Twilio helper library to send an outbound SMS
    # via the REST API
    client.messages.create(to=to_number, from_=from_number,
                           body=message_body)
    # this response is sent back to the web browser client
    return "SMS sent to " + to_number


@route('/send-mms/<to_number>/<from_number>/<message_body>/')
def outbound_mms(to_number, from_number, message_body):
    # uses the Twilio helper library to send an outbound MMS
    # via the REST API
    client.messages.create(to=to_number, from_=from_number,
                           body=message_body, media_url=MMS_URL)
    return "MMS sent to " + to_number


@post('/twilio')
def inbound_sms():
    twiml_response = twiml.Response()
    # obtain message body from the request. could also get the "To" and 
    # "From" phone numbers as well from parameters with those names
    inbound_message = request.forms.get("Body")
    response_message = "I don't understand what you meant...need more code!"
    # we can use the incoming message text in a condition statement
    if inbound_message == "Hello":
        response_message = "Well, hello right back at ya!"
    twiml_response.message(response_message)
    # we return back the mimetype because Twilio needs an XML response
    response.content_type = "application/xml"
    return str(twiml_response)


if __name__ == '__main__':
    # use the Bottle framework run function to start the development server
    run(host='127.0.0.1', port=5000, debug=True, reloader=True)

Our application is ready to go – time to give our phone number a try! Send “Hello” or whatever text you want to your phone number. Here is what the result looks like on my iPhone.

sms-response.png
Nice work! This concise Bottle web app is a good start to build more complicated programs.

What’s next?

Awesome, our Bottle application can send and reply to inbound SMS and MMS messages. There’s so much more our application can do. Here are several more in-depth tutorials that’ll help you take the next step towards making your Python applications even more useful:

If you have questions or comments, please contact me via the following channels:

  • Twitter: @mattmakai
  • GitHub: mattmakai
  • Email: makai@twilio.com
  • Twitch (live coding along with other Twilio Developer Evangelists): Twilio channel

Getting Started with Python, Bottle and Twilio SMS / MMS

Viewing all 78 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>