Get Straight to MVP with Chatbots (Part 1)

When we design a product at Theodo, we follow the Lean Startup methodology. Which means we try to ship a Minimum Viable Product for our clients as soon as possible, and then iterate on this product using final user feedback in order to avoid developing useless features.

This methodology implies focusing on the core business features. However, while developing a product, you often face many other challenges (design, technical infrastructure) that might slow you down and distract you from creating value for your customer.

In this vein, Chatbots are a way to create the perfect MVP. Chatbots are software services that you offer through traditional messaging apps that users already installed on their phones (the most famous of them being Facebook Messenger, Whatsapp, Slack, Telegram… ). Since you are bound to using the conversational interface provided by those services, you do not need to focus on things like design and delivering message in real time through a robust infrastructure: those platforms do it for you.

In this series of blog posts, I will show you the potential of different messaging platforms on which you can easily build bots through an example I personally implemented: a bot to search through movie showtimes around me in Paris.

Telegram

The first platform I will introduce is Telegram since it is the easiest one to work with. It has a huge community of over 100M monthly active users, and is free of charge and cross-platform.

Step 1: Talk to the BotFather

In order to create a bot, you have to talk… to a bot!

The Botfather Get your application token
botfather botfather-sc1

Get in touch with the BotFather on Telegram in order to create a bot, give it a profile picture, and get a token to be notified when it receives messages – and answer accordingly.

Now I advise storing this token in your .bash_profile or equivalent in order to use it in your code.

export CINEBOT_TELEGRAM_TOKEN=XXXXX

Listening to your Bot

In order to fetch messages received by your bot, Telegram offers two options: Long Polling and Webhooks

Long Polling

Long polling is a way to retrieve new information from the server. Unlike traditional polling – which opens an HTTP connection, fetches the newest results, and then closes the connection, every X seconds – long polling makes an HTTP request to the server and keeps that connection open until it receives fresh informations, and then closes the connection. We only send a ping when the connection is closed, either because we received data, or because the request timed out.

Webhooks

webhooks

Webhooks are one of the most used technologies to implement reaction on an event happening on a distant server. The idea is to indicate to the chatbot platform (Telegram, Facebook Messenger) an endpoint on which your application will be listening. Whenever your bot receives a message, the chatbot platform sends a POST request to this endpoint, containing information about the messages your bot received (sender identifier, content, location, etc…). Since you need to specify an IP address, it requires that you host your application on a server, unless you use a tunneling application like Ngrok that allows you to generate a fixed proxy address that redirects to your localhost. Read this article by my fellow Theodoer Matthieu Augier if you’re interested in that.

Getting started

I chose to implement this application in NodeJS, since it allows us to handle asynchronous events (like receiving messages and responding) by default, and is fast as well as commonly understood.

mkdir cinebot
npm init
npm install --save node-telegram-bot-api
touch bot.js

Now we’ll start coding! The code I will insert here can be found in a demo repository I created for this article. It contains the code for the whole Telegram bot as well as a mock for the showtimes API.

Handling messages

I chose to use a NodeJS library that allows for listening to event received by my bot and react accordingly. Here I chose the option to listen to messages using long polling because it allows you to get your bot up and running in a few minutes.

  var TelegramBot = require('node-telegram-bot-api');
  var CineApi = require('./cine-api.js')

  var telegramToken = process.env.CINEBOT_TELEGRAM_TOKEN;

  // Setup polling way
  var bot = new TelegramBot(telegramToken, {polling: true});
  var cineApi = new CineApi();

The CineApi class implements methods that will enable us to query showtimes data using text and location, as well as format the response for sleek display.

  CineApi.prototype.query = function(text, location, onResultCallback) { ... }

  CineApi.prototype.formatTitle = function(hit) { ... }

  CineApi.prototype.formatDescription = function(hit) { ... }

Saying Hello

A good way to get your user onboard is through a warm welcome message, which you can also use to explain how to converse with your bot. For this purpose, I advise to create a file introduction.txt within your bot folder, that will contain that message.

Then you need to listen to the /start command which a user automatically triggers when they want to converse with your bot

  bot.onText(/\/start/, function(msg, match) {
    var chatId = msg.from.id;
    bot.sendMessage(chatId, introText);
  });
node bot.js

Now engage with your Bot – from your phone, or the Telegram web view – WOW 😍

start

Handling text messages

The first step for the bot is to be able to fetch showtimes based on some text from the user.

  bot.on(/\/showtimes (.+)/, function(msg, match) {
    var queryText = match[1]
    var chatId = query.chat.id

    cineApi.query(queryText, null, function(result) {
      var seances = content.hits;
      var response = ''
      for(var i = 0; i < seances.length; i++) {
        var seance = seances[i]
        var response = cineApi.formatTitle(seance) + '\n' + cineApi.formatDescription(seance) + '\n'

        bot.sendPhoto(chatId, seance.movie.posterUrl, {caption: response})
      }
    });
  });

bot-showtimes-1

Handling location messages

Usually, I find myself in situations where I want to go see a movie nearby, but I do not know where the closest cinema is, or which movies it features. Therefore, I want to be able to send my location to my bot, and retrieve the showtimes of theaters around me.

  bot.on('location', function(message) {
    var chatId = message.chat.id

    // Doesn't hurt being polite
    bot.sendMessage(chatId, 'Thanks for your location!')

    cineApi.query(' ', message.location, function (content) {
      var showtimes = content.hits;
      var response = ''
      for(var i = 0; i < seances.length; i++) {
        var seance = seances[i]
        var response = cineApi.formatTitle(seance) + '\n' + cineApi.formatDescription(seance) + '\n'

        bot.sendPhoto(chatId, seance.movie.posterUrl, {caption: response})
      }
    });
  })

location

Reducing friction: use custom keyboards

Using location is great, but it requires the user to make the effort of sending you his location. To reduce the friction even more, you can use custom keyboards to control the users choices, and bind actions to buttons to improve the user experience. Let’s see below how we can improve our intro message to directly engage the user by offering them the choice to try the service with just one tap.

  // Editing directly the /start callback
  bot.onText(/\/start/, function(msg, match) {
    var chatId = msg.from.id;
    bot.sendMessage(chatId, introText);

    var opts = {
      reply_markup: {
        keyboard: [[{'text': 'Oui', 'request_location':true} ], ['Non']],
        resize_keyboard: true,
        one_time_keyboard: true
      }
    }
    setTimeout( function() {
      bot.sendMessage(chatId, 'I need your location to help you! Can you send me your location?', opts);
    }, 3000)
  });

Mimicking a human being by setting a fake 3 seconds wait is also a way to make your user experience more natural.

bot-start-with-location

Combining both with inline queries

An amazing feature from Telegram is that you can query a bot directly from a conversation with someone else. This allows us to combine text and location-based searches in a very natural way:

bot.on('inline_query', function(request) {
  var queryText = request.query;
  var queryLocation = request.location
    var inline_query_id = request.id;
    cineApi.query(queryText, queryLocation, function (content) {
        var hits = content.hits;
        var inlineResults = []

        for(var i = 0; i < hits.length; i++) {
            var inlineResult = hitToInlineResult(hits[i]);
            inlineResults.push(inlineResult);
        }

        bot.answerInlineQuery(inline_query_id, inlineResults);
    });
});

This can prove useful in many situations, like for example when you need to quickly set up a movie date with Scarlett Johansson!

scarlett-1

Ready for production

We now have a working useful bot that runs in local. But if you want your bot to stay up and running at all time so that you can sleep peacefully, you’ll need to deploy your bot on a server and run a process manager to take care of it.

I personally use Digital Ocean which has one of the cheapest plans out there. Coupled with pm2, I can sleep safe knowing that my bot is ready to help night wanderers find the right movie at the right theater, at all time.

You can use the bot here if you want to test it live! However it only gives you the showtimes of French theaters around Paris at the moment.

That’s all folks!

Just kidding, a second part is coming! That one will focus on Facebook Messenger: with over a billion daily active users, it is currently the most used messaging app in the world, and offers slightly different functionalities compared to Telegram.

I will also discuss the best ways to remember users in order to re-activate them on a regular basis.

Data & Credits

Built with Algolia Logo, with data from

Interested in building a Chatbot ? Please leave a comment if you need advice or just want to discuss :)


You liked this article? You'd probably be a good match for our ever-growing tech team at Theodo.

Join Us