Telega

Package VersionHex Docs

A Gleam library for the Telegram Bot API on BEAM.

Telega

It provides

Quick start

If you are new to Telegram bots, read the official Introduction for Developers written by the Telegram team.

First, visit @BotFather to create a new bot. Copy the token and save it for later.

Initiate a gleam project and add telega as a dependency:

$ gleam new first_tg_bot
$ cd first_tg_bot
$ gleam add telega gleam_erlang telega_httpc

Replace the first_tg_bot.gleam file content with the following code:

import gleam/erlang/process
import telega
import telega/reply
import telega/router
import telega/update
import telega_httpc
fn handle_text(ctx, text) {
use ctx <- telega.log_context(ctx, "echo_text")
let assert Ok(_) = reply.with_text(ctx, text)
Ok(ctx)
}
fn handle_command(ctx, command: update.Command) {
use ctx <- telega.log_context(ctx, "echo_command")
let assert Ok(_) = reply.with_text(ctx, "Command: " <> command.text)
Ok(ctx)
}
pub fn main() {
let router = router.new("echo_bot")
|> router.on_any_text(handle_text)
|> router.on_commands(["start", "help"], handle_command)
let client =
telega_httpc.new("BOT_TOKEN")
let assert Ok(_bot) =
telega.new_for_polling(api_client: client)
|> telega.with_router(router)
|> telega.init_for_polling_nil_session()
process.sleep_forever()
}

Replace "BOT_TOKEN" with the token you received from the BotFather. Then run the bot:

$ gleam run

And it will echo all received text messages.

Congratulations! You just wrote a Telegram bot :)

Architecture

Calling telega.init_for_polling() (or telega.init() for webhooks) starts an OTP supervision tree:

TelegaRootSupervisor (OneForOne)
├── ChatInstances (factory_supervisor, Transient children)
│ ├── ChatInstance {chat1:user1}
│ ├── ChatInstance {chat2:user2}
│ └── ...
├── Bot actor (Permanent)
└── Polling worker (Permanent) — only in polling mode

Each telega.init* call creates an independent tree with its own ETS registry, so multiple bot instances don't conflict.

Graceful shutdown

telega.shutdown(bot)

Sends an OTP shutdown signal to the root supervisor, which stops children in reverse start order (polling → bot → chat factory).

Dependency injection

Handlers reach shared services — a database pool, an HTTP client, an i18n catalog — through the typed, non-persisted dependencies slot on Context. It is set once at init and is never serialized, unlike session (which holds per-user state):

pub type Dependencies {
Dependencies(db: Connection, catalog: Catalog)
}
telega.new_for_polling_with_dependencies(api_client: client, dependencies: Dependencies(db:, catalog:))
|> telega.with_router(router)
|> telega.init_for_polling()
// in any handler / flow step / middleware:
fn my_bookings(ctx: Context(Nil, String, Dependencies), _cmd) {
let bookings = db.list_bookings(ctx.dependencies.db, ctx.update.from_id)
reply.with_text(ctx, format_bookings(bookings))
}

Bots that need no services pay nothing: dependencies defaults to Nil. See the Dependency injection guide.

Testing

Telega ships with a testing toolkit under telega/testing/ — mock clients, data factories, and a declarative conversation DSL. No real Telegram API calls needed.

import telega/testing/conversation
pub fn greeting_flow_test() {
conversation.conversation_test()
|> conversation.send("/start")
|> conversation.expect_reply_containing("Hello")
|> conversation.send("Alice")
|> conversation.expect_reply_containing("Alice")
|> conversation.run(build_router(), fn() { MySession(name: "") })
}

See the full Testing guide for handler isolation, mock clients, media assertions, and more.

Ecosystem

Telega is a monorepo. The core telega package is HTTP-client- and storage-agnostic; pick the adapters you need:

PackagePurpose
telega_wispWisp webhook adapter (endpoint handling, secret-token validation)
telega_mistMinimal webhook adapter directly over mist, without wisp
telega_httpcHTTP client adapter over Erlang httpc
telega_hackneyHTTP client adapter over hackney
telega_storage_postgresPostgreSQL session/flow storage adapter
telega_storage_sqliteSQLite session/flow storage adapter
telega_storage_redisRedis/Valkey session/flow storage adapter
telega_webappTelegram Mini Apps (Web App) initData validation and helpers
telega_i18nInternationalization: TOML/JSON catalogs, locale middleware, interpolation, CLDR pluralization

Examples

Progressive examples in the examples directory:

  1. 00-echo-bot — Basic echo with long polling
  2. 01-commands-bot — Command handling
  3. 02-session-bot — Stateful sessions
  4. 03-conversation-bot — Multi-message conversations
  5. 04-keyboard-bot — Inline keyboards and callbacks
  6. 05-media-group-bot — Media group handling
  7. 06-restaurant-booking — Full-featured application with flows and database

Development

gleam build # Build the project
gleam test # Run the tests
gleam format # Format code
gleam shell # Run an Erlang shell