rockneurotiko/ex_gram

Telegram Bot API low level API and framework

Elixir

239

497 commits

updated Aug 28, 2026

See the code

README

ExGram

ExGram

Hex.pm Documentation Hex.pm Hex.pm Build Status

ExGram is a powerful Elixir library for building Telegram Bots. Use the low-level API for fine-grained control, or leverage the opinionated framework for rapid bot development.

Features

  • Automatic API Generation - Always up-to-date with the latest Telegram Bot API
  • Flexible DSL - Build responses elegantly with the context-based DSL
  • Polling & Webhooks - Choose the update method that fits your needs
  • Easy discovery of the API - Complete typespecs and documentation for all methods and models
  • Middleware System - Add authentication, logging, and custom processing
  • Message Entities Builder - Easily format messages without Markdown/HTML escaping
  • Multiple Bots - Run multiple different bots, or instances of the same bot in a single application
  • Production Ready - Battle-tested in real-world applications

Quick Start

Installation

Add to your mix.exs:

def deps do
  [
    {:ex_gram, "~> 0.70"},
    {:jason, "~> 1.4"},
    {:req, "~> 0.5"}  # HTTP adapter
  ]
end

Configure the adapter:

# config/config.exs
config :ex_gram, adapter: ExGram.Adapter.Req

Configure the formatter (Optional):

# .formatter.exs
[
    # ....
    import_deps: [:ex_gram] # Add :ex_gram here
]

Your First Bot

Generate a bot module:

mix bot.new

Add to your supervision tree:

# lib/my_app/application.ex
def start(_type, _args) do
  children = [
    ExGram,
    {MyApp.Bot, [method: :polling, token: "YOUR_BOT_TOKEN"]}
  ]

  Supervisor.start_link(children, strategy: :one_for_one)
end

Implement handlers:

defmodule MyApp.Bot do
  use ExGram.Bot, name: :my_bot, setup_commands: true

  import ExGram.Dsl.Keyboard

  command("start")
  command("help", description: "Show help")
  
  middleware(ExGram.Middleware.IgnoreUsername)  

  def handle({:command, :start, _}, context) do
    answer(context, "Welcome! I'm your bot.")
  end

  def handle({:command, :help, _}, context) do
    message = """
    Available commands:
    /start - Start the bot
    /help - Show this help
    """

    keyboard = 
      keyboard :inline do
        row do
          button "Test button", callback_data: "button"
        end
      end

    answer(context, message, reply_markup: keyboard)
  end

  def handle({:callback_query, %{data: "button"}}, context) do
    context
    |> answer_callback("Button clicked!")
    |> edit(:inline, "You clicked the button!")
  end
end

Run your bot:

mix run --no-halt

DSL Functions

The ExGram DSL builds actions on the context that execute after your handler returns.

Sending Messages

context 
|> answer("Hello!")
|> answer_document({:file, "/path/to/file.pdf"})

Inline Keyboards

  • With the DSL
import ExGram.Dsl.Keyboard

markup = 
  keyboard :inline do
    row do
      button "Button text", callback_data: "button"
    end
  end
  
answer(context, "Choose:", reply_markup: markup)
  • With a helper method
markup = create_inline_keyboard([
  [%{text: "Button", callback_data: "button"}]
])
answer(context, "Choose:", reply_markup: markup)

Editing & Deleting

edit(context, "Updated message")
edit_markup(context, new_keyboard)
delete(context)

Callback Queries & Inline Queries

answer_callback(context, "Processing...")
answer_inline_query(context, results)

Use steps results

context
|> answer("Important message!")
|> on_result(fn 
  {:ok, message}, name ->
    ExGram.pin_chat_message(message.chat.id, message.message_id, bot: name)
    
  error, _name ->
    error
end)

Extracting Information

extract_id(context)           # Chat ID
extract_user(context)         # User
extract_message_id(context)   # Message ID
extract_update_type(context)  # Update type
extract_message_type(context) # Message type

Low-Level API

Use ExGram as a library without the framework:

# Configure globally
config :ex_gram, token: "YOUR_TOKEN"

ExGram.send_photo(chat_id, {:file, "photo.jpg"})
ExGram.get_me()

# Or you can pass the token directly
ExGram.send_message(chat_id, "Hello!", token: "YOUR_TOKEN")

All methods return {:ok, result} | {:error, ExGram.Error.t()} and have bang variants:

{:ok, message} = ExGram.send_message(chat_id, "Hello")
message = ExGram.send_message!(chat_id, "Hello")  # Raises on error

See the Sending Messages guide for a complete reference or the Cheatsheet for a quick overview.

Documentation

Getting Started

Building Bots

Intermediate

Advanced

Deployment

  • Fly.io - Deploy your bot to production

Reference

  • Cheatsheet - Quick reference for common patterns
  • HexDocs - Complete API documentation

Ecosystem

Companion libraries that extend ExGram. If you have a library that extends ExGram feel free to open a discussion to add it here:

  • ExGram Router — A declarative routing DSL that replaces handle/2 pattern-match chains with composable scope/filter/handle blocks. Includes built-in filters for commands, text, callback queries, and more, plus mix tasks for inspecting your bot's route tree. See the Router guide.
  • ExGram FSM — Finite state machine conversation management with named flows, validated transitions, pluggable storage backends, and automatic ExGram.Router integration. See the FSM guide.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

Beer-Ware License. See LICENSE for details.

api
bot
elixir
elixir-lang
hacktoberfest
telegram
telegram-bot-api

Contributors

rockneurotiko

402 commits

Ironjanowar

25 commits

pxp9

22 commits

prtngn

17 commits

rockneurotiko/ex_gram

Telegram Bot API low level API and framework

Elixir

239

497 commits

updated Aug 28, 2026

See the code

README

ExGram

ExGram

Hex.pm Documentation Hex.pm Hex.pm Build Status

ExGram is a powerful Elixir library for building Telegram Bots. Use the low-level API for fine-grained control, or leverage the opinionated framework for rapid bot development.

Features

  • Automatic API Generation - Always up-to-date with the latest Telegram Bot API
  • Flexible DSL - Build responses elegantly with the context-based DSL
  • Polling & Webhooks - Choose the update method that fits your needs
  • Easy discovery of the API - Complete typespecs and documentation for all methods and models
  • Middleware System - Add authentication, logging, and custom processing
  • Message Entities Builder - Easily format messages without Markdown/HTML escaping
  • Multiple Bots - Run multiple different bots, or instances of the same bot in a single application
  • Production Ready - Battle-tested in real-world applications

Quick Start

Installation

Add to your mix.exs:

def deps do
  [
    {:ex_gram, "~> 0.70"},
    {:jason, "~> 1.4"},
    {:req, "~> 0.5"}  # HTTP adapter
  ]
end

Configure the adapter:

# config/config.exs
config :ex_gram, adapter: ExGram.Adapter.Req

Configure the formatter (Optional):

# .formatter.exs
[
    # ....
    import_deps: [:ex_gram] # Add :ex_gram here
]

Your First Bot

Generate a bot module:

mix bot.new

Add to your supervision tree:

# lib/my_app/application.ex
def start(_type, _args) do
  children = [
    ExGram,
    {MyApp.Bot, [method: :polling, token: "YOUR_BOT_TOKEN"]}
  ]

  Supervisor.start_link(children, strategy: :one_for_one)
end

Implement handlers:

defmodule MyApp.Bot do
  use ExGram.Bot, name: :my_bot, setup_commands: true

  import ExGram.Dsl.Keyboard

  command("start")
  command("help", description: "Show help")
  
  middleware(ExGram.Middleware.IgnoreUsername)  

  def handle({:command, :start, _}, context) do
    answer(context, "Welcome! I'm your bot.")
  end

  def handle({:command, :help, _}, context) do
    message = """
    Available commands:
    /start - Start the bot
    /help - Show this help
    """

    keyboard = 
      keyboard :inline do
        row do
          button "Test button", callback_data: "button"
        end
      end

    answer(context, message, reply_markup: keyboard)
  end

  def handle({:callback_query, %{data: "button"}}, context) do
    context
    |> answer_callback("Button clicked!")
    |> edit(:inline, "You clicked the button!")
  end
end

Run your bot:

mix run --no-halt

DSL Functions

The ExGram DSL builds actions on the context that execute after your handler returns.

Sending Messages

context 
|> answer("Hello!")
|> answer_document({:file, "/path/to/file.pdf"})

Inline Keyboards

  • With the DSL
import ExGram.Dsl.Keyboard

markup = 
  keyboard :inline do
    row do
      button "Button text", callback_data: "button"
    end
  end
  
answer(context, "Choose:", reply_markup: markup)
  • With a helper method
markup = create_inline_keyboard([
  [%{text: "Button", callback_data: "button"}]
])
answer(context, "Choose:", reply_markup: markup)

Editing & Deleting

edit(context, "Updated message")
edit_markup(context, new_keyboard)
delete(context)

Callback Queries & Inline Queries

answer_callback(context, "Processing...")
answer_inline_query(context, results)

Use steps results

context
|> answer("Important message!")
|> on_result(fn 
  {:ok, message}, name ->
    ExGram.pin_chat_message(message.chat.id, message.message_id, bot: name)
    
  error, _name ->
    error
end)

Extracting Information

extract_id(context)           # Chat ID
extract_user(context)         # User
extract_message_id(context)   # Message ID
extract_update_type(context)  # Update type
extract_message_type(context) # Message type

Low-Level API

Use ExGram as a library without the framework:

# Configure globally
config :ex_gram, token: "YOUR_TOKEN"

ExGram.send_photo(chat_id, {:file, "photo.jpg"})
ExGram.get_me()

# Or you can pass the token directly
ExGram.send_message(chat_id, "Hello!", token: "YOUR_TOKEN")

All methods return {:ok, result} | {:error, ExGram.Error.t()} and have bang variants:

{:ok, message} = ExGram.send_message(chat_id, "Hello")
message = ExGram.send_message!(chat_id, "Hello")  # Raises on error

See the Sending Messages guide for a complete reference or the Cheatsheet for a quick overview.

Documentation

Getting Started

Building Bots

Intermediate

Advanced

Deployment

  • Fly.io - Deploy your bot to production

Reference

  • Cheatsheet - Quick reference for common patterns
  • HexDocs - Complete API documentation

Ecosystem

Companion libraries that extend ExGram. If you have a library that extends ExGram feel free to open a discussion to add it here:

  • ExGram Router — A declarative routing DSL that replaces handle/2 pattern-match chains with composable scope/filter/handle blocks. Includes built-in filters for commands, text, callback queries, and more, plus mix tasks for inspecting your bot's route tree. See the Router guide.
  • ExGram FSM — Finite state machine conversation management with named flows, validated transitions, pluggable storage backends, and automatic ExGram.Router integration. See the FSM guide.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

Beer-Ware License. See LICENSE for details.

api
bot
elixir
elixir-lang
hacktoberfest
telegram
telegram-bot-api

Contributors

rockneurotiko

402 commits

Ironjanowar

25 commits

pxp9

22 commits

prtngn

17 commits

Languages

Elixir

98.7%

Python

1.3%