Send an SMS with Twilio and Golang
By: Vishal K
Tags: Go, Golang, Twilio, Telecom, User Experience

Modern internet applications use primarily standard communication protocols such as HTTP. However, the telecom space uses many protocols and makes it difficult to easily work with an external application. How does an application interact with SMS/voice messages to users on their personal phones?

Solution: Twilio

Twilio is a service that acts as a bridge between the internet and telecom systems. It creates an ordinary-looking phone number for an application. The application uses Twilio REST APIs for SMS and voice messages. The application need not bother about delivering the message to the end-user phone number. Twilio takes care of interacting with the telecom operator and requesting delivery of the message.

The developer needs to purchase a Twilio phone number. There is also a trial, a free number for development and testing. The application makes API calls using the Twilio number, the destination number, target message, and a couple of access tokens.

Golang App Server Example

The following Go code creates a web server that sends a text SMS message to a user’s phone.

package main



import (

"encoding/json"

"fmt"

"io/ioutil"

"net/http"

"net/url"

"strings"

)



var (

TwilioAccountSID = "XXX"

TwilioAuthToken = "YYY"

)



func reqToSendMessage(message string) (*twResponse, error) {

// Set initial variables

accountSid := TwilioAccountSID

authToken := TwilioAuthToken



urlStr := "https://api.twilio.com/2010-04-01/Accounts/" + accountSid + "/Messages.json"



// Build out the data for our message

to := "destination_number"

from := "twilio_number"



rb := *strings.NewReader(buildRequest(to, from, message))



// Create Client

client := &http.Client{}



req, err := http.NewRequest("POST", urlStr, &rb)

if err != nil {

return nil, err

}



req.SetBasicAuth(accountSid, authToken)

req.Header.Add("Accept", "application/json")

req.Header.Add("Content-Type", "application/x-www-form-urlencoded")



// Make request

resp, err := client.Do(req)

if err != nil {

return nil, err

}



if resp.StatusCode >= 200 && resp.StatusCode < 300 {

var data map[string]interface{}



bodyBytes, err := ioutil.ReadAll(resp.Body)

if err != nil {

return nil, err

}



err = json.Unmarshal(bodyBytes, &data)

if err != nil {

return nil, err

}



sid := fmt.Sprintf("%v", data["sid"])

message := fmt.Sprintf("%v", data["body"])



return &twResponse{SID: sid, StatusCode: resp.StatusCode, Message: message}, nil

}



fmt.Printf("status code is %d

", resp.StatusCode)



return nil, ErrInternalSystem

}



func buildRequest(to, from, message string) string {

// Build out the data for our message

v := url.Values{}

v.Set("To", to)

v.Set("From", from)

v.Set("Body", message)



return v.Encode()



}

App server

package main



import (

"errors"

"fmt"

"log"

"net/http"

)



var (

ErrInternalSystem = errors.New("internal system error")

)



func handler(w http.ResponseWriter, r *http.Request) {

if r.URL.Path != "/message" {

http.Error(w, "404 not found.", http.StatusNotFound)

return

}



if r.Method != "POST" {

w.WriteHeader(http.StatusNotFound)

fmt.Fprintf(w, "method is not supported")

return

}



if err := r.ParseForm(); err != nil {

fmt.Fprintf(w, "ParseForm() err: %v", err)

return

}



message := r.FormValue("message")



headers := w.Header()

headers.Set("Content-Type", "application/json")



// call Twilio

resp, err := reqToSendMessage(message)

if err != nil {

w.WriteHeader(http.StatusInternalServerError)

fmt.Fprintf(w, "twilio failure - %v", err)

return

}



fmt.Fprintf(w, "resp=%d message=%v", resp.StatusCode, resp.Message)

}



func main() {

http.HandleFunc("/", handler)

log.Fatal(http.ListenAndServe(":8080", nil))

}



type twResponse struct {

SID string

Message string

StatusCode int

}

References