I have a little brother. Being in my late 20s I’ve had a lot of time to perfect my harassment game. One of my favorite games I like to play with my brother is the “how much time can I make you take to open your Christmas presents” game.
This is a fun game for the whole family, except for my brother of course. The game is simple:
In my family, we all take turns opening gifts. If my brother guesses a gift out of order, he gets skipped and has to wait for another round. This results in a particularly long and hilarious Christmas morning. He always starts with vigor and gradually loses his mojo, devolving into random guessing.
The game isn’t easy. To give you an idea for what I’ve done in years past, one year I drew a different Native American tribe on each of his gifts and told him the order was the westernmost tribe to the easternmost. He was not allowed to use his phone to figure it out.
Another year I did some research on obscure dog breeds. I labeled each gift with the dog breed and told my brother the order was the dividend of the average shoulder height divided by the average weight of the male as recognized by the AKC, from least to greatest.
I decided to take it up a notch this year.
I’m a software engineer by trade, so why not use my skills to my advantage? There are lots of free tools and services out there at my disposal, so let’s tie a few of them together to make for a fun, technical Christmas morning.
After a quick brainstorming session, I landed on a plan. I was going to assign each gift a phone number, and he was going to have to call or text it to figure out if it was time to open. The gift would respond to his call/text and tell him yes, no, or some out of context nonsense. To take it up another level, I was going to make him answer trivia questions along the way.
Each phone number would be expecting the answer to a trivia question. When he got one right, he would receive the next trivia question, so on and so forth until he opens all his presents.
Thinking about my master plan from a high level, it’s nothing more than a state machine. A simple state machine that will rain the fire of frustration on my brother on Christmas morning.
End to end, there were three components:
1. Sending/receiving calls and texts
2. State machine
3. File storage (for some soundbites I wanted to playback)
From an architectural point of view, it was a pretty basic flow:
1. A call or text comes in.
2. The details from the call/text get routed to a web service that manages state.
3. The service processes the message in response to the state and returns a message, sound, or a flag to follow up to get more information.
4. The message or follow up is returned back to the user.
My decision for messaging was easy. There’s really only one answer when you’re a developer and you need to make a text or a phone call: Twilio.
Twilio has a design feature called Twilio Studio that lets you build workflows via a drag and drop user interface. It has specific routes for a call versus a text and gives you the ability to call into a web API for special logic. This is exactly what I needed.
I assembled a generic flow that passes in the contact type (call or text), phone number, and message into an API I was going to write. Based on the response, it will do one of three things:
1. Say/text a message from the response of the API
2. Play a sound
3. Ask a question and gather input from my brother
In lieu of going into specifics of the workflow, here is the JSON of the flow from Twilio Studio.
Next, I had to acquire some phone numbers. I went to Twilio and purchased eight phone numbers, one for each gift. With Twilio, phone numbers only cost $1/month per number, so I felt like I was getting a steal.
After the numbers were purchased, I had to bind them up to my Flow. So, I went to the dashboard for each phone number, told it to use my Christmas Flow, and then boom. Done.
Messaging dashboard for the phone numbers
There were a few sound bites I wanted to play back when some phone numbers were called. I decided to go with AWS and use S3 (Simple Storage Service) to host the files. For the number of files I wanted to host, it wouldn’t cost a penny.
I uploaded the sound files to S3, made them publicly accessible, and took note of the URLs.
This was easily the most complicated part of the project. I wanted something that was elegant but as small as possible. And didn’t make a fuss when I went to deploy.
OutSystems is a company focused on low-code/no-code development that manages everything for you. They take away the hassles of deployment, writing extraneous code, and manipulating HTML by providing you with a rich experience through their service studio and designer. Plus, it’s free to use if you’re doing non-production development. Win-win!
This is definitely where you could veer off the path I took and go with a full AWS solution. I did consider that initially, but I wanted to keep my development time down to a minimum, so I opted for OutSystems instead.
Below is the entity-relationship diagram for the state machine.
Call — Used for tracking/humiliating my brother. This shows me every attempt he makes at trying to figure out which present to open. It was initially used for debugging purposes but ended up with a funny use so I can tell him how many times he contacted his presents. This is entirely optional.
NumberState — The response for each phone number at any given state. As the gifts get opened, the state progresses and the phone numbers change their responses and call/text preferences. This is the real meat of the state machine.
Number — The phone number assigned to a gift.
ContactType — Hardcoded list, CALL or TEXT.
State — Represents which gift my brother was on. This Christmas he was lucky enough to get eight gifts, so there were eight states.
Progress — This is a singleton that represents the current progress through the state machine. It was responsible for looking up the appropriate NumberState given the parameters passed into the service.
There were two endpoints that I needed to make for the state machine to flow properly: Attempt and FollowUp.
Attempt was the primary entry into the system. Here is the logical flow for the endpoint.
The flow starts off with some initial lookups to get its bearings. It loads the current progress of the state machine, then grabs the appropriate NumberState given the progress, incoming phone number, and contact type.
The NumberState will then provide information like, “Am I expecting a specific value? What is the value I expect to see? Do I need to ask a follow-up question?”
If the incoming message matches the value we are looking for, we progress the state forward and update the state machine.
The questions are returned in the response that we feed to the Twilio Flow. The Flow knows how to play a sound, dictate a message, or go for a follow-up.
Speaking of follow up, let’s take a look at that logic.
As you can see, the logic is pretty similar, except here we can make some assumptions. We assume progress has already started, meaning it’s not the very first contact ever, and we know there isn’t going to be another follow-up. That’s just a rule I imposed for this game. Also, the LookupMatchingNumberState now looks for the FollowUp flag to be set on the NumberState. Other than that, pretty identical logic.
With this, we now have all the logic in place for the best Christmas gift ever. Time to assemble the NumberStates.
I made a simple web page that allowed me to add my states quickly and easily.
Stage — What gift is my brother currently opening?
Number — What number is he contacting?
Contact Type — Is this in response to a call or a text?
Message — What is the phone number going to say back?
Needs Follow-up — I need more input and call the follow-up endpoint
Is Follow-up — Denotes that this state is a response to a follow-up?
Value to match — What the incoming message is supposed to say (i.e. the answer to my trivia questions) :)
Rejection message — What the phone number says back if the incoming message isn’t the value to match
Sound URL — URL to the sound bite to play. If this is set, it trumps everything else
All I had to do to complete my game was just build an ungodly amount of these. I got to work and began piecing my wonderful Christmas game of brotherly horror. Oddly enough, this was actually the most time-consuming part of the whole process.
I am not without my faults. I know that despite how well all my test runs go, there will be an error that pops up on Christmas morning. Any developer out there will tell you that. The infamous “works on my machine” saying bites us more often than we would like to admit.
With that in mind, I also added a monitoring web page so I can control the state of the game in case something goes wrong. I can move the state back and forth just in case the app didn’t do so automatically. Plus we can get a good laugh about it after he’s done and get some replay value.
Since this whole thing is about poking fun and good humor, I added a contact tracker at the bottom of my monitor page so I can show him how many times he contacted each number, if it was a call or text, and what he said.
Numbers redacted for obvious reasons :)
At the writing of this article, Christmas still isn’t here, but I do intend on coming back and updating all of you with how it went.
This was a great learning experience for me trying to make something cheap, fast, robust, and generic. The tools I used enabled me to hit all of those criteria and come up with something unique and fun for Christmas. Let’s take a look at the total cost to host/build/test/run this thing.
All things considered, getting everything planned, built, tested, and deployed took about eight hours. Finding the right tools for the job makes a world of difference. In today’s environment where there are free tiers for pretty much every service available, all you need to do is a little exploring and you can connect the dots on your own side project quickly, efficiently, and cheap!
My brother had a wide range of emotions on Christmas day. He ranged from “your system is ridiculous. It’s all luck!” to ending up at “you really need to put this on reddit so people can see how much I suffered.”
When he was still trying to figure out my riddles and the flow of the game, he had a bad time. And honestly, figuring out the first present was all luck. He was mad because he kept getting skipped for presents.
If I had to change anything about the experience, I would probably have written better instructions on how to play. While it made sense to me, it did not make sense to my brother, who I just sprung this whole thing on.
Either way, he ended up liking the experience overall. Now I just have to figure out what to do for next year….