Building A Tamagotchi With Twilio SMS

June 10, 2015
Written by

Twilio <3 Tamagotchi

Growing up in an apartment can be hard, but when you live in a big city like Sao Paulo it becomes even harder. Space is always limited and your parents are never really keen on adding anyone else unless strictly necessary. My parents weren’t different, so as soon as I mentioned I wanted a dog, they gave me a Tamagotchi instead.

I am not a parent myself but think a Tamagotchi is the dream of every parent; it sure was my parents dream. They hardly take up any space or incur any costs. Kids keep it in their pockets and in the process learn to be more responsible and take care of something. The only real mess they can make is run out of batteries.

I was about 9 when my parents gave me one. I was skeptical since all I wanted was a dog, but as soon as I pulled up the battery tab my “pet” was alive! Now I had to take care of it. I named it Tommy.

I’d spend big chunks of my day making sure Tommy was happy, fed and clean. Forget about it for a day and it would end up very upset, or worse still – it would end up dead.
If that’s not responsibility, I don’t know what responsibility is.

Fast forward to a couple of months back when I was visiting my mum and she handed me a box with some of my old belongings. This was the moment we all dread where our mums tell us to keep our crap in our own houses. Among some of my childhood memories was Tommy, which by now was out of battery.

For a moment I thought I should just go out and get myself some batteries. But then it occurred to me that Tamagotchis were all about the experience of taking care of something, and I should be able to write software that replicates this experience on my phone. That’s why today we are going to create our own SMS-powered Tamagotchi.

What you’ll need

If you don’t want to go through the entire build, feel free to checkout the code repository here.

Run it now!

Want to skip right to playing with your Tamagotchi like it was 1999? I’ve got you covered:

Deploy

If you want to understand how everything works and add your own personal touch to your Tamagotchi, follow the next step-by-step instructions.

The Embryo

We’ll begin by creating a new node application that uses Hapi as it’s application framework. You can have this application running from any directory, I have mine running from ~/Projects/JavaScript/twilio-tamagotchi.

From within that folder run the following:

$ npm init

You will then be asked several questions but at the end of the process your package.json file will have been created, which will save us from having to create it manually. You can leave most of the options as default, except for the entry point, which we will change to be app.js.

Create a new file called app.js and add the following to it.


var Hapi = require('hapi');
var util = require('util');

// Create a server with a host and port
var server = new Hapi.Server();
server.connection({
  host: 'localhost',
  port: 8000
});

// Add the route
server.route({
    method: 'GET',
    path:'/', 
    handler: function (request, reply) {
       reply('hello world');
    }
});

// Start the server
server.start(function() {

});

A simple “hello world” in Node.js and Hapi is as simple as that. We require our dependencies, create a new server and tell it where to run from, add any routes we may want, and start the server we just created.

The example above has only one route that when accessed will return “hello world” to the screen. We will modify this route later as it will be the entry point that will allow us to interact with our Tamagotchi.

Back in the terminal, run the following:

$ npm install hapi twilio --save

This will install all the dependencies we need including Hapi and the Twilio library for Node.js. Notice a new folder called node_modules was automatically created. This contains all of our dependencies.

Now run the following from terminal again:

$ node app.js

In your browser, navigate to http://localhost:8000, and you should see a “hello world” message on the screen. Congratulations, you are ready to start building your Tamagotchi.

The Egg is Laid

Now that we have our basic application up and running, we need to start writing the logic that will bring our Tamagotchi to life.

We will start off by creating a configuration file called config.js that will be used throughout this tutorial. Having that will make it easier for us to reuse values later on.


var config = {};

// HTTP Port to run our web application
config.port = process.env.PORT || 8000;

// This should be your own mobile telephone number
// You will use it to control your Tamagotchi
config.myNumber = process.env.MY_NUMBER;

// Your Twilio account SID and auth token, both found at:
// https://www.twilio.com/user/account
// A good practice is to store these string values as system environment variables, and load them from there as we are doing below. 
// Alternately, you could hard code these values here as strings.
config.twilioConfig = {
     // You can get your AccountSid and AuthToken from https://www.twilio.com/user/account/voice
    // Make sure you expand the Show API Credentials
    accountSid: process.env.TWILIO_ACCOUNT_SID,
    authToken: process.env.TWILIO_AUTH_TOKEN,
    // A Twilio number you control - choose one from:
    // https://www.twilio.com/user/account/phone-numbers/incoming
    number: process.env.TWILIO_NUMBER
  }
  // configure foods and games to be played
config.actions = {
    pet: ["hangman", "tic-tac-toe", "human knot", "london bridges", "frog races", "water balloon fights", "silly relay races"],
    food: ["apple", "cheetos", "burrito", "lettuce", "pear", "ice cream"]

  }
  // Export configuration object
module.exports = config;

Now we need a Twilio number in order to configure the number variable. To do that, go to Numbers in your Twilio account dashboard and buy one in your country of choice if you don’t already have one.

Copy that number and add it to the TWILIO_NUMBER environment variable, or directly into your config file if you prefer.


Under SMS & MMS we will change the Request URL to point to your local environment. Because Twilio needs access to it, we will use ngrok to make our local environment accessible externally. My colleague Kevin Whinnery wrote a great blog post on getting up and running with ngrok.
ngrok 8000

Once Ngrok is running it will acquire a unique URL for your web application that will make it accessible to Twilio.


Copy this URL and paste it as follows to the Request URL and then save:

Now create a new file called tamagotchi.js. This file will be the object responsible for the life of our tamagotchi. All the interactions will happen here.

var util = require('util');

var Tamagotchi = function(name, pet, food) {
  this.name = name;
  this.pet = pet;
  this.food = food;
  this.hunger = 0;
  this.awake = true;
  this.happy = 3;
  this.poo = 0;
  this.age = 0;
}

Tamagotchi is the main object and constructor for our pet. It has attributes that relate to the life and well being of our little creature. It gets initialised with a name which you will provide later, the food and game options, and some default attributes.

Just under the last line of the code above we will add some of the actions we can use to interact with our pet. These are all the things that will keep it happy.

 

// ACTIONS
Tamagotchi.prototype.play = function() {
  // A pet usually get's happy and hungry after playing a game
  this.hunger++;
  this.happy++;
  return util.format('%s played a nice game of %s', this.name, this.pet[Math.floor(Math.random() * this.pet.length)]);
};

Tamagotchi.prototype.feed = function() {
  // When you feed it, it gets a little bit less hungry, but will eventually get tired and need to poop
  if (this.hunger > 0) {
    this.hunger--;
    this.poo++;
    this.getSad();
    return util.format("%s just ate some delicious %s", this.name, this.food[Math.floor(Math.random() * this.food.length)]);
  }
  return "Not hungry!";
};

Tamagotchi.prototype.sleep = function() {
  // Sleeping takes a lot of energy
  if (this.awake) {
    this.poo++;
    this.awake = false;
    return util.format("%s just went to sleep", this.name);
  }
  return util.format("%s is already sleeping", this.name);
};
Tamagotchi.prototype.wake = function() {
  if (this.awake) {
    return util.format("%s is already awake", this.name);
  }
  this.awake = true;
  return util.format("%s just woke up", this.name);
};
Tamagotchi.prototype.poop = function() {
  if (this.poo > 0) {
    this.poo--;
    return util.format("%s just had a nice poo", this.name);
  }
  return util.format("%s doesn't need to poo right now", this.name);
};

Tamagotchi.prototype.getSad = function() {
  if (this.happy > 0)
    this.happy--;
};

Tamagotchi.prototype.aging = function() {
  // Everyone gets a little bit sad when they get older.
  this.age++;
  this.getSad();
  return util.format("%s is now one year older", this.name);
};

Now that we have defined all the actions to enable us to communicate with our pet, we need to make sure we have a way of getting information about its wellbeing. So we will implement some status functions.

// STATUS
Tamagotchi.prototype.checkAge = function() {
  return util.format("%s age: %d", this.name, this.age);
};

Tamagotchi.prototype.checkHunger = function() {
  return util.format("%s hunger level: %d", this.name, this.hunger);
};

Tamagotchi.prototype.checkHappiness = function() {
  return util.format("%s happiness level: %d", this.name, this.happy);
};

All the methods above read information about our pet and return it in a readable way.
At the very bottom of the file, add an exports so our object is made available outside of this file.

module.exports = Tamagotchi;

The Incubation

We have created all possible interactions we need with our Tamagotchi, but we need to make sure we can talk to it. We will use SMS for that, so create a new file called message.js and add the following to it.


var config = require('./config');
var twilio = require('twilio');

// Create a new REST API client to make authenticated requests against the twilio back end
var client = new twilio.RestClient(config.twilioConfig.accountSid, config.twilioConfig.authToken);

// This method is to be used when we want to initiate SMS messages via REST API
exports.send = function(message) {
  client.sendSms({
    to: config.myNumber,
    from: config.twilioConfig.number,
    body: message
  }, function(error, message) {
    if (!error) {
      console.log('Success!');
    } else {
      console.log('Oops! There was an error.');
      console.log(error)
    }
  });
}
// We will use this method in conjunction with Twilio's webhooks to automatically reply to interactions
exports.twiml = function(message){
  // Create a TwiML object to respond to requests with TwiML
  var response = new twilio.TwimlResponse();
  return response.message(message).toString();
}

Above we are creating one method to initiate SMS messages via Twilio’s REST API and one to reply to interaction messages via TwiML. Both methods take a message as a parameter

It’s time to refactor the file app.js so it communicates with our tamagotchi. Open that file and add the following three new requirements at the top.

var config = require('./config');
var Tamagotchi = require('./tamagotchi');
var message = require('./message');

Change the server creation to use the port from our config file instead of having it hardcoded. This will be useful if we later want to change the port in which our tamagotchi runs.


// Create a server with a host and port
var server = new Hapi.Server();
server.connection({
  host: 'localhost',
  port: config.port
});

var tamagotchi,
    pet = config.actions.pet,
    food = config.actions.food;

We are also adding three variables – tamagotchi, pet and food. These will be used later on.

We no longer need our “hello world” route, so you can get rid of that and replace it with our much more robust route, which orchestrates the entire communication with our tamagotchi.


server.route({
  method: 'POST',
  path: '/interact',
  handler: function(request, reply) {
    var action = request.payload.Body;
    var status;

    // check whether our pet has already been created
    if (tamagotchi == undefined) {
      // if our tamagotchi still hasn't been created we will create one
      tamagotchi = new Tamagotchi(action, pet, food);
      status = util.format('Hello, my name is %s and I was just born. Do you wanna play?', action);
    } else {
      // check if our pet is awake, we don't want to wake it up
      if (!tamagotchi.awake && action.toLowerCase() != "wake") {
        status = util.format('%s is asleep now', tamagotchi.name);
      } else {
        // let's check which action the user is going for
        switch (action.toLowerCase()) {
          case ("play"):
            status = tamagotchi.play();
            break;
          case ("feed"):
            status = tamagotchi.feed();
            break;
          case ("wake"):
            status = tamagotchi.wake();
            break;
          case ("sleep"):
            status = tamagotchi.sleep();
            break;
          case ("poo"):
            status = tamagotchi.poop();
            break;
          case ("age"):
            status = tamagotchi.checkAge();
            break;
          case ("hungry"):
            status = tamagotchi.checkHunger();
            break;
          case ("happy"):
            status = tamagotchi.checkHappiness();
            break;
          default:
            status = "Actions: play, feed, sleep, wake, poo\nStatus: age, hungry, happy";
        }
      }
    }
    reply(message.twiml(status));
  }
});

Now we need to know when our pet is alive and ready to be named. We do this by changing the server startup at the end of the file as follows.


server.start(function() {
  var start = 'What are you gonna name your pet?';
  console.log(start);
  message.send(start);
});

The Egg Hatches

Go back to terminal and run your application again. It should now send you a text message asking you to name your pet. You will now be able to interact with it and check its status.

$ node app.js

5.png

Adding Personality

No being is truly complete unless it has personality. Our pet is no different so how about we give it some intelligence?

Let’s go ahead and add some random actions to it, so whenever we interact with it, our pet also does something else randomly, that way it can go to sleep and wake up on its own and also learn to do some of its necessities without any human intervention.

Open up the tamagotchi.js file and add a requirement for message.js.

var message = require('./message');

Also, add the following two functions right after the checkHappiness function.

// PERSONALITY

Tamagotchi.prototype.aiSimulate = function() {
  if (!this.awake) {
    odds(100, 50) ? this.wake() : message.send("ZzzzZzzzZzzz");
  }
  if (odds(10, 3)) {
    if (this.poo > 0) {
      this.poop();
    }
  } else if (odds(10, 2)) {
    this.aging();
  } else if (this.awake && odds(100, 20)) {
    this.sleep();
  }
  if (this.poo > 3 || this.hunger > 3) {
    this.getSad();
  }
  if (this.happiness == 1 && odds(10, 5) || this.happiness == 0) {
    this.poo++;
    this.hunger++;
    message.send(util.format("Please take better care of %s :-(", this.name))

  }
  if (this.hunger > 5) {
    this.poo++;
    message.send(util.format("Please give %s some food :-(", this.name));
  }
};

function odds(range, threshold) {
  var random = Math.floor(Math.random() * range) + 1;
  if (random > threshold) {
    return true;
  }
  return false;
};

The odds function adds some randomness and returns a boolean based on two numbers and the chance of something happening. So for example odds(10,2) means there is an 80% chance a number above 2 will be picked between 0 and 10.

The aiSimulate function uses a mix of our pet’s current status and some randomness from the odds function. That way nothing is really predictable when it comes to the life of our pet.

Open up app.js and at the bottom of our route where we send the messages, add a call to the aiSimulate function:


tamagotchi.aiSimulate();
reply(message.twiml(status));

Now every time you interact with your pet, it will also do something else randomly based on its status and mood.

It’s alive!

Just like in the 90’s, you can take your pet with you wherever you want. You will get a new pet every time you restart your application, and as long as you remember to feed, play and clean it, it should live a very happy life. It will also bring you back the joys of taking care of your own little creature.

How about adding MMS so you also get a picture of your pet to get that extra peace of mind about how it feels? Can you make it even more intelligent and interactive? Which other old school toys can you replicate with software and Twilio?

I would love to see what you come up with. Reach me up on Twitter @marcos_placona, by email on marcos@twilio.com or +MarcosPlacona on G+ to tell me about it.