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