A learning journey


No toilet paper? No problem! Creating an SMS alerting system using event-driven architecture, Azure Functions and Twilio TwiML

Some days ago I was at the airport and noticed a sign like this in the restroom mirror.

I’ve seen the sign in the past, but this time I started wondering, what do you need to build something like that?

First, it’s event-driven, because each problem is an event that need to have a response, eventually; so then it’s also asynchronous: when someone reports a problem, they won’t wait at the restroom until it’s solved. It’s a fire and forget, and maybe go to a different restroom that do have the supplies for your need.

So, how would we build something like this?

Receiving SMS

In order to receive an SMS you need, well, a number. Twilio offers the option to buy a phone number that you can use for multiple purposes. In this case, the number I selected is able to make and receive calls, faxes, text messages and multimedia messages.

A Twilio number purchased for the alerting system. It has the call and messaging capabilities required.

Now that we have a number to receive text messages, we need to do something with them.

If we click on the phone number, we get redirected to the configuration page. In the last section we can find all the details for setting up the messaging capability.

Twilio phone number configuration

If we send a message right now, we get a demo response from Twilio.

Thanks for the message. Configure your number’s SMS URL to change this message. Reply HELP for help. Reply STOP to unsubscribe. Msg&Data rates may apply.

Now we need to create a webhook, and we’ll be using Azure Functions for this.

Creating a webhook

According to Microsoft

Azure Functions is a serverless compute service that enables you to run code on-demand without having to explicitly provision or manage infrastructure. Use Azure Functions to run a script or piece of code in response to a variety of events.

Basically, you can define events to respond to, inputs to be consumed and outputs to be created for each function. In this case, we want to respond to a specific event: an incoming text message. But since Twilio expects a webhook to be called once we receive a text message, we create a new function (after creating a Function App) that listens to an HTTP Trigger, this is, when someone calls our function URL (1) the function will be executed. Each time it runs it will parse the request body (2), add the phone number where the text message comes from and the SMS body to a message queue (more on this later) and return an HTTP 200 response, which Twilio receives and passes back to the sender as an SMS automated response.

HTTP Trigger Function Code

The call we receive from Twilio sends information on x-www-form-urlencoded format. For this scenario we just need the phone number and the body, but for other type of applications we might want to take advantage of information like the city.

Twilio request body

As I mentioned before, Azure functions works with triggers, inputs and outputs. In this case we said we listen to an HTTP Trigger (our function URL) for each time a text message is received and we add the problem and phone number to a message queue. For this, an output for Azure Queue Storage is added (1); in the output configuration the queue name is specified (2).

Function “Integration” tab

Once we have the function, we set the corresponding URL in our Twilio phone configuration

New configuration for incoming text messages

This is a synchronous call, and it has a limit of 15000 ms on Twilio and about 2.5 minutes of execution time in Azure. Although it is plenty of time, we don’t want the function to have other responsibility than receiving the messages, parse them and adding them to the corresponding message queue.

Processing problems

Once we added the problems that were reported into our queue, we need to be determine what’s needed to solve them. If we go back to our example about a problem in the airport restroom, we can experience things like missing soap or toilet paper, a malfunctioning door, leaks, etc., so we first need to determine the type of problem. For this, we create a new Azure Function, this time listening to our problem queue.

A function to process the problem requests received in the queue

Every time we add a new item to the problem queue, the function will parse it to get the phone number, the room where the problem is happening and the problem description (1). Once we have the description, we analyze it to determine the type of problem (2). In this (dumb) example I just detect some key words, but you can have something like smart text analysis to actually detect the problems. I’d like to try something like Cognitive Services for this, but for now let’s pretend this is enough.

An output queue per problem type

Once we determine what type of problem needs to be solved, we add an item to the corresponding queue. The only responsibility of this step is detecting the problem type.

Solving easy problems

As you can imaging, we’ll need to create a new Azure Function per queue, where we define the specific process required to solve each kind of problem.

Refilling toilet paper or soap containers is much easier than fixing a toilet leaking, so in this case we’ll pretend that we can just trigger an alert for someone to do it.

In many cases you don’t want to go to a restroom far away from your boarding gate. So for this kind of easy problems we’d like to send a ‘courtesy’ message to the user saying the issue is now fixed.

Since doing something to solve the problem is enough for this function, we’ll add the message we want to send to a new queue. We’ll do the same for the soap problems processing function.

Sending notifications to users

As you can imagine, we’ll now create an Azure Function to process the notifications to be sent. Since the initial contact was made through a text message, we want to reply in the same way. For this function we won’t have a queue output, but a Twilio SMS one.

Creating the message is actually pretty easy. Since we are passing the phone number and the message in the payload, we just need to call CreateMessageOptions constructor, assign the message to the body and that’s it, our function will do the rest of the job.

A special scenario: Calling the plumber

As stated before, soap and toilet paper problems are easy to fix, so we want to notify our user when it’s been done. But what about a clogged toilet or a leaking? That kind of issues require a little more than just refilling something. In this case, I’d like to automatically call a plumber every time I get a report of this type.

For this, we want to create an Azure Function that listens to our plumbing queue. Since there is no output capable of making a call (yet), we don’t select any.

A function listening to a queue trigger, with no output defined.

Creating an automated phone call using Twilio service is also pretty easy.

After getting all the values we need, we just create a CallResource using Twilio SDK.

Defining the call message

Making the call is simple, but how do you actually define what’s to be said? Twilio makes this really easy too, by using TwiML.

TwiML (the Twilio Markup Language) is a set of instructions you can use to tell Twilio what to do when you receive an incoming call, SMS, or fax.

For this scenario, we can also use TwiML to define what we want to be said when we perform an automated call. Something I really loved about this is that you can even define the language and the voice of your message, all using the same Say tag (1). No need to record anything, define your own process for transforming your messages, or any other complicated stuff.

In case you already had your own voice resources, or prefer to record something that doesn’t sound like a robot, you could also play an audio file.

Wrapping up

As you can see, it is pretty easy to create a system that respond to events using Azure Functions and Twilio. You just need to write the code required for your business rules and the rest of the plumbing (pun intended) is already there for you.

Leave a Reply

Your email address will not be published. Required fields are marked *