Packages
ex_gram
0.65.0
0.67.0
0.66.0
0.65.0
0.64.0
0.63.0
0.62.0
0.61.0
0.60.0
0.58.0
0.57.0
0.56.1
0.56.0
0.55.1
0.55.0
0.54.0
0.53.0
0.52.2
0.52.1
0.52.0
0.51.1
0.51.0
0.50.2
0.50.1
0.50.0
0.41.0
0.40.0
0.34.0
0.33.0
0.32.0
0.31.0
0.30.0
0.29.0
0.28.0
0.27.0
0.26.0
0.25.0
0.24.1
0.24.0
0.23.0
0.22.0
0.21.0
0.20.0
0.15.0
0.14.0
0.13.0
0.12.0
0.11.0
0.10.0
0.9.0
0.8.1
0.8.0
0.7.1
0.7.0
0.6.2
0.6.1
0.6.0
0.5.0
0.5.0-rc6
0.5.0-rc5
0.5.0-rc4
0.5.0-rc3
0.5.0-rc2
Telegram Bot API low level and framework
Current section
Files
Jump to
Current section
Files
guides/handling-updates.md
# Handling Updates
This guide explains how to handle different types of updates from Telegram in your ExGram bot.
## The `handle/2` Function
Every bot must implement the `c:ExGram.Handler.handle/2` function. It receives:
1. **Update tuple** - Different tuple patterns for different update types
2. **Context** - A `t:ExGram.Cnt.t/0` struct with update information
```elixir
def handle(update_tuple, context) do
# Process the update and return context
context
end
```
The context contains:
- `update` - The full [Update](https://core.telegram.org/bots/api#update) object
- `name` - Your bot's name (important for multiple bots)
- `bot_info` - You bot's information, extracted with `ExGram.get_me/1` at startup
- `extra` - Custom data from middlewares
- Internal fields used by ExGram
## Update Patterns
ExGram parses updates into convenient tuples for pattern matching.
### Commands
Matches messages starting with `/command` or `/command@your_bot`.
```elixir
def handle({:command, "start", msg}, context) do
answer(context, "Welcome! You sent: #{msg}")
end
def handle({:command, "help", _msg}, context) do
answer(context, """
Available commands:
/start - Start the bot
/help - Show this help
/settings - Configure settings
""")
end
```
The `msg` parameter contains any text after the command:
- `/start` → `msg = ""`
- `/start hello world` → `msg = "hello world"`
You can also declare commands at the module level:
```elixir
command("start")
command("help", description: "Show help message")
command("settings", description: "Configure your settings")
```
With `setup_commands: true`, these are automatically registered with Telegram.
And, once you declare them, you will receive the commands as atoms:
```elixir
def handle({:command, :start, msg}, context) do
# ...
end
def handle({:command, :help, _msg}, context)
def handle({:command, :settings, _msg}, context)
# You can still handle not defined commands
def handle({:command, "othercommand", _msg}, context)
```
This is really important if you want to provide command translations or commands for different roles.
Check the [commands guide](./commands.md) if you want to know more.
### Plain Text
Matches regular text messages (respects [privacy mode](https://core.telegram.org/bots#privacy-mode)).
```elixir
def handle({:text, text, message}, context) do
cond do
String.contains?(text, "hello") ->
answer(context, "Hello to you too!")
String.length(text) > 100 ->
answer(context, "That's a long message!")
true ->
answer(context, "You said: #{text}")
end
end
```
### Regex Patterns
Define regex patterns at module level and match against them:
```elixir
defmodule MyBot.Bot do
use ExGram.Bot, name: :my_bot
# Define regex patterns
regex(:email, ~r/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/)
regex(:phone, ~r/\b\d{3}[-.]?\d{3}[-.]?\d{4}\b/)
def handle({:regex, :email, message}, context) do
answer(context, "I detected an email address in your message!")
end
def handle({:regex, :phone, message}, context) do
answer(context, "That looks like a phone number!")
end
end
```
### Callback Queries
Handles button presses from inline keyboards.
```elixir
def handle({:callback_query, %{data: "button_" <> id} = callback}, context) do
context
|> answer_callback("Processing button #{id}")
|> answer("You clicked button #{id}")
end
def handle({:callback_query, %{data: "delete"}}, context) do
context
|> answer_callback("Deleting message...")
|> delete()
end
```
See [Sending Messages](sending-messages.md) for creating inline keyboards.
### Inline Queries
Handles inline queries (e.g., `@yourbot search term`).
```elixir
def handle({:inline_query, query}, context) do
results = [
%{
type: "article",
id: "1",
title: "Result 1",
input_message_content: %{message_text: "You selected result 1"}
},
%{
type: "article",
id: "2",
title: "Result 2",
input_message_content: %{message_text: "You selected result 2"}
}
]
answer_inline_query(context, results)
end
```
### Location Messages
Handles location sharing.
```elixir
def handle({:location, %ExGram.Model.Location{latitude: lat, longitude: lon}}, context) do
answer(context, "You're at #{lat}, #{lon}. Thanks for sharing!")
end
```
### Edited Messages
Handles message edits.
```elixir
def handle({:edited_message, edited_msg}, context) do
# You can choose to process edited messages differently
# or ignore them entirely
Logger.info("Message #{edited_msg.message_id} was edited")
context
end
```
### Generic Message Handler
Catches any message that doesn't match other patterns.
```elixir
def handle({:message, message}, context) do
cond do
message.photo ->
answer(context, "Nice photo!")
message.document ->
answer(context, "Thanks for the document!")
message.sticker ->
answer(context, "Cool sticker!")
message.voice ->
answer(context, "I received your voice message!")
true ->
answer(context, "I received your message, but I'm not sure what to do with it.")
end
end
```
### Default Handler
Catches all other updates.
ExGram will slowly add more specific handlers to make it easier to differentiate all the possible update types.
```elixir
def handle({:update, update}, context) do
Logger.debug("Received unhandled update: #{inspect(update)}")
context
end
```
## The Context (`t:ExGram.Cnt.t/0`)
The context struct contains:
```elixir
%ExGram.Cnt{
update: %ExGram.Model.Update{}, # Full Telegram update, useful to get more information about the update in specific handlers
name: :my_bot, # Your bot's name (the one from "use ExGram.Bot, name: :my_bot")
bot_info: %ExGram.Model.User{} | nil, # The bot's information, extracted with ExGram.get_me at bot's startup
extra: %{} # Custom data from middlewares
# More fields used internally
}
```
### Adding Extra Data
Middlewares can add custom data to `context.extra`:
```elixir
# In a middleware
use ExGram.Middleware
def call(context, _opts) do
user_id = extract_id(context)
extra_data = %{user_role: fetch_user_role(user_id)}
add_extra(context, extra_data)
end
# In your handler
def handle({:command, "admin", _msg}, context) do
case context.extra[:user_role] do
:admin -> answer(context, "Admin panel: ...")
_ -> answer(context, "Access denied")
end
end
```
Read more about middlewares in [this guide](./middlewares.md)
## The `c:ExGram.Handler.init/1` Callback
The optional `c:ExGram.Handler.init/1` callback runs once before processing updates. Use it to initialize your bot:
```elixir
def init(opts) do
# opts contains [:bot, :token]
ExGram.set_my_description!(
description: "This bot helps you manage tasks",
bot: opts[:bot]
)
ExGram.set_my_name!(
name: "TaskBot",
token: opts[:token]
)
# Do some logic you need before starting your bots
# MyBot.notify_admins_restart(opts[:bot])
:ok
end
```
**Note:** If you use `setup_commands: true`, commands are automatically registered. Use `init/1` for additional setup.
## Pattern Matching Tips
### Multiple Clauses
Use multiple function clauses for clean code:
```elixir
def handle({:command, :start, _}, context), do: answer(context, "Welcome!")
def handle({:command, :help, _}, context), do: show_help(context)
def handle({:command, :about, _}, context), do: show_about(context)
def handle({:callback_query, %{data: "yes"}}, context) do
answer_callback(context, "You chose yes!")
end
def handle({:callback_query, %{data: "no"}}, context) do
answer_callback(context, "You chose no!")
end
def handle({:text, text, _msg}, context) when is_binary(text) do
answer(context, "Echo: #{text}")
end
def handle(_update, context), do: context
```
### Guards
Use guards for additional filtering:
```elixir
def handle({:text, text, _msg}, context) when byte_size(text) > 500 do
answer(context, "Please send shorter messages (max 500 characters)")
end
def handle({:text, text, _msg}, context) when text in ["hi", "hello", "hey"] do
answer(context, "Hello there!")
end
```
### Extracting Data
Pattern match to extract specific fields:
```elixir
def handle({:message, %{from: %{id: user_id, username: username}}}, context) do
answer(context, "Hello @#{username} (ID: #{user_id})")
end
def handle({:callback_query, %{from: user, data: data}}, context) do
Logger.info("User #{user.id} clicked: #{data}")
answer_callback(context, "Got it!")
end
```
## Next Steps
- [Sending Messages](sending-messages.md) - Learn the DSL for building responses
- [Message Entities](message-entities.md) - Format messages without Markdown or HTML
- [Middlewares](middlewares.md) - Add preprocessing logic to your bot
- [Low-Level API](low-level-api.md) - Direct API calls for complex scenarios
- [Cheatsheet](cheatsheet.md) - Quick reference for all patterns