Bouke van der Bijl

My experience creating a Telegram Bot

Telegram has been adding lots of features this year, including extensive chatbot support. To highlight and promote these features they announced a bot prize in April.

I got very interested of course, not just because of the cold hard cash prize, but also because bots are super hot right now, with Slack, and Facebook betting big on bots. I wrote this post to document my experience in creating Memorization Bot, which is a so-called ‘spaced-repetition system’ bot. Spaced-repetition systems are designed to make you remember things by showing you a flash card and asking you to recall what piece of information is hidden on the other side of it. After you reveal the information, it asks you how well you remembered it, and depending on your reply it schedules the card to be shown again at some later point in the future. This is super useful for remembering any kind of factual information you want, lots of people use it for learning new languages for example.

I’ve tried using other more popular spaced-reptition systems in the past like Anki, but one deficiency I found with that one in particular is that you need to open up the application every day to check whether there’s anything that needs a review.

Memorization Bot

This is where the idea for Memorization Bot came from. I realized that the bot format is perfect for this, as the bot can simply send you a message telling you it’s time for your rehearsal. This makes the rehearsal process entirely frictionless, and I haven’t missed a single day since starting to use it.

Example of usage

Every day at 09:30 I receive a notification telling me it’s time for my rehearsal.

When I open the chat, I am presented with the front side of the card, and the option to show the back.

Then, I press “🔄 Show back” which presents me with the back and it asks me to reply with how well I recalled it.

After I reply, it will schedule the card to be shown again in the future. If there’s more cards to show it will do so, otherwise I’m done! The chat bot format is super beneficial for this app, as I didn’t need to seek out anything, it was all presented to me and I just needed to press the buttons on the screen.

Algorithm

For the algorithm I went with SuperMemory 2, with some modifications to make it work with 4 replies instead of 6. You can find the implementation in the repository. The original version is sm.SM2, with the modified version available as sm.SM2Mod.

Technical details

For the implementation I decided to use Go with Postgres as the backing database. I used Go because in my experience it’s really good at handling lots of HTTP requests without breaking a sweat, which is needed as Telegram will hammer your application with webhooks. I used Postgres because it has great timezone handling and consistency guarantees. I utilize PL/pgSQL functions to handle all the scheduling and timezone logic.

The hardest part of creating the bot was keeping track of what state the user was in, and determining what to display to the user depending on that. I solved this by having two giant case statements, one that determines what actions to take depending on the user reply, and another that determines what to display to the user when they transition into a state. This latter it easy to display the right message, no matter what the starting state is.

All the possible states are in a single big enumeration, which looks something like this:

type State uint

const (
  DeckList = State(iota)
  Rehearsing
  RehearsingCardReview
  DeckCreate
  DeckDetails
  CardCreate
  CardCreateBack
  CardDelete
  etc...

This integer is saved in the users table with a JSON object containing some extra info about the state (e.g. the selected deck when the state is DeckDetails).

On every incoming message I simply fetch the current state from the database, process the message, and save the new state back into the database. I then look up the state and reply with the appropriate messages.

To ensure that users don’t get in a confused state, I use Postgres row-level locking around the processing of a user message so only one event is being processed per user at any time. Every operation inside the handling of a message is done in a transaction, so any error simply brings the user back to their previous state.

For the actual communication with the Telegram API I used telegram-bot-api which is a fantastically well maintained and complete implementation.

You can add the bot here and find all the code on my GitHub.

Why I like Telegram’s Bot API

I haven’t tried creating a bot for Facebook yet, but at first glance its API seems a lot less powerful than Telegram’s. Telegram’s power comes from the fact that it allows you to define custom keyboards that enable you to build a custom UI. This makes it very easy to create a menu system of sorts, and allows the user to clearly see what they can do at any moment, without having to guess random phrases.

Facebook’s messenger platform allows you to send multiple cards that each only allow you to present up to 3 buttons. This is a lot less flexible. Presumably this is because Facebook wants people to make natural language bots, through platforms like wit.ai. I personally have never had a pleasant interaction with a natural language bot, and I don’t think it’s going to happen any time soon. The biggest problem in my experience is that there’s no way to figure out what the bot can actually do, which Telegram solved elegantly with the reply keyboards.

You should make a bot too!

When I started out creating Memorization Bot I couldn’t find any complete examples of Telegram bots people created. Most of them were either trivial bots that just replied with the message people sent, or would just broadcast the same message to everyone. I hope these notes help someone else create an interesting and useful bot, make sure to tweet at me if you do!

Oct 2016