Routers split a bot into smaller handler modules. They are useful when a bot has separate areas: admin commands, surveys, onboarding, reports, or integrations.
Defining a Router
defmodule MyApp.AdminRouter do
use TelegramEx.Router
def handle_message(%{text: "/admin", chat: chat}, ctx) do
ctx
|> Message.text("Admin mode")
|> Message.send(chat["id"])
end
endRegister routers in the main bot:
defmodule MyBot do
use TelegramEx,
name: :my_bot,
routers: [MyApp.AdminRouter]
endDispatch Chain
For each update, TelegramEx tries modules in this order:
- router modules, left to right
- the main bot module
Routers get default fallback handlers:
def handle_message(_message, _ctx), do: :pass
def handle_callback(_callback, _ctx), do: :pass:pass means "try the next router". Any other return value stops dispatch.
This makes router order meaningful:
use TelegramEx,
name: :my_bot,
routers: [MyApp.AdminRouter, MyApp.PublicRouter]If AdminRouter handles an update, PublicRouter will not see it. Keep broad
fallback clauses near the end of a router or in the main bot.
Routers with FSM
Routers can define state-specific handlers:
defmodule MyApp.SurveyRouter do
use TelegramEx.Router
defstate :survey_name do
def handle_message(%{text: name, chat: chat}, ctx) do
ctx
|> Message.text("How old are you?")
|> Message.send(chat["id"])
{:transition, :survey_age, %{name: name}}
end
end
defstate :survey_age do
def handle_message(%{text: age, chat: chat}, ctx) do
ctx
|> Message.text("Saved #{ctx.data.name}, age #{age}.")
|> Message.send(chat["id"])
FSM.reset_state(:my_bot, chat["id"])
end
end
endThe main bot can start the flow:
def handle_message(%{text: "/survey", chat: chat}, ctx) do
ctx
|> Message.text("What is your name?")
|> Message.send(chat["id"])
{:transition, :survey_name, %{}}
endRouters with Commands
Routers can define commands with defcommand/3:
defmodule MyApp.CommandRouter do
use TelegramEx.Router
defcommand "echo", description: "Echo arguments", bind: [:ctx, :message, :args] do
ctx
|> Message.text(Enum.join(args, " "))
|> Message.send(message.chat["id"])
end
endCommands from routers are included when TelegramEx registers commands with Telegram. See Commands for how this metadata is collected.
When to Introduce Routers
Start with one bot module for small bots. Add routers when:
- one file starts mixing unrelated workflows
- a group of handlers shares an FSM flow
- a feature has its own callbacks and commands
- you want to test or reason about a feature independently
Routers are organizational. They do not create separate Telegram bots or separate polling processes.