# 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