Cap 8: Despliegue
Modos de operación
flowchart TD
subgraph DEV[Desarrollo local]
POL[Polling\nbun bot.py] -->|funciona sin URL| TG1[Telegram]
NGR[Webhook + ngrok\nURL temporal] --> TG1
end
subgraph PROD[Producción]
VPS[VPS / Railway\nWebhook HTTPS] --> TG2[Telegram]
end
Para el tutorial de Claude Code local (cap. 5), el bot siempre corre en local en modo polling — el Claude Code necesita acceso al filesystem de tu máquina.
Desarrollo local con polling
La forma más simple. No necesitas ngrok ni URL pública:
# Activar entorno virtual
source .venv/bin/activate
# Iniciar bot
python bot.py
Para recargar automáticamente en cambios:
pip install watchfiles
watchfiles "python bot.py" .
Desarrollo con webhook — ngrok
Si necesitas probar webhooks localmente:
# Instalar ngrok
# linux: snap install ngrok / brew install ngrok
ngrok http 8080
# → https://abc123.ngrok-free.app
Actualiza bot.py para webhook:
import os
from telegram.ext import Application
WEBHOOK_URL = os.getenv("WEBHOOK_URL") # tu URL de ngrok o producción
PORT = int(os.getenv("PORT", "8080"))
def main():
init_db()
app = Application.builder().token(TELEGRAM_TOKEN).build()
# ... handlers
if WEBHOOK_URL:
# Modo webhook
app.run_webhook(
listen="0.0.0.0",
port=PORT,
webhook_url=f"{WEBHOOK_URL}/webhook",
url_path="/webhook",
)
else:
# Modo polling (desarrollo simple)
app.run_polling(allowed_updates=["message", "callback_query"])
if __name__ == "__main__":
main()
# Con webhook local
WEBHOOK_URL=https://abc123.ngrok-free.app python bot.py
Despliegue en Railway
Railway es ideal para el bot con Claude API (sin Claude Code local):
1. Preparar Procfile
# Procfile
web: python bot.py
2. Subir a GitHub
git init
git add .
git commit -m "feat: bot telegram con claude"
git remote add origin https://github.com/usuario/mi-bot.git
git push -u origin main
3. Crear proyecto en Railway
# Instalar Railway CLI
npm install -g @railway/cli
railway login
railway init
railway up
4. Configurar variables de entorno en Railway
En el dashboard de Railway → Variables:
TELEGRAM_BOT_TOKEN = 7123...
ANTHROPIC_API_KEY = sk-ant-...
ALLOWED_USER_ID = 123456789
CLAUDE_MODEL = claude-opus-4-5-20251001
WEBHOOK_URL = https://mi-bot.up.railway.app
PORT = 8080
5. Despliegue automático
Railway re-deploya en cada push a main. El bot estará disponible 24/7.
Despliegue en VPS (Ubuntu)
Para el caso de Claude Code local — necesitas una máquina con tu repositorio:
Instalar dependencias
# En el VPS
sudo apt update && sudo apt install -y python3.11 python3.11-venv git nodejs npm
# Claude Code
npm install -g @anthropic-ai/claude-code
claude # hacer login
# Clonar tu repositorio del proyecto
git clone https://github.com/usuario/mi-proyecto.git ~/mi-proyecto
# Clonar el bot
git clone https://github.com/usuario/mi-bot.git ~/mi-bot
cd ~/mi-bot
python3.11 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
Configurar como servicio systemd
# /etc/systemd/system/telegram-bot.service
sudo nano /etc/systemd/system/telegram-bot.service
[Unit]
Description=Telegram Claude Bot
After=network.target
[Service]
Type=simple
User=ubuntu
WorkingDirectory=/home/ubuntu/mi-bot
EnvironmentFile=/home/ubuntu/mi-bot/.env
ExecStart=/home/ubuntu/mi-bot/.venv/bin/python bot.py
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
sudo systemctl enable telegram-bot
sudo systemctl start telegram-bot
sudo systemctl status telegram-bot
# Ver logs
sudo journalctl -u telegram-bot -f
Actualizar el bot
cd ~/mi-bot
git pull
sudo systemctl restart telegram-bot
Dockerfile para contenedor
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "bot.py"]
docker build -t telegram-bot .
docker run -d \
--env-file .env \
--name telegram-bot \
--restart unless-stopped \
telegram-bot
Keep-alive y health check
Para evitar que el servicio muera silenciosamente, agrega un endpoint de health y un ping periódico:
# En bot.py, antes de run_polling/run_webhook
from aiohttp import web
async def health_check(request):
return web.Response(text="ok")
async def start_health_server():
app_web = web.Application()
app_web.router.add_get("/health", health_check)
runner = web.AppRunner(app_web)
await runner.setup()
site = web.TCPSite(runner, "0.0.0.0", 9090)
await site.start()
Agrega aiohttp a requirements.txt.
Checklist de producción
-
.envno commiteado (verificar.gitignore) -
ALLOWED_USER_IDconfigurado - Rate limiting activo
- Error handler registrado
- Purge job de DB configurado
- Logs con
logging.INFO(noDEBUGen producción) - Restart automático (
Restart=alwaysen systemd / Railway auto-redeploy) -
WEBHOOK_URLen HTTPS (Railway lo da automáticamente) - Timeout de subprocess configurado (cap. 5)
Monitoreo básico
Agrega un comando /ping para verificar que el bot responde:
async def cmd_ping(update: Update, context: ContextTypes.DEFAULT_TYPE):
import time
t = time.time()
msg = await update.message.reply_text("Pong!")
latencia = int((time.time() - t) * 1000)
await msg.edit_text(f"Pong! Latencia: {latencia}ms")
app.add_handler(CommandHandler("ping", cmd_ping))
Arquitectura final del sistema
flowchart TD
subgraph LOCAL[Máquina local o VPS]
BOT[Bot Python\npython bot.py] --> DB[(SQLite\nbot_history.db)]
BOT --> CC[Claude Code CLI\nclaude --print]
CC --> REPO[Repositorio\nde código]
end
subgraph CLOUD[Cloud]
TG[Telegram Servers]
ANT[Anthropic API]
end
PHONE([Teléfono]) <--> TG
TG <-->|polling| BOT
BOT <-->|HTTP| ANT