TelegramBotEngine

A mountable Rails engine that adds subscriber management, authorization, broadcasting, and an admin UI on top of the telegram-bot gem (v0.16.x).

The telegram-bot gem handles all Telegram protocol concerns (API client, webhook ingestion, controller/command routing, callback queries, session, async delivery, and testing). This engine adds the persistence and management layer that telegram-bot deliberately doesn't provide.

Installation

Add to your Gemfile:

gem "telegram_bot_engine"

Install migrations:

bin/rails telegram_bot_engine:install:migrations
bin/rails db:migrate

Configuration

Bot token

Following telegram-bot's convention, configure via Rails credentials:

# config/credentials.yml.enc
telegram:
  bot:
    token: "YOUR_BOT_TOKEN"
    username: "your_bot"

Engine configuration

# config/initializers/telegram_bot_engine.rb
TelegramBotEngine.configure do |config|
  # Authorization: only these Telegram usernames can /start and subscribe.
  config.allowed_usernames = %w[alice bob charlie]
  # OR dynamic:
  # config.allowed_usernames = -> { MyModel.pluck(:telegram_username) }
  # OR managed via admin UI:
  # config.allowed_usernames = :database
  # OR open access (no allowlist):
  # config.allowed_usernames = nil

  # Optional: disable admin UI
  # config.admin_enabled = false

  # Optional: custom messages
  # config.unauthorized_message = "Sorry, you're not authorized to use this bot."
  # config.welcome_message = "Welcome %{username}! Available commands:\n%{commands}"

  # Event logging — logs commands, deliveries, auth failures to the database
  # config.event_logging = true          # default: true
  # config.event_retention_days = 30     # default: 30, auto-purges older events
end

Webhook controller

Create a controller inheriting from telegram-bot's UpdatesController and include the engine's concern:

# app/controllers/telegram_webhook_controller.rb
class TelegramWebhookController < Telegram::Bot::UpdatesController
  include TelegramBotEngine::SubscriberCommands
  # Provides: start!, stop!, help! with authorization and subscription management.

  # Add your own commands:
  def status!(*)
    respond_with :message, text: "All systems operational"
  end
end

Routes

# config/routes.rb
Rails.application.routes.draw do
  telegram_webhook TelegramWebhookController

  # Mount admin UI (protect with your own authentication)
  authenticate :user, ->(u) { u.admin? } do
    mount TelegramBotEngine::Engine, at: "/telegram/admin"
  end
end

Running more than one bot? Replace telegram_webhook with the engine's inbound dispatcher — see Multiple bots.

Set webhook

These rake tasks come from the telegram-bot gem:

# Register your app's URL with Telegram so it sends updates to your server
bin/rails telegram:bot:set_webhook RAILS_ENV=production

# Remove the webhook (e.g. before switching to polling)
bin/rails telegram:bot:delete_webhook

# Run a local poller for development (no webhook needed)
bin/rails telegram:bot:poller

The webhook URL is derived from your Rails routes (telegram_webhook route helper). Make sure your production server is accessible via HTTPS before setting the webhook.

For local development with ngrok, configure default_url_options in config/environments/development.rb:

if ENV['HOST'].present?
  routes.default_url_options[:host] = ENV.fetch("HOST", "localhost:3000")
  routes.default_url_options[:protocol] = ENV.fetch("PROTOCOL", "https")
end

Then run:

HOST=your-subdomain.ngrok-free.app bin/rails telegram:bot:set_webhook

Usage

Broadcasting

# Broadcast to all active subscribers
TelegramBotEngine.broadcast("Deployment complete!")

# With Markdown formatting
TelegramBotEngine.broadcast(
  "*Deploy complete*\nVersion: `v2.3.4`",
  parse_mode: "Markdown"
)

Direct messaging

TelegramBotEngine.notify(
  chat_id: 123456789,
  text: "Your report is ready."
)

Admin UI

When mounted, the engine provides a web interface for:

  • Dashboard — bot info, subscription counts
  • Subscriptions — list, activate/deactivate, delete
  • Allowlist — add/remove usernames (when config.allowed_usernames = :database)
  • Events — browsable log of commands, deliveries, and auth failures with filtering by type, action, and chat ID

Event log

The engine automatically logs operational events to the telegram_bot_engine_events table:

Event type Actions When
command start, stop, help User sends a bot command
delivery broadcast, notify, delivered, blocked Messages are queued or delivered
auth_failure unauthorized Unauthorized user attempts a command

Events are viewable in the admin UI and can be queried directly:

# Recent command events
TelegramBotEngine::Event.by_type("command").recent.limit(20)

# Deliveries to a specific chat
TelegramBotEngine::Event.by_type("delivery").by_chat_id(123456789)

# Events in the last 24 hours
TelegramBotEngine::Event.since(24.hours.ago)

# Manual purge (automatic purge runs probabilistically)
TelegramBotEngine::Event.purge_old!

Disable event logging entirely with config.event_logging = false.

Multiple bots

The engine manages many Telegram bots from one Rails host. Everything keyed by a Telegram bot identity — subscribers, allowlist, delivery, and inbound routing — is scoped per bot. Existing single-bot setups keep working unchanged: every call without a bot: argument targets the default bot, which is seeded automatically from your existing telegram.bot credentials (or TELEGRAM_BOT_TOKEN).

The Bot model

Each bot is a persisted TelegramBotEngine::Bot record (token, slug, and per-bot webhook credentials). Manage them in the admin UI, a seed, or the console:

TelegramBotEngine::Bot.create!(
  name: "Support bot",
  slug: "support",
  token: Rails.application.credentials.dig(:telegram, :support_bot, :token),
  active: true
)

TelegramBotEngine::Bot.default            # the back-compat anchor (auto-seeded on first use)
TelegramBotEngine::Bot.resolve("support") # look up by slug
bot.client                                # this bot's memoized Telegram::Bot::Client

webhook_id (the non-secret id that appears in the webhook URL) and webhook_secret (the bearer credential, sent only in the X-Telegram-Bot-Api-Secret-Token header) are generated automatically.

Bot-aware delivery

support = TelegramBotEngine::Bot.resolve("support")

# Broadcast / notify through a specific bot's own client
TelegramBotEngine.broadcast("Support is online", bot: support)
TelegramBotEngine.notify(chat_id: 123456789, text: "Ticket updated", bot: support)

# Omit bot: to target the default bot (unchanged single-bot behavior)
TelegramBotEngine.broadcast("Deployment complete!")

Subscribers and allowlist entries are scoped per bot: a user who blocks the support bot is deactivated only for that bot, and a per-bot allowlist entry never escalates into a global one.

Inbound updates for multiple bots

telegram-bot's telegram_webhook route serves a single bot. For multiple bots, mount the engine's inbound dispatcher once — it resolves the target bot from the URL, validates the secret-token header, then hands off to your UpdatesController:

# config/initializers/telegram_bot_engine.rb
TelegramBotEngine.configure do |config|
  config.webhook_base_url    = "https://app.example.com"     # host public HTTPS base
  config.webhook_mount_path  = "/telegram/bot"               # must match the mount below
  config.dispatch_controller = "TelegramWebhookController"    # your UpdatesController (incl. SubscriberCommands)
  config.auto_register_webhooks = true                       # (un)register webhooks on Bot save/destroy
end
# config/routes.rb
mount TelegramBotEngine::Dispatch, at: "/telegram/bot"

With webhook_base_url set and auto_register_webhooks enabled, saving an active Bot registers its Telegram webhook (<base_url>/telegram/bot/<webhook_id>, secret in the header) and deactivating it removes the webhook — no manual set_webhook per bot. You can also (re)register explicitly:

TelegramBotEngine::WebhookRegistrar.register(TelegramBotEngine::Bot.resolve("support"))

Unlike telegram-bot 0.16, the dispatcher validates the X-Telegram-Bot-Api-Secret-Token header (constant-time) on every inbound request, so only Telegram can drive your bots.

Requirements

  • Ruby >= 3.3.0
  • Rails >= 7.0
  • telegram-bot ~> 0.16

License

MIT