Gem Version Code Climate Build Status

Tools for developing bot for Telegram. Best used with Rails, but can be be used in standalone app. Supposed to be used in webhook-mode in production, and poller-mode in development, but you can use poller in production if you want.

Package contains:

  • Ligthweight client for bot API (with fast and thread-safe httpclient under the hood).
  • Controller with message parser. Allows to write separate methods for each command.
  • Middleware and routes helpers for production env.
  • Poller with automatic source-reloader for development env.
  • Rake tasks to update webhook urls.

Here is sample telegram_bot_app with session, keyboards and inline queries. Run it on your local machine in 1 minute!


Add this line to your application's Gemfile:

gem 'telegram-bot'

And then execute:

$ bundle

Or install it yourself as:

$ gem install telegram-bot



Add telegram section into secrets.yml:

    # just set the token
    chat: TOKEN_1
    # or add username to support commands with mentions (/help@ChatBot)
      token: TOKEN_2
      username: ChatBot

  # Single bot can be specified like this
  bot: TOKEN
  # or
    token: TOKEN
    username: SomeBot


From now clients will be accessible with Telegram.bots[:chat] or Telegram.bots[:auction]. Single bot can be accessed with or Telegram.bots[:default].

You can create clients manually with, username). Username is optional and used only to parse commands with mentions.

There is request(path_suffix, body) method to perform any query. And there are also shortcuts for available queries in underscored style (answer_inline_query instead of answerInlineQuery). All this methods just post given params to specific URL.

bot.request(:getMe) or bot.get_me
bot.request(:getupdates, offset: 1) or bot.get_updates(offset: 1)
bot.send_message chat_id: chat_id, text: 'Test'

By default client will return parsed json responses. You can enable response typecasting to virtus models using telegram-bot-types gem:

# Add to your gemfile:
gem 'telegram-bot-types', '~> x.x.x'
# Enable typecasting:
# or for single instance:
bot.extend Telegram::Bot::Client::TypedResponse

bot.get_me.class # => Telegram::Bot::Types::User

Any API request error will raise Telegram::Bot::Error with description in its message. Special Telegram::Bot::StaleChat is raised when bot can't post messages to the chat anymore.


class Telegram::WebhookController < Telegram::Bot::UpdatesController
  # use callbacks like in any other controllers
  around_action :with_locale

  # Every update can have one of: message, inline_query, chosen_inline_result,
  # callback_query.
  # Define method with same name to respond to this updates.
  def message(message)
    # message can be also accessed via instance method
    message == self.payload # true
    # store_message(message['text'])

  # This basic methods receives commonly used params:
  #   message(payload)
  #   inline_query(query, offset)
  #   chosen_inline_result(result_id, query)
  #   callback_query(data)

  # Define public methods to respond to commands.
  # Command arguments will be parsed and passed to the method.
  # Be sure to use splat args and default values to not get errors when
  # someone passed more or less arguments in the message.
  # For some commands like /message or /123 method names should start with
  # `on_` to avoid conflicts.
  def start(data = nil, *)
    # do_smth_with(data)

    # There are `chat` & `from` shortcut methods.
    response = from ? "Hello #{from['username']}!" : 'Hi there!'
    # There is `respond_with` helper to set `chat_id` from received message:
    respond_with :message, text: response
    # `reply_with` also sets `reply_to_message_id`:
    reply_with :photo, photo:'party.jpg')


  def with_locale(&block)
    I18n.with_locale(locale_for_update, &block)

  def locale_for_update
    if from
      # locale for user
    elsif chat
      # locale for chat

Optional typecasting

You can enable typecasting of update with telegram-bot-types by including Telegram::Bot::UpdatesPoller::TypedUpdate:

class Telegram::WebhookController < Telegram::Bot::UpdatesController
  include Telegram::Bot::UpdatesController::TypedUpdate

  def message(message)
    message.class # => Telegram::Bot::Types::Message


There is support for sessions using ActiveSupport::Cache stores.

# configure store in env files:
config.telegram_updates_controller.session_store = :redis_store, {expires_in: 1.month}

class Telegram::WebhookController < Telegram::Bot::UpdatesController
  include Telegram::Bot::UpdatesController::Session
  # or just shortcut:

  # You can override global config
  self.session_store = :file_store

  def write(text = nil, *)
    session[:text] = text

  def read
    respond_with :message, text: session[:text]

  # By default it uses bot's username and user's id as a session key.
  # Chat's id is used only when `from` field is empty.
  # Override `session_key` method to change this behavior.
  def session_key
    # In this case session will persist for user only in specific chat:

Message context

It's usual to support chain of messages like BotFather: after receiving command it asks you for additional argument. There is MessageContext for this:

class Telegram::WebhookController < Telegram::Bot::UpdatesController
  include Telegram::Bot::UpdatesController::MessageContext

  def rename(*)
    # set context for the next message
    save_context :rename
    respond_with :message, text: 'What name do you like?'

  # register context handlers to handle this context
  context_handler :rename do |*words|
    update_name words[0]
    respond_with :message, text: 'Renamed!'

  # You can do it in other way:
  def rename(name = nil, *)
    if name
      update_name name
      respond_with :message, text: 'Renamed!'
      save_context :rename
      respond_with :message, text: 'What name do you like?'

  # This will call #rename like if it is called with message '/rename %text%'
  context_handler :rename

  # If you have a lot of such methods you can use
  # It'll use context value as action name for all contexts which miss handlers.

You can use CallbackQueyContext in the similar way to split #callback_query into several specific methods. It doesn't require session support, and takes context from data. If data has a prefix with colon like this my_ctx:smth... it'll call my_ctx_callback_query('smth...') when there is such action method. Otherwise it'll call callback_query('my_ctx:smth...') as usual.

Processesing updates

To process update run:

ControllerClass.dispatch(bot, update)

There is also ability to run action without update:

# Most likely you'll want to pass :from and :chat
controller =, from: telegram_user, chat: telegram_chat)
controller.process(:help, *args)


Use telegram_webhooks helper to add routes. It will create routes for bots at "telegram/#botbot.token" path.

# Create routes for all Telegram.bots to use same controller:
telegram_webhooks TelegramController

# Or pass custom bots usin any of supported config options:
telegram_webhooks TelegramController,
                 {token: token, username: username},

# Use different controllers for each bot:
telegram_webhooks bot => TelegramChatController,
                  other_bot => TelegramAuctionController

# telegram_webhooks creates named routes.
# Route name depends on `Telegram.bots`.
# When there is single bot it will use 'telegram_webhook'.
# When there are it will use bot's key in the `Telegram.bots` as prefix
# (eg. `chat_telegram_webhook`).
# You can override this options or specify others:
telegram_webhooks TelegramController, as: :my_webhook
telegram_webhooks bot => [TelegramChatController, as: :chat_webhook],
                  other_bot => TelegramAuctionController,
                  admin_chat: TelegramAdminChatController

For Rack applications you can also use Telegram::Bot::Middleware or just call .dispatch(bot, update) on controller.

Development & Debugging

Use rake telegram:bot:poller to run poller. It'll automatically load changes without restart in development env. Optionally specify bot to run poller for with BOT envvar (BOT=chat).

This task will not work if you don't use telegram_webhooks. You can run poller manually with Telegram::Bot::UpdatesPoller.start(bot, controller_class).


There is Telegram::Bot::ClientStub class to stub client for tests. Instead of performing API requests it stores them in requests hash.

To stub all possible clients use Telegram::Bot::ClientStub.stub_all! before initializing clients. Most likely you'll want something like this:

RSpec.configure do |config|
  # ...
  config.after { }
  # ...

There are also some helpers for controller tests. Check out telegram/bot/updates_controller/rspec_helpers and telegram/bot/updates_controller/testing.


Use rake telegram:bot:set_webhook to update webhook url for all configured bots. Certificate can be specified with CERT=path/to/cert. metrics

Initialize with bot =, botan: 'botan token') or just add botan key in secrets.yml:

      token: bot_token
      botan: botan_token

Access to Botan client with bot.botan. Use bot.botan.track(event, uid, payload) to track events.

There are some helpers for controllers in Telegram::Bot::UpdatesController::Botan:

class Telegram::WebhookController < Telegram::Bot::UpdatesController
  include Telegram::Bot::UpdatesController::Botan

  # This will track with event: action_name & data: payload
  before_action :botan_track_action

  def smth(*)
    # This will track event for current user only when botan is configured.
    botan_track :my_event, custom_data

    # or get access directly to botan client:

There is no stubbing for botan clients, so don't set botan token in tests.


After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to

Different Rails versions

To setup development for specific major Rails version use:

RAILS=5 bundle install
# or
RAILS=5 bundle update


Bug reports and pull requests are welcome on GitHub at