Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
b6ecfb0
feat(lenses): add Telegram Core API integration (lux-issue-62)
sirius-016 May 14, 2026
61962ae
feat(lenses): add Telegram Core API integration (lux-issue-62)
sirius-016 May 14, 2026
c8d709f
feat(lenses): add Telegram Core API integration (lux-issue-62)
sirius-016 May 14, 2026
0e8908a
feat(lenses): add Telegram Core API integration (lux-issue-62)
sirius-016 May 14, 2026
291cff0
feat(lenses): add Telegram Core API integration (lux-issue-62)
sirius-016 May 14, 2026
e13497b
feat(lenses): add Telegram Core API integration (lux-issue-62)
sirius-016 May 14, 2026
a125235
feat(lenses): add Telegram Core API integration (lux-issue-62)
sirius-016 May 14, 2026
57d6494
feat(lenses): add Telegram Core API integration (lux-issue-62)
sirius-016 May 14, 2026
23db7eb
feat(lenses): add Telegram Core API integration (lux-issue-62)
sirius-016 May 14, 2026
99b491b
feat: add lux/benchmarks/twitter_benchmark.exs
sirius-016 May 15, 2026
45b9eb1
feat: add lux/guides/twitter_integration.livemd
sirius-016 May 15, 2026
5cd3669
feat: add lux/lib/lux/integrations/twitter.ex
sirius-016 May 15, 2026
79fec4c
feat: add lux/lib/lux/integrations/twitter/client.ex
sirius-016 May 15, 2026
6deb941
feat: add lux/lib/lux/integrations/twitter/media.ex
sirius-016 May 15, 2026
3ed2ab8
feat: add lux/lib/lux/integrations/twitter/oauth.ex
sirius-016 May 15, 2026
74ce306
feat: add lux/lib/lux/integrations/twitter/rate_limiter.ex
sirius-016 May 15, 2026
87e5f99
feat: add lux/lib/lux/lenses/twitter/get_followers.ex
sirius-016 May 15, 2026
940bd44
feat: add lux/lib/lux/lenses/twitter/get_following.ex
sirius-016 May 15, 2026
0de8cc2
feat: add lux/lib/lux/lenses/twitter/get_mentions.ex
sirius-016 May 15, 2026
f5f1763
feat: add lux/lib/lux/lenses/twitter/get_timeline.ex
sirius-016 May 15, 2026
8689fc4
feat: add lux/lib/lux/lenses/twitter/get_tweet.ex
sirius-016 May 15, 2026
6f099aa
feat: add lux/lib/lux/lenses/twitter/get_user.ex
sirius-016 May 15, 2026
19cbb32
feat: add lux/lib/lux/lenses/twitter/search_tweets.ex
sirius-016 May 15, 2026
d82be6e
feat: add lux/lib/lux/prisms/twitter/tweets/bookmark.ex
sirius-016 May 15, 2026
962849a
feat: add lux/lib/lux/prisms/twitter/tweets/create_thread.ex
sirius-016 May 15, 2026
3fea875
feat: add lux/lib/lux/prisms/twitter/tweets/create_tweet.ex
sirius-016 May 15, 2026
6f8f290
feat: add lux/lib/lux/prisms/twitter/tweets/delete_tweet.ex
sirius-016 May 15, 2026
7828ed9
feat: add lux/lib/lux/prisms/twitter/tweets/like_tweet.ex
sirius-016 May 15, 2026
38a619c
feat: add lux/lib/lux/prisms/twitter/tweets/quote_tweet.ex
sirius-016 May 15, 2026
5e49590
feat: add lux/lib/lux/prisms/twitter/tweets/retweet.ex
sirius-016 May 15, 2026
ec461c5
feat: add lux/lib/lux/prisms/twitter/users/block_user.ex
sirius-016 May 15, 2026
c68bed2
feat: add lux/lib/lux/prisms/twitter/users/follow_user.ex
sirius-016 May 15, 2026
26e0b2d
feat: add lux/lib/lux/prisms/twitter/users/mute_user.ex
sirius-016 May 15, 2026
311d351
feat: add lux/test/unit/lux/integrations/twitter/client_test.exs
sirius-016 May 15, 2026
e9e9247
feat: add lux/test/unit/lux/integrations/twitter/media_test.exs
sirius-016 May 15, 2026
6ae042e
feat: add lux/test/unit/lux/integrations/twitter/oauth_test.exs
sirius-016 May 15, 2026
287e9fc
feat: add lux/test/unit/lux/integrations/twitter/rate_limiter_test.exs
sirius-016 May 15, 2026
420c5d0
feat: add lux/test/unit/lux/lenses/twitter_test.exs
sirius-016 May 15, 2026
16eb3d2
feat: add lux/test/unit/lux/prisms/twitter_test.exs
sirius-016 May 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
322 changes: 322 additions & 0 deletions guides/lenses/telegram_integration.livemd
Original file line number Diff line number Diff line change
@@ -0,0 +1,322 @@
# Telegram Integration Guide

Welcome to the Lux Telegram Bot API Lens! This guide covers everything you need to know to build powerful Telegram bots with Lux.

## Overview

`Lux.Lenses.TelegramLens` provides a complete, type-safe interface to the Telegram Bot API. It handles:

- **All core Bot API methods** (messages, media, chat management)
- **Rate limiting** with token bucket queue (30 msg/sec global limit)
- **Webhook & polling** update handlers
- **Inline & custom keyboards**
- **Error handling** with automatic retries
- **Media uploads** (photos, documents, voice, video)

## Setup

### 1. Get Your Bot Token

Start a chat with [@BotFather](https://t.me/botfather) on Telegram:

1. Send `/newbot`
2. Follow the prompts to name your bot
3. Copy the token you receive

### 2. Configure the Token

**Option A: Environment variable**

```bash
export TELEGRAM_BOT_TOKEN="your_token_here"
```

**Option B: Elixir config**

```elixir
# config/config.exs
config :lux, :telegram_token, "your_token_here"
```

## Quick Start

### Send a Message

```elixir
alias Lux.Lenses.TelegramLens

# Basic message
{:ok, message} = TelegramLens.send_message(123_456_789, "Hello from Lux!")

# With HTML formatting
{:ok, message} = TelegramLens.send_message(123_456_789, "<b>Bold</b> and <i>italic</i>",
parse_mode: "HTML"
)

# Disable notification
{:ok, message} = TelegramLens.send_message(123_456_789, "Silent message",
disable_notification: true
)
```

### Send Media

```elixir
# Send a photo
{:ok, message} = TelegramLens.send_photo(chat_id, "/path/to/photo.jpg",
caption: "A beautiful sunset"
)

# Send a document
{:ok, message} = TelegramLens.send_document(chat_id, "/path/to/report.pdf")

# Send voice message
{:ok, message} = TelegramLens.send_voice(chat_id, "/path/to/audio.ogg",
duration: 30
)

# Send video
{:ok, message} = TelegramLens.send_video(chat_id, "/path/to/video.mp4",
supports_streaming: true
)
```

### Inline Keyboards

```elixir
alias Lux.Lenses.TelegramLens

# Create inline keyboard
keyboard = TelegramLens.inline_keyboard([
[
TelegramLens.button("Option A", "choice_a"),
TelegramLens.button("Option B", "choice_b")
],
[
TelegramLens.button("Visit Website", nil, url: "https://example.com")
]
])

# Send with keyboard
{:ok, message} = TelegramLens.send_message(chat_id, "Choose an option:",
reply_markup: keyboard
)
```

### Message Editing & Deletion

```elixir
# Edit a message
{:ok, message} = TelegramLens.edit_message_text(chat_id, message_id, nil,
"Updated text here",
parse_mode: "HTML"
)

# Delete a message
:ok = TelegramLens.delete_message(chat_id, message_id)
```

### Polls

```elixir
# Regular anonymous poll
{:ok, message} = TelegramLens.send_poll(chat_id, "Favorite programming language?",
["Elixir", "Rust", "Python", "Go"]
)

# Quiz with correct answer
{:ok, message} = TelegramLens.send_poll(chat_id, "What is 2 + 2?",
["3", "4", "5"],
type: "quiz",
correct_option_id: 1,
explanation: "Basic arithmetic!"
)

# Close a poll
{:ok, poll} = TelegramLens.close_poll(chat_id, message_id)
```

## Receiving Updates

### Option 1: Long Polling

```elixir
defmodule MyBot do
def handle_update(update) do
if message = update["message"] do
chat_id = message["chat"]["id"]
text = message["text"]

case text do
"/start" ->
TelegramLens.send_message(chat_id, "Welcome! Type /help for commands.")

"/help" ->
TelegramLens.send_message(chat_id, "Available commands: /start, /help")

_ ->
TelegramLens.send_message(chat_id, "You said: #{text}")
end
end

:acknowledge
end
end

# Start polling
{:ok, pid} = Lux.Lenses.TelegramLens.Polling.start_link(
handler: {MyBot, :handle_update},
timeout: 30
)
```

### Option 2: Webhook

```elixir
# In your Phoenix/Raxx endpoint:

plug Lux.Lenses.TelegramLens.Webhook,
handler: {MyBot, :handle_update},
secret: System.get_env("TELEGRAM_WEBHOOK_SECRET")

# Set the webhook
:ok = TelegramLens.set_webhook("https://myapp.com/telegram/webhook",
max_connections: 40,
allowed_updates: ["message", "callback_query"]
)
```

### Handling Callback Queries

```elixir
defmodule MyBot do
def handle_update(%{"callback_query" => query}) do
data = query["data"]
message = query["message"]

case data do
"choice_a" ->
TelegramLens.send_message(message["chat"]["id"], "You chose A!")

"choice_b" ->
TelegramLens.send_message(message["chat"]["id"], "You chose B!")
end

# Always answer callback queries to remove loading state
TelegramLens.answer_callback_query(query["id"])

:acknowledge
end
end
```

## Rate Limiting

Telegram limits bots to ~30 messages per second globally. Lux handles this automatically:

- **Write operations** (sendMessage, sendPhoto, etc.) are rate-limited
- **Read operations** (getMe, getChat, getUpdates) bypass the limiter
- Requests are queued with a 35-second timeout
- 429 errors are automatically retried after `retry_after`

```elixir
# Burst send messages (will queue automatically)
for i <- 1..50 do
Task.async(fn ->
TelegramLens.send_message(chat_id, "Message #{i}")
end)
end
|> Task.await_many()
```

## Error Handling

All functions return `{:ok, result}` or `{:error, reason}`:

```elixir
case TelegramLens.send_message(chat_id, text) do
{:ok, message} ->
IO.puts("Sent: #{message["message_id"]}")

{:error, {:telegram_error, code, desc}} ->
IO.puts("Telegram error #{code}: #{desc}")

{:error, :rate_limited} ->
IO.puts("Rate limited, try again later")

{:error, reason} ->
IO.puts("Network error: #{inspect(reason)}")
end
```

## Type Reference

### Keyboard Buttons

```elixir
# Callback button
TelegramLens.button("Click me", "callback_data")

# URL button
TelegramLens.button("Visit", nil, url: "https://example.com")

# Switch inline query
TelegramLens.button("Search", nil, switch_inline_query: "query")
```

### Inline Keyboard

```elixir
keyboard = TelegramLens.inline_keyboard([
# Row 1
[
TelegramLens.button("A", "a"),
TelegramLens.button("B", "b")
],
# Row 2
[
TelegramLens.button("Link", nil, url: "https://example.com")
]
])
```

## Testing

```elixir
# In your test file:
use ExUnit.Case, async: true

test "sends message" do
Req.Test.expect(Lux.Lens, fn conn ->
{:ok, body, _} = Plug.Conn.read_body(conn)
assert body["chat_id"] == 123
assert body["text"] == "Hello"
Plug.Conn.resp(conn, 200, ~s({"ok": true, "result": {"message_id": 1}}))
end)

assert {:ok, %{"message_id" => 1}} = TelegramLens.send_message(123, "Hello")
end
```

## Performance

The lens is designed for high throughput:

- **Concurrent requests**: Use `Task.async_stream/3` for parallel sends
- **Batched media**: Upload photos/documents in parallel, they each consume 1 token
- **Webhook preferred**: For >1000 messages/hour, use webhooks (no polling overhead)
- **Queue timeout**: 35 seconds max wait for rate limit tokens

## Configuration Options

```elixir
# Rate limiter settings
config :lux, :telegram_rate_limiter,
bucket_size: 30,
refill_rate: 30,
queue_timeout: 35_000

# Webhook secret
config :lux, :telegram,
webhook_secret: "your_secret"
```
Loading