I have a couple of reasons to write this article. First of all I have a repository called ruby-telegram-bot-starter-kit, which contains a boilerplate for creating simple Telegram bots. It’s pretty cool, it allows you to persist your data, translate your messages via i18n, use custom keyboards and etc., but as I’ve realised later I did miss a couple of things which are pretty important.

I’ve been asked a lot via Github issues about the right/possible way to store state of a Telegram bot vs user communication. The technique of communicating with users that I used in this repo was plain old long-polling, which sucks, because Telegram has Webhooks API which is a much more convenient way to setup user/bot communication because you will get updates only once a user sends a message to your bot.

So, today we are going to write a little quest game bot in Telegram using Ruby on Rails, Webhooks API and some architecture which will help you to store your user/bot state like a charm 😉

Let’s imagine that you already have a Rails app created and a Rails server running on localhost:3000.

Webhooks API works only with HTTPS, so we need to use ngrok to proxy our localhost:3000 with some server accessible via HTTPS. So, let’s download ngrok (or install it via brew on OS X) and execute:

ngrok http 3000

If everything is going ok you’ll see something like this:

So, since we need HTTPS, we will use this one:

Our next step is to create a bot in Telegram and get its token.

To do that you need to find a Botfather in Telegram.

Write him a /newbot command to start new bot creation:

Cool, now we have a Rails server running and a Telegram bot available to communicate with our users.

Our next step is to setup communication with our user using Webhooks API. Let’s do that.

We need to visit URL, which can be constructed like this one:

https://api.telegram.org/bot<telegram_bot_token_from_botfather>/setWebhook?url=<your_https_ngrok_url>/webhooks/telegram_<place_some_big_random_token_here>

So, in my case it will be something like this:

https://api.telegram.org/bot220521163:AAHNZe-njGuJz_6-zwOyR1BKkhyJoGpNPyo/setWebhook?url=https://4691cf2f.ngrok.io/webhooks/telegram_vbc43edbf1614a075954dvd4bfab34l1

As you can see, I placed a random string to the end of our URL to make sure that nobody will guess it and get access to our bot/user communication.

If you’ve done everything ok, when visiting this URL you’ll see a success message which looks something like this:

So, this message means that all of the messages sent to your bot will go through your Rails application’s WebhooksController, which we are going to implement further.

Open an existing or create a new webhooks_controller.rb file and implement a method called #callback.

Do not forget to add mapping to routes.rb.

post ‘/webhooks/telegram_vbc43edbf1614a075954dvd4bfab34l1’ => 'webhooks#callback'

Your WebhooksController will now look like this:

When user sends a message to our bot, it goes to our WebhooksController#callback in a params[‘webhook’] hash, which contains the message and info about the user who sent it and etc.

So, right now we have a controller action for receiving incoming messages.

But we have used a User model which is not created yet, let’s generate it.

rails g model User

Open our migration at db/migrate and replace it with this code:

P.S. Do not forget to add a uniqueness validation for telegram_id in User model class.

validates_uniqueness_of :telegram_id

Run migrations then:

bundle exec rake db:migrate

Next step is to implement a plain Ruby class that will differentiate our messages and answer them correctly according to the step our user is now on. So, for these purposes we need to create a class called BotMessageDispatcher which will have a list of commands available for the user.

Since we are writing a very simple quest game we need to define a command for each particular step of our game. One more thing we need to do to make our game work is to remember on which step the user is now and not to jump across and between independent steps.

So, let’s decide what we will do in our quest game. I decided to go with ‘Become a Rails rockstar’ game which will have 3 simple steps:

1. Be born.

2. Accomplish your very first Rails tutorial.

3. Write a blog in 15 minutes.

You are a real Rails rockstar!

So, you can not accomplish any Rails tutorials without being born and also you can not write a blog in 15 minutes without accomplishing any Rails tutorials. So, we need to take care about this and not allow the user to jump to step 2 without accomplishing step 1 and etc.

That is where state storing comes in. We are going to create some methods for our User model to get the current step, next step, save the current step and etc.

So, as you can see we implemented some very useful helpers which will help us to store and retrieve user state from database. Here you can see the word bot_command, so, we will build our user/bot communication with abstractions represented in the plain Ruby class called BotCommand.

We will create a base class with basic functionality and a class inherited from it for each the command we need.

So, first of all let’s implement our BotMessageDispatcher which will know which particular command to apply at any moment of bot-user communication.

Here it is:

Here you can see a constant array of all the commands we need and a method called #process. It will figure out which command our user is able to process now, save it, and then prepare user for processing another command.

So, we have a mechanism for dispatching messages, storing state, figuring out which command we need to perform at each moment of user-bot communication. So, the last step of our implementation is to implement the BotCommand class and a separate class for each command. Let’s do it.

Our BotCommand class will actually send messages to users, so we need to install a Ruby gem for communication with Telegram to do that.

Just add this line to your Gemfile and do a bundle install command.

gem ‘telegram-bot-ruby’

One more thing we need to send messages is our Telegram bot token, so let’s add it to the secrets.yml file in a key called bot_token.

For me it will be:

We are ready to go!

Here is how your BotCommand::Base class will look like:

Here it is. We have a base class for our abstract bot command. Here we have a method for sending messages and 2 methods which need to be implemented in all the other command classes which will be inherited from the Base class.

So, let’s do that. The very first command is called Start and is used to trigger Telegram bot to start conversation with our user.

The next command we need to implement is BotCommand::Born class.

Cool, we have been born, let’s try to accomplish our very first Rails tutorial.

Tutorial accomplished, your user knows a lot of stuff about Rails already. Let’s give him the ability to write a blog and become a Rails rockstar.

Last step we need is to implement a BotCommand::Undefined class which will be triggered each time the bot will not understand the command you’ve sent.

Looks like we’ve done it.

As you can see our architecture is pretty simple and very flexible, so you are welcome to change it according to your needs.

Good luck in creating useful bots and thanks for recommending this article to your friends by clicking on a ‘heart’ icon :)

Get ALL USEFUL INFORMATION about bots in one free awesome repo on Github!