Making and Receiving Phone Calls With Golang

October 22, 2014
Written by

maxwell_smart_8370_645x

If you read my post showing how to send SMS messages using Go you know I’ve been spending 2014 learning Go and having tons of fun doing it. My most recent project has been using Go to make and receive phone calls using Twilio. One great thing about working on this is that it required me to learn how to build a basic Go webserver. In this post I’ll show you what I’ve learned and before you know it you’ll be making and receiving phone calls from your Go apps too.

Our Tools

  • Go running on your local machine
  • A Twilio Account – Sign up for free!
  • Ngrok to expose your localhost to Twilio

A Basic Web Server

When a user makes a phone call to your Twilio phone number we ask your server for some basic instructions of what to do with that phone call. In order to do this with Go we need to set up a basic web server. Fortunately Go’s net/http library makes this really easy.

Fire up your favorite text editor and create a new file called main.go. We’ll start it off by declaring our package and importing the net/http package:

package main

import (
  "net/http"
)

Now we’ll write the code that listens for a request to a certain route and responds with some plain text:

func main() {
  http.HandleFunc("/twiml", twiml)
  http.ListenAndServe(":3000", nil)
}

func twiml(w http.ResponseWriter, r *http.Request) {
  w.Write([]byte("Hello World!"))
}

We create a new function called main. Our main function will be called when our app runs. Inside this function we call http.HandleFunc. http.HandleFunc takes two arguments – the route you want to listen for and the name of the function. If you’re wondering why I’m using the word twiml here, don’t worry it’ll make sense soon. Next we call http.ListenAndServe to start up our server. We’re passing :3000 to listen on port 3000.

Next, we have our twiml function that we said we would use to respond to requests to /twiml. In this case, we’re using the http.ResponseWriter to print out the text “Hello World!”. Let’s run our app to make sure this code works:

go run main.go

Now browse to localhost:3000/twiml and you should see “Hello World!” in your browser.

Speaking Twilio’s Language

Now that we have our basic web server in place we can start building a response that Twilio can understand. This is where TwiML comes in. TwiML is our own subset of XML that lets you provide some basic instructions for Twilio to take when a phone call or message comes in. Let’s update our code to respond to requests to /twiml with TwiML instead of plaintext.

First we need to create a new struct we’ll use to define our TwiML. We can do this right after our imports in main.go. If you haven’t used the backtick character in a long time you can find it on your keyboard next to the number 1:

type TwiML struct {
  XMLName xml.Name `xml:"Response"`

  Say    string `xml:",omitempty"`
}

TwiML contains many different verbs, but in this example we’ll be using the verb . let’s you use text-to-speech to say something to the user on your call. Inside our struct we define a string called Say. Go has some awesome tools for converting structs to XML that we’ll be taking advantage of. In our struct we define our XMLName – this will be the root tag it uses when our struct is converted to XML. We also use the xml attribute omitempty on our Say property to make sure we’re not passing along any unneeded tags in the future. In order to take advantage of the XML encoding, we need to add the encoding/xml package to our imports:

import (
  "encoding/xml"
  "net/http"
)

Now we can update our twiml function we wrote earlier to respond with XML:

func twiml(w http.ResponseWriter, r *http.Request) {
  twiml := TwiML{Say: "Hello World!"}
  x, err := xml.Marshal(twiml)
  if err != nil {
    http.Error(w, err.Error(), http.StatusInternalServerError)
    return
  }

  w.Header().Set("Content-Type", "application/xml")
  w.Write(x)
}

First we create a new TwiML struct with a Say value of “Hello World!”. Then we call xml.Marshal and pass it our struct. This will attempt to convert our struct to XML. If there’s an error with this conversion we’ll send an http.Error. Otherwise, we want to set our Content-Type header to application/xml so that Twilio knows we’re responding with XML. Finally, we’ll actually write the xml content that we generated.

Fire up your app again:

go run main.go

Browse to localhost:3000/twiml and instead of plaintext you should now see the XML response we want to give to Twilio.

For Twilio to access this URL, we need it to be exposed to the outside world. One of the best tools to make this happen is called ngrok. If you haven’t worked with ngrok before let my good friends Kevin Whinnery and Bo Jackson show you how. Make sure you register for a free account so you can use the incredibly helpful subdomain flag. Once you have ngrok setup run it with the following command:

ngrok -subdomain=asubdomainofyourchoice 3000

Now hop into your Twilio account and set up a new number. Configure the voice request URL for this number to your publicly accessible Go URL from ngrok that’s serving your TwiML:

Give that number a call and listen to the beautiful robot voice say ‘hello world’.

Making a Call Using Twilio

Now that we can receive a call let’s use the Twilio REST API to make a phone call. We need to write code that makes a POST request to the Calls resource. We’ll be using a few packages to make this happen, let’s start off by adding them to our imports at the beginning of the file:

import (
  "encoding/xml"
  "net/http"
  "net/url"
  "fmt"
  "strings"
  "io/ioutil"
  "encoding/json"
)

Now we can create a new function called call where our code that makes the POST request will live. We’ll start off with some default variables:

func call(w http.ResponseWriter, r *http.Request) {
  // Let's set some initial default variables
  accountSid := "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
  authToken := "YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY"
  urlStr := "https://api.twilio.com/2010-04-01/Accounts/" + accountSid + "/Calls.json"

}

We’re setting our Twilio accountSid and authToken – you can find these in your Twilio Account Dashboard. Then we build out the urlStr for the Calls resource, this is where we’ll be making our POST request to.

Next let’s set the data we want to use in our request:

// Build out the data for our message
v := url.Values{}
v.Set("To","+155555555555")
v.Set("From","+15555551234")
v.Set("Url","[CHANGE_TO_YOUR_NGROK_URL]/twiml")
rb := *strings.NewReader(v.Encode())

We’ll be passing 3 pieces of information:

  • To – the phone number you want to make the call to.
  • From – The phone number you want to call from. The Twilio number you set up earlier.
  • Url – A URL that returns TwiML instructing Twilio what to do once this call connects. We’ll use the same ngrok TwiML url we used for our outgoing call.

Finally, let’s use Go’s http.Client to make a request:

// Create Client
client := &http.Client{}
req, _ := http.NewRequest("POST", urlStr, &rb)
req.SetBasicAuth(accountSid, authToken)
req.Header.Add("Accept", "application/json")
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")

// make request
resp, _ := client.Do(req)
if( resp.StatusCode >= 200 && resp.StatusCode < 300 ) {
  var data map[string]interface{}
  bodyBytes, _ := ioutil.ReadAll(resp.Body)
  err := json.Unmarshal(bodyBytes, &data)
  if( err == nil ) {
    fmt.Println(data["sid"])
  }
} else {
  fmt.Println(resp.Status);
  w.Write([]byte("Go Royals!"))
}

We create a new request with a method of POST, our urlStr as the destination and the data we set earlier. We set our authentication and make sure to set our headers correctly. Once we make our request we confirm we got a 200 and if we do parse the response body using json.Unmarshal.

We’re all set to make a call to someone and play an mp3. Let’s add a new route to our web server that we’ll hit to trigger our call:

func main() {
  http.HandleFunc("/twiml", foo)
  http.HandleFunc("/call", call);
  http.ListenAndServe(":3000", nil)
}

Now browse to localhost:3000/call and wait for your phone to ring. When we triggered this request right now we hear our robotic voice again. Let’s update our code to switch the TwiML to use the verb to play an mp3. First we need to add a new Play property to our TwiML struct:

type TwiML struct {
 XMLName xml.Name `xml:"Response"`

 Say    string `xml:",omitempty"`
 Play   string `xml:",omitempty"`
}

Now that we have our Play property, we can change our twiml function to use it instead of Say:

func twiml(w http.ResponseWriter, r *http.Request) {
 twiml := TwiML{Play: "http://demo.rickyrobinett.com/huh.mp3"}

 x, err := xml.MarshalIndent(twiml, "", "  ")
 if err != nil {
   http.Error(w, err.Error(), http.StatusInternalServerError)
   return
 }
 w.Header().Set("Content-Type", "application/xml")
 w.Write(x)
}

Now browse to localhost:3000/call again. If you didn’t change out the mp3 file in my sample then you should get a glorious message from 1987. Having issues? Take a look at the final code here.

Conclusion

Now that you have the ability to make and receive phone calls from Go what will you build? Maybe something simple like a wakeup call. Or something complex like an IVR or call center. Whatever you build, I’d love to see it. You can find me hanging on twitter or hit me up on e-mail.