Telegram
Connect to Telegram with support for groups, channels, inline keyboards, and a polling fallback for local development.
Install
pnpm add @chat-adapter/telegramQuick start
The adapter auto-detects TELEGRAM_BOT_TOKEN, TELEGRAM_WEBHOOK_SECRET_TOKEN, and TELEGRAM_BOT_USERNAME from the environment.
import { Chat } from "chat";
import { createTelegramAdapter } from "@chat-adapter/telegram";
const bot = new Chat({
userName: "mybot",
adapters: {
telegram: createTelegramAdapter(),
},
});
bot.onNewMention(async (thread, message) => {
await thread.post(`You said: ${message.text}`);
});import { bot } from "@/lib/bot";
export async function POST(request: Request): Promise<Response> {
return bot.webhooks.telegram(request);
}Configure your bot webhook in BotFather / via the Telegram API:
curl -X POST "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/setWebhook" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-domain.com/api/webhooks/telegram",
"secret_token": "your-secret-token"
}'Configuration
Prop
Type
botToken is required — either via config or env var.
Authentication
Create a bot via BotFather:
- Send
/newbotand follow the prompts. - Copy the token to
TELEGRAM_BOT_TOKEN. - Optionally pick a username and copy it to
TELEGRAM_BOT_USERNAME.
Advanced
Polling for local development
import { Chat } from "chat";
import { createTelegramAdapter } from "@chat-adapter/telegram";
import { createMemoryState } from "@chat-adapter/state-memory";
const telegram = createTelegramAdapter({
mode: "polling",
});
const bot = new Chat({
userName: "mybot",
adapters: { telegram },
state: createMemoryState(),
});Polling and webhooks are mutually exclusive in Telegram. mode: "polling" deletes the webhook by default before calling getUpdates.
Auto mode
mode: "auto" (the default) checks getWebhookInfo: if a webhook URL is set, it uses webhook mode; otherwise it falls back to polling on long-running runtimes. If getWebhookInfo fails, the adapter stays in webhook mode (safe fallback).
const telegram = createTelegramAdapter({ mode: "auto" });
void bot.initialize();
console.log(telegram.runtimeMode); // "webhook" | "polling"Markdown formatting
Outbound messages are sent with Telegram's MarkdownV2 parse mode. The adapter walks the markdown AST and emits MarkdownV2 with context-aware escaping (text vs. code blocks vs. link URLs), so you author standard markdown and the adapter handles every reserved character.
Pass { raw: "..." } only if you need to ship a fully pre-escaped MarkdownV2 string.
Notes
- Telegram does not expose full historical message APIs to bots.
fetchMessagesreturns adapter-cached messages from the current process. listThreadsis not available for Telegram chats.- Telegram callback data is limited to 64 bytes — keep
Buttonid/valuepayloads short. - Other rich card elements (images, select menus, radios) render as fallback text.
Feature support
Messaging
| Feature | Supported |
|---|---|
| Post message | |
| Edit message | |
| Delete message | |
| File uploads | Single file |
| Streaming | Post+Edit |
| Scheduled messages |
Rich content
| Feature | Supported |
|---|---|
| Card format | MarkdownV2 + inline keyboard |
| Buttons | Inline keyboard |
| Link buttons | Inline keyboard URLs |
| Select menus | |
| Tables | ASCII |
| Fields | |
| Images in cards | |
| Modals |
Conversations
| Feature | Supported |
|---|---|
| Slash commands | |
| Mentions | |
| Add reactions | |
| Remove reactions | |
| Typing indicator | |
| DMs | |
| Ephemeral messages | |
| User lookup | Seen users |
| Parent subject | |
| Native client | |
| Custom API endpoint |
Message history
| Feature | Supported |
|---|---|
| Fetch messages | Cached |
| Fetch single message | Cached |
| Fetch thread info | |
| Fetch channel messages | Cached |
| List threads | |
| Fetch channel info | |
| Post channel message |