# TelegramEx
Elixir library for building Telegram bots. Provides a simple interface for handling messages, callbacks, and inline queries with automatic polling.
## Why This Library Exists
I decided to create this library because I couldn't find anything in the existing Elixir ecosystem that I liked. Maybe I just didn't search well enough, but still.
What makes this library different? I like the macro-based implementation, similar to how `GenServer` works. It feels like the right approach for this kind of library, and I think others might appreciate it too.
## Usage
Add the bot to your application's supervision tree:
```elixir
defmodule MyApp.Application do
def start(_type, _args) do
children = [MyBot]
Supervisor.start_link(children, strategy: :one_for_one)
end
end
```
Create a bot module:
```elixir
defmodule MyBot do
use TelegramEx
def handle_message(message) do
# Handle incoming messages
end
def handle_callback(callback) do
# Handle callback queries
:ok
end
end
```
Add your bot token to `config/runtime.exs`:
```elixir
import Config
config :telegram_ex,
name: "my_bot",
token: System.fetch_env!("TELEGRAM_BOT_TOKEN")
```
## Sending Messages
Messages are sent using the `TelegramEx.Builder.Message` module:
```elixir
defmodule MyBot do
use TelegramEx
def handle_message(%{chat: chat}) do
Message.text("Hello!")
|> Message.send(chat["id"])
end
end
```
### Message Builder Functions
- `Message.text(text)` - Create a text message
- `Message.text(text, parse_mode)` - Create a text message with parse mode (e.g., "Markdown", "HTML")
- `Message.inline_keyboard(message, keyboard)` - Add inline keyboard
- `Message.reply_keyboard(message, keyboard, opts)` - Add reply keyboard with options
- `Message.remove_keyboard(message)` - Remove custom keyboard
- `Message.silent(message)` - Send without notification
- `Message.answer_callback_query(message, callback)` - Answer callback query
- `Message.send(message, chat_id)` - Send the message
## Sending Photos
Use `TelegramEx.Builder.Photo` to send images:
```elixir
defmodule MyBot do
use TelegramEx
def handle_message(%{chat: chat}) do
Photo.path("/path/to/image.jpg")
|> Photo.caption("Here's a photo!")
|> Photo.send(chat["id"])
end
end
```
### Photo Builder Functions
- `Photo.url(url)` - Send photo from URL
- `Photo.path(path)` - Send photo from local file path
- `Photo.caption(photo, caption)` - Add caption to photo
- `Photo.caption(photo, caption, parse_mode)` - Add caption with parse mode
- `Photo.silent(photo)` - Send without notification
- `Photo.send(photo, chat_id)` - Send the photo
## Sending Documents
Use `TelegramEx.Builder.Document` to send files:
```elixir
defmodule MyBot do
use TelegramEx
def handle_message(%{chat: chat}) do
Document.path("/path/to/file.pdf")
|> Document.caption("Here's the document")
|> Document.send(chat["id"])
end
end
```
### Document Builder Functions
- `Document.url(url)` - Send document from URL
- `Document.path(path)` - Send document from local file path
- `Document.caption(document, caption)` - Add caption to document
- `Document.caption(document, caption, parse_mode)` - Add caption with parse mode
- `Document.silent(document)` - Send without notification
- `Document.send(document, chat_id)` - Send the document
### Keyboard Examples
**Inline Keyboard:**
```elixir
def handle_message(%{chat: chat}) do
keyboard = [[
%{text: "Button 1", callback_data: "btn_1"},
%{text: "Button 2", callback_data: "btn_2"}
]]
Message.text("Choose an option:", "Markdown")
|> Message.inline_keyboard(keyboard)
|> Message.send(chat["id"])
end
```
**Reply Keyboard:**
```elixir
def handle_message(%{chat: chat}) do
keyboard = [["/help", "/settings"], ["Contact"]]
Message.text("Use the buttons below:")
|> Message.reply_keyboard(keyboard, resize_keyboard: true, one_time_keyboard: true)
|> Message.send(chat["id"])
end
```
**Reply Keyboard Options:**
- `resize_keyboard: true` - Request clients to resize the keyboard
- `one_time_keyboard: true` - Hide keyboard after first use
## Handling Callback Queries
When a user presses an inline keyboard button, `handle_callback/1` is called:
```elixir
def handle_callback(%{data: "btn_1"} = callback) do
# Handle button 1 press
end
def handle_callback(%{data: "btn_2"} = callback) do
# Handle button 2 press
end
```
### Answering Callback Queries
To show an alert or update the user after a callback:
```elixir
def handle_callback(%{data: data, chat: chat} = callback) do
Message.text("Processed: #{data}")
|> Message.answer_callback_query(callback)
|> Message.send(chat["id"])
end
# Or simply answer callback without sending message
def handle_callback(callback) do
Message.answer_callback_query(callback)
end
```
**Callback Query Structure:**
- `:id` - Unique identifier for the callback query
- `:from` - User who triggered the callback (map with string keys)
- `:message` - The message the callback was attached to
- `:inline_message_id` - Identifier of the inline message (if applicable)
- `:chat_instance` - Global identifier for the chat
- `:data` - Data associated with the callback button
## Message Structure
The `handle_message/1` callback receives a `%TelegramEx.Types.Message{}` struct with the following fields:
- `:message_id` - Unique message identifier
- `:from` - Sender information (map with string keys)
- `:chat` - Chat information (map with string keys)
- `:date` - Message date as Unix timestamp
- `:text` - Message text content
- `:photo` - Photo attachments (if any)
- `:document` - Document attachment (if any)
- `:sticker` - Sticker (if any)
- `:video` - Video (if any)
- `:voice` - Voice message (if any)
- `:caption` - Caption for media
## Examples
### Echo Bot
```elixir
defmodule EchoBot do
use TelegramEx
def handle_message(%{text: text, chat: chat}) do
Message.text("Echo: #{text}")
|> Message.send(chat["id"])
end
end
```
### Command Handling
```elixir
defmodule MyBot do
use TelegramEx
def handle_message(%{text: "/start", chat: chat}) do
Message.text("Welcome! Send me any message.")
|> Message.send(chat["id"])
end
def handle_message(%{text: text, chat: chat}) do
Message.text("Echo: #{text}")
|> Message.send(chat["id"])
end
end
```