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 Programmable Voice API that exposes functionality for playing a voice or audio.
Project Design ποΈ
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 (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.
Adding Environment Variables π€«
One of the packages we installed was dotenv
. Dotenv 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.
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
.
npm init
Then run the NPM command below to install our required packages.
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
!
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.
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 this site 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 TwiML, 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.
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 Postman to send a POST
request to localhost:3000/voice
. You should see something like this:
<?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 Ngrok, 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.
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:
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.
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.
// 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.
Scroll down and click 'Phone Numbers'.
Select your Twilio number.
Scroll down to 'Voice & Fax' and add your ngrok url that points to the /voice
endpoint as a webhook for 'A CALL COMES IN'.
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 @GregoryAGaines.
Now go create something great! If you create a Twilio app or need some help hit me up on Twitter (@GregoryAGaines) and we can talk about it.
Top comments (3)
I am the man who knocks!
Oh yes, frequently
You got options. Time to start coding!