Playing Breaking Bad Quotes From a Phone Call Using Node.JS and Twilio ⚗️
Published: 2022-04-14 17:21:29

## Howdy Reader 👋🏽
Have you ever been in a situation where you wanted to hear quotes from your favorite show but didn't have a way to? I recently watched Breaking Bad and Walter's and Jesse's iconic voices still bounce around in my head.

Today, I'll be teaching you how to set up a number that automatically plays Breaking Bad quotes when called, or you can use it for something lame like setting up an automated support line for a product, startup, or whatever capitalistic endeavor you have in mind. 🥱

**Call this number for a Demo: _+1(318) 490-4496_**

---

## Prerequisites ✋🏽
- Basic **JavaScript** Knowledge.
- A **Twilio Account**
- A recent **Node.js** installation.

## What is Twilio? 📱
Twilio is a company that provides APIs for various communicating needs like phone calls, text messaging, or P2P video streaming.

We are using the <a href="https://www.twilio.com/docs/voice" target="_blank">Programmable Voice API</a> that exposes functionality for playing a voice or audio.

## Project Design 🏗️
<img src="https://i.imgur.com/nQiOSbK.png" />

Let's go over the design!

A user first calls our number, then Twilio calls an endpoint on our express server which tells Twilio "Hey, answer the call and return this audio". Twilio says ok, then plays the audio back to the caller.

## Project Setup
Let's start cooking our project.

### Twilio API Keys 🔑
Let's kick off by getting our Twilio API keys, we'll need them later for testing. If you don't have a Twilio account, here’s the [sign-up page](https://www.twilio.com/try-twilio) (don't worry it's not an affiliate link). Don't forget to generate a free phone number.

The console should show two tokens connected to your account info, the **Account SID** and **Auth Token**.

<img src="https://i.imgur.com/87EEqsm.png" />

### Adding Environment Variables 🤫
One of the packages we installed was `dotenv`. <a href="https://www.npmjs.com/package/dotenv" target="_blank">Dotenv</a> allows us to define a `.env` file for variables or sensitive keys (depending on who you ask) that are loaded into `process.env`.

Open the `.env` file and add your Twilio API keys and your generated phone number.

```console
TWILIO_ACCOUNT_SID=<YOU_ACCOUNT_SID>
TWILIO_AUTH_TOKEN=<YOUR_AUTH_TOKEN>
TWILIO_NUMBER=<YOUR_GENERATED_TWILIO_NUMBER> # Ex. +14045555555
```

### Creating Express.js Server
To handle Twilio asking "what should I do?", we need an Express.js server. Create an empty directory and init an empty npm project and set whatever configs you want except change "entry" to `app.js`.

```c
npm init
```

Then run the NPM command below to install our required packages.

```c
npm install express dotenv twilio ngrok
```

Create the below folder structure.

```
├── public/ - Public static files
├── routes/ - HTTP routes
    └── voice.js - Twilio API endpoints
├── .env
├── app.js - HTTP server definition
├── package.json
├── package-lock.json
```

Let's add some server code to `app.js`!

```javascript
const express = require('express');
const path = require('path');
const http = require('http');

// Pull environment variables 
require('dotenv').config();

// Init Express
const app = express();
app.use(express.json());
app.use(express.urlencoded({extended: false}));
app.use(express.static(path.join(__dirname, 'public')));

// Set port
const port = '3000';
app.set('port', port);

// Create HTTP server
const server = http.createServer(app);

// Start server on port
server.listen(port, () => {
  console.log("Started server");
});

module.exports = app;
```

Run this command to start the server.

```c
node app.js
```

## Adding Audio Files 🎤
As per our requirements, let's gather some audio files to play when someone calls our number. You already know I'm doing Breaking Bad quotes but you can use whatever you want, I'm not your boss.

> I used <a href="https://jayuzumi.com/breaking-bad" target="_blank">this site</a> to download Breaking Bad quotes.

After getting your audio files, place them in the `public` folder. If you don't want your audio files exposed publicly, you can create another folder and relocate them.

## Create Audio Response Endpoint
When someone calls our Twilio number, we direct Twilio to our `POST` endpoint that tells Twilio how to respond to the caller using <a href="https://www.twilio.com/docs/voice/twiml" target="_blank">TwiML</a>, Twilios markup language for commands.

Navigate to the `routes/voice.js` file. We are adding an `POST` endpoint named `/voice` which randomly selects a quote, then creates and returns a TwiML command telling Twilio to play this file.

```javascript
const express = require('express');
const VoiceResponse = require('twilio').twiml.VoiceResponse;
const fs = require('fs');

// Create router
const router = express.Router();

// Create a 'POST' endpoint named '/voice'
router.post('/voice', (req, res) => {

  // Get all the files in the /public folder 
  // then filter for only .mp3 files.
  const audioFiles = fs.readdirSync('./public').filter((file) => {
    return file.match(new RegExp('.*\.(mp3)', 'ig'));
  });

  // Choose a random .mp3
  const randomAudioFile = audioFiles[Math.floor(Math.random() * audioFiles.length)];

  // Create a voice response
  const voiceResponse = new VoiceResponse();

  // Add a pause because the audio starts too quickly
  // and the person calling might miss the beginning.
  voiceResponse.pause({
    length: 1,
  });

  // Generate a TwiML command that says 
  // "Play this audio file once".
  voiceResponse.play({
    loop: 1
  }, randomAudioFile);

  // Send response to Twilio
  res.type('text/xml')
    .status(200).send(voiceResponse.toString());
});

module.exports = router;
```

Now attach the route to our server by editing `app.js`.

```
const voiceRouter = require('./routes/voice');
app.use(voiceRouter);
```

> Make sure to restart your server on code changes.

## Testing '/voice' Endpoint 🔬
Before attaching our endpoint to Twilio, we'd better test it first unless you're a bad programmer then by all means skip this section. Just tell me what your product is so I know NOT to add my debit card to it 🤣.

To check if the endpoint path works, use an HTTP client like <a href="https://www.postman.com/" target="_blank">Postman</a> to send a `POST` request to `localhost:3000/voice`. You should see something like this:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<Response>
    <Pause length="1"/>
    <Play loop="1">who_knocks.mp3</Play>
</Response>
```

To test the functionality of our endpoint, we create a Twilio function to call a number using the command from the `/voice` endpoint. If your Twilio account isn't activated, the only number you can call is the one you created your account with.

Before we can do that, Twilio can't run commands that run on `localhost`. Doesn't matter if we're testing or not. Luckily, there is a tool called <a href="https://ngrok.com/" target="_blank">Ngrok</a>, a reverse proxy that exposes our `localhost:3000` express server to the internet like it's an actual server!

Navigate to the `app.js` file. In the `server.listen` command's callback, add code to connect our express server to the internet then restart the server.

```javascript
const ngrok = require('ngrok');

// Listen on port
server.listen(port, () => {
  console.log("Started server");
  
  // Create a public url for our
  // `localhost:3000` express server.
  ngrok.connect({
    addr: 3000, // Our localhost port
  }).then((ngrokUrl) => {
    console.log("Connected to url: " + ngrokUrl);
  });
});
```

You should see something like:

```c
Connected to url: https://xxxx-xx-xx-xxx-xx.ngrok.io
```

Now we create the Twilio code to test our command! Create a Twilio client using the `TWILIO_ACCOUNT_SID` and `TWILIO_AUTH_TOKEN` environment variables we added earlier.

```javascript
const twilio = require("twilio");

// Listen on port
server.listen(port, () => {
  console.log("Started server");
   
   // Create Twilio client
  const twilioClient =
     new twilio(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN);
...
```

With our Twilio client, we send a command to call a phone number using the command from the `/voice` endpoint.

```javascript
// Listen on port
server.listen(port, () => {
  console.log("Started server");

  // Create a public url for our
  // `localhost:3000` express server.
  ngrok.connect({
    addr: 3000,
  }).then((ngrokUrl) => {
    console.log("Connected to url: " + ngrokUrl);

    // Append voice endpoint to ngrokUrl
    const voiceUrl = `${ngrokUrl}/voice`;

    // Create Twilio client
    const twilioClient = new twilio(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN);

    // Call a phone number using the
    // command from the /voice endpoint.
    twilioClient.calls.create({
      to: '<TARGET_PHONE_NUMBER>', // Target number to call
      from: process.env.TWILIO_NUMBER, // Your generated Twilio number
      url: voiceUrl, // The ngrok url that points to the /voice endpoint
      method: 'POST'
    }).then((call) => {
      console.log(call); // Print call log
    }).catch((err) => {
      console.log("Error making call " + err); // Print error
    });
  });
});
```

After a few seconds, you should get a call playing the selected audio file!! Pretty cool right? 😊

## Connect /voice Endpoint to Twilio 🪛
It's time for the moment of truth! We are going to connect our `/voice` endpoint to Twilio. Login to your Twilio account and navigate to the console. Click 'Explore Products' in the left sidebar.

![Twilio Sidebar](https://i.imgur.com/pszhbhP.png)

Scroll down and click 'Phone Numbers'.

![Phone Numbers](https://i.imgur.com/TpVNwiC.png)

Select your Twilio number.

![Twilio Number](https://i.imgur.com/2ff6oUM.png)

Scroll down to 'Voice & Fax' and add your ngrok url that points to the `/voice` endpoint as a webhook for 'A CALL COMES IN'.

![Webhook](https://i.imgur.com/74oeEME.png)

Call your Twilio phone number and your audio will play right into your ear!!! 👍🏾👍🏾👍🏾

## Final Thoughts 💭
Who says T.V rots your brain? Because of Breaking Bad, we learned about system design, creating a server, how to use environment variables, the importance of testing, and most importantly how to cook... code 😉. Hopefully, you've enjoyed the read and taken something from it.

I'm Gregory Gaines, a goofy software engineer who's trying to write good articles. If you want more content, follow me on Twitter at <a href="https://twitter.com/GregoryAGaines" target="_blank">@GregoryAGaines</a>.

Now go create something great! If you create a Twilio app or need some help hit me up on Twitter (<a href="https://twitter.com/GregoryAGaines" target="_blank">@GregoryAGaines</a>) and we can talk about it.