· 7 min read
Building a Practical Telegram + AI Bot: A Developer’s Tutorial
Learn how to wire Telegram updates to an AI model, manage conversation state, and return useful responses without turning your bot into a generic chatbot. This tutorial focuses on a production-friendly implementation you can adapt for support, summarization, and internal automation.
This tutorial shows how to build a Telegram bot that sends user messages to an AI model, returns a useful answer, and keeps enough context to feel coherent without becoming expensive or hard to maintain.
Instead of aiming for a “chat with everything” demo, we’ll build a bot with a clear shape:
- Telegram receives the message
- your backend normalizes and stores minimal context
- an AI model generates a response
- the bot sends the result back to the user
That architecture is useful for support bots, internal helpers, document Q&A, moderation assistants, and task automation.
What you will build
By the end, you’ll have a bot that can:
- respond to
/startand/help - process incoming text messages
- keep per-chat conversation state
- call an AI model with a structured prompt
- send the AI response back to Telegram
- handle timeouts and failures gracefully
We’ll use:
- Telegram Bot API for messaging
- Python for the backend
- FastAPI for a webhook endpoint
- OpenAI-style chat completion as the AI layer
You can swap Python for Node.js or any other stack. The important part is the flow and the state management.
Why webhook-based bots are better for AI workflows
For simple bots, long polling is fine. For AI-enabled bots, webhooks are often a better fit because:
- they reduce latency
- they scale better under bursty traffic
- they integrate more naturally with async workers
- they make it easier to enqueue AI work
If your bot may do more than one model call or may retrieve documents before answering, a webhook-based architecture gives you more room to grow.
High-level architecture
A practical AI bot usually has three layers:
1. Telegram transport
This layer receives updates from Telegram and sends messages back. It should stay thin.
2. Application logic
This layer decides what to do with each message: ignore, answer directly, fetch context, or route to a worker.
3. AI orchestration
This layer builds prompts, injects relevant context, calls the model, and post-processes the result.
A good rule: do not put business logic inside the prompt. Keep prompt content focused on generation, not control flow.
Step 1: Create the bot and set up Telegram
Create a bot with BotFather and save the token as an environment variable.
export TELEGRAM_BOT_TOKEN="123456:abc..."
export OPENAI_API_KEY="sk-..."
You’ll also need a public HTTPS endpoint for the webhook. During development, a tunnel such as ngrok can work, but production should use a real domain and TLS termination.
Step 2: Install dependencies
pip install fastapi uvicorn httpx python-dotenv
If you want a database, add your preferred driver later. For now, we’ll keep state in memory to make the example easy to follow.
Step 3: Build the webhook handler
Create main.py:
import os
import asyncio
from collections import defaultdict, deque
import httpx
from fastapi import FastAPI, Request, HTTPException
app = FastAPI()
TELEGRAM_BOT_TOKEN = os.environ["TELEGRAM_BOT_TOKEN"]
OPENAI_API_KEY = os.environ["OPENAI_API_KEY"]
TELEGRAM_API = f"https://api.telegram.org/bot{TELEGRAM_BOT_TOKEN}"
# Minimal in-memory conversation state.
# Replace with Redis or a database in production.
conversation_store = defaultdict(lambda: deque(maxlen=10))
async def send_message(chat_id: int, text: str):
payload = {
"chat_id": chat_id,
"text": text,
"parse_mode": "Markdown",
"disable_web_page_preview": True,
}
async with httpx.AsyncClient(timeout=10) as client:
response = await client.post(f"{TELEGRAM_API}/sendMessage", json=payload)
response.raise_for_status()
async def call_ai(messages):
# Replace this with your model provider of choice.
# The important part is the shape of the messages list.
headers = {
"Authorization": f"Bearer {OPENAI_API_KEY}",
"Content-Type": "application/json",
}
payload = {
"model": "gpt-4.1-mini",
"messages": messages,
"temperature": 0.2,
}
async with httpx.AsyncClient(timeout=30) as client:
response = await client.post(
"https://api.openai.com/v1/chat/completions",
headers=headers,
json=payload,
)
response.raise_for_status()
data = response.json()
return data["choices"][0]["message"]["content"]
def build_messages(chat_id: int, user_text: str):
history = list(conversation_store[chat_id])
system_prompt = {
"role": "system",
"content": (
"You are a concise Telegram assistant. "
"Answer directly, avoid long introductions, and ask a clarifying question only if needed. "
"If the user asks for code, provide production-oriented examples."
),
}
messages = [system_prompt]
messages.extend(history)
messages.append({"role": "user", "content": user_text})
return messages
@app.post("/telegram/webhook")
async def telegram_webhook(request: Request):
update = await request.json()
message = update.get("message")
if not message:
return {"ok": True}
chat = message.get("chat", {})
chat_id = chat.get("id")
text = message.get("text", "")
if not chat_id or not text:
return {"ok": True}
if text == "/start":
await send_message(chat_id, "Hi. Send me a question and I’ll answer with AI.")
return {"ok": True}
if text == "/help":
await send_message(chat_id, "Try asking for a summary, rewrite, extraction, or code suggestion.")
return {"ok": True}
# Store the user message before generating a response.
conversation_store[chat_id].append({"role": "user", "content": text})
try:
messages = build_messages(chat_id, text)
answer = await call_ai(messages)
# Store assistant response for future context.
conversation_store[chat_id].append({"role": "assistant", "content": answer})
await send_message(chat_id, answer)
except Exception as e:
await send_message(chat_id, "Sorry, I couldn’t generate a response right now.")
raise HTTPException(status_code=500, detail=str(e))
return {"ok": True}
Step 4: Run the app locally
uvicorn main:app --reload --port 8000
Then expose it:
ngrok http 8000
Set the Telegram webhook to your public HTTPS URL:
curl -X POST "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/setWebhook" \
-d "url=https://YOUR_PUBLIC_DOMAIN/telegram/webhook"
Step 5: Understand the message flow
When a user sends a message:
- Telegram POSTs the update to your webhook
- your handler extracts
chat_idand message text - your app builds a prompt from recent history
- the AI API returns a completion
- your bot sends the completion back to Telegram
That flow is simple, but there are a few important details.
Practical implementation details that matter
1. Keep message history short
Do not send the entire chat history on every request. It wastes tokens and increases latency. Keep a small sliding window, like the last 5–10 messages.
For many bots, recent context is enough.
If you need more memory, store summaries rather than raw history.
2. Use a structured system prompt
A good Telegram AI bot is not just “helpful.” It should be optimized for the use case.
Examples:
- support bot: prioritize troubleshooting steps and ask for error messages
- internal bot: answer in your team’s style and include links to internal docs
- summarizer bot: produce bullet points and action items
- code bot: keep answers implementation-oriented
Treat the system prompt as part of your product.
3. Separate transport errors from AI errors
Telegram delivery can fail independently of model calls.
Handle them separately:
- if Telegram send fails, retry or log the transport problem
- if the AI call fails, return a fallback message
- if the webhook receives invalid payloads, ignore them safely
4. Add typing feedback for long operations
If AI generation takes more than a second or two, send a chat action like typing before the model call.
async def send_chat_action(chat_id: int, action: str = "typing"):
payload = {"chat_id": chat_id, "action": action}
async with httpx.AsyncClient(timeout=10) as client:
await client.post(f"{TELEGRAM_API}/sendChatAction", json=payload)
Then call it before call_ai(...). This makes the bot feel responsive.
5. Watch your webhook time budget
Telegram expects webhook handlers to return quickly. If your model call may take too long, consider:
- acknowledging the update immediately
- pushing the AI work into a queue
- sending the result later
For a first version, direct handling is acceptable. For production, asynchronous job processing is safer.
Upgrading from in-memory state to Redis
The in-memory conversation_store works for a demo, but it resets on restart and fails with multiple workers.
A better pattern is Redis:
- key by
chat_id - store a JSON array of recent messages
- set a TTL if you want the context to expire
This gives you:
- persistence across restarts
- shared state across processes
- easy cleanup
Example conceptually:
# Pseudocode
history_key = f"chat:{chat_id}:history"
redis.lpush(history_key, message_json)
redis.ltrim(history_key, 0, 9)
redis.expire(history_key, 86400)
If your bot serves many chats, Redis is a strong default.
Handling commands cleanly
In real bots, Telegram commands are not just UI sugar. They are a control surface.
Useful commands include:
/resetto clear context/modeto switch behavior/summaryto summarize the current thread/exportto produce a structured report
For example, implement /reset like this:
if text == "/reset":
conversation_store.pop(chat_id, None)
await send_message(chat_id, "Context cleared.")
return {"ok": True}
This is especially useful because AI contexts can drift.
Prompting for Telegram-specific output
Telegram has its own formatting and UX constraints.
A useful bot should output:
- short paragraphs
- bullet points when appropriate
- limited markdown
- no massive blocks of text unless requested
Example system prompt upgrade:
system_prompt = {
"role": "system",
"content": (
"You are a Telegram assistant. Format responses for mobile screens. "
"Prefer short paragraphs and bullets. Avoid unnecessary verbosity. "
"When giving code, keep it focused and valid."
),
}
This is better than asking the model to be “helpful.”
Common failure modes and how to fix them
Model answers are too verbose
Fix:
- lower temperature
- tighten the system prompt
- ask for bullet points or direct answers
Bot repeats itself
Fix:
- reduce stored history
- deduplicate user messages
- ensure you are not appending the same text twice
Context becomes irrelevant
Fix:
- summarize older turns
- reset context on command
- scope the bot to one task
Telegram messages break formatting
Fix:
- escape Markdown carefully or use HTML mode
- avoid passing raw model output directly if it may contain broken markup
Bot feels slow
Fix:
- send typing indicators
- shorten prompts
- cache retrieval results
- move model calls to background workers
A more production-ready pattern
If you want to move beyond the tutorial version, here is a better architecture:
- webhook handler receives update
- validate and persist the event
- enqueue a job
- worker builds prompt and calls model
- worker stores result
- sender process posts result to Telegram
That separation gives you:
- retries
- observability
- easier scaling
- less risk of webhook timeouts
For many Telegram AI bots, this is the architecture that survives real traffic.
Security and abuse prevention
Telegram bots that call AI models can be abused quickly if you skip controls.
Add these protections:
- verify webhook source or use a secret path
- rate-limit per user or per chat
- reject oversized messages
- avoid sending sensitive user data to the model unless necessary
- keep API keys in environment variables or secret storage
If your bot handles internal data, be explicit about what is allowed to leave your system.
Example use cases
Here are a few practical bots you can build with this pattern:
Support triage bot
User sends a problem report, AI extracts:
- product area
- error details
- urgency
- likely next action
Meeting summary bot
Forward a discussion thread and generate:
- key decisions
- action items
- open questions
Internal knowledge bot
Search documents first, then let AI answer with citations.
Moderation assistant
Classify messages, flag spam, and propose moderation actions.
Each of these is more useful than a generic chat interface because the output is constrained and operational.
Final advice
The biggest mistake developers make with Telegram + AI is building a vague chatbot. A good Telegram AI integration has a job, a prompt style, and a narrow set of behaviors.
Start with a small, reliable workflow:
- one message in
- one useful answer out
- short memory
- clear fallback behavior
Once that works, add retrieval, summaries, queues, and commands.
If you treat Telegram as the transport layer and the model as a specialized reasoning component, you’ll end up with a bot that is fast enough, cheap enough, and actually useful.
Next steps
If you want to extend this tutorial, the best additions are:
- Redis-backed conversation history
- job queue for AI requests
- document retrieval before generation
- command routing by intent
- structured outputs with JSON schemas
Those upgrades turn a basic bot into a real assistant.