A Telegram bot powered by Claude gives you a personal AI assistant accessible from your phone. In this tutorial you’ll build one from scratch — handling messages, keeping conversation history, and deploying it.
Prerequisites
- Python 3.10+
- Anthropic API key
- Telegram account
Step 1: Create a Telegram Bot
1. Open Telegram, search for @BotFather 2. Send /newbot and follow the prompts 3. Copy the bot token (looks like 7123456789:AAF...)
Step 2: Install Dependencies
pip install python-telegram-bot anthropic
Step 3: Basic Echo Bot
from telegram import Update
from telegram.ext import Application, MessageHandler, filters, ContextTypes
BOT_TOKEN = "YOUR_TELEGRAM_BOT_TOKEN"
async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE): text = update.message.text await update.message.reply_text(f"You said: {text}")
app = Application.builder().token(BOT_TOKEN).build() app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_message)) app.run_polling()
Test it first — make sure messages echo back before adding Claude.
Step 4: Add Claude
from telegram import Update
from telegram.ext import Application, MessageHandler, CommandHandler, filters, ContextTypes
import anthropic
BOT_TOKEN = "YOUR_TELEGRAM_BOT_TOKEN" claude = anthropic.Anthropic()
conversations: dict[int, list] = {}
async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE): chat_id = update.effective_chat.id user_text = update.message.text
if chat_id not in conversations: conversations[chat_id] = [] conversations[chat_id].append({"role": "user", "content": user_text})
history = conversations[chat_id][-20:]
response = claude.messages.create( model="claude-sonnet-4-6", max_tokens=1024, system="You are a helpful assistant. Be concise — this is a Telegram chat.", messages=history, )
reply = response.content[0].text conversations[chat_id].append({"role": "assistant", "content": reply}) await update.message.reply_text(reply)
async def reset_command(update: Update, context: ContextTypes.DEFAULT_TYPE): conversations.pop(update.effective_chat.id, None) await update.message.reply_text("Conversation reset.")
app = Application.builder().token(BOT_TOKEN).build() app.add_handler(CommandHandler("reset", reset_command)) app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_message)) app.run_polling()
Step 5: Add Typing Indicator
async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE):
await context.bot.send_chat_action(chat_id=update.effective_chat.id, action="typing")
# ... rest of handler
Step 6: Handle Long Responses
Telegram has a 4096-character limit. Split long responses:
def split_message(text: str, limit: int = 4000) -> list[str]:
if len(text) <= limit:
return [text]
parts = []
while text:
parts.append(text[:limit])
text = text[limit:]
return parts
In your handler:
for part in split_message(reply):
await update.message.reply_text(part)
Step 7: Deploy on a VPS
# /etc/systemd/system/claude-bot.service
[Unit]
Description=Claude Telegram Bot
After=network.target
[Service] User=ubuntu WorkingDirectory=/home/ubuntu/claude-bot ExecStart=/usr/bin/python3 bot.py Restart=always Environment=ANTHROPIC_API_KEY=your-key Environment=BOT_TOKEN=your-token
[Install] WantedBy=multi-user.target
sudo systemctl enable claude-bot
sudo systemctl start claude-bot
Cost Estimate
With 100 daily messages (avg 200 input + 150 output tokens each) on Sonnet:
- Input: ~$0.06/day
- Output: ~$0.23/day
- Total: ~$9/month
What to Build Next
/summarizecommand — paste a URL, get a summary- Image analysis — handle
filters.PHOTOand use Claude’s vision - Scheduled reminders — use
job_queuefrom python-telegram-bot - Group chat mode — different system prompt for group vs private
Originally published at kalyna.pro