Cómo hacer un bot de Telegram en Python — De cero a tu primer bot
Hacer un bot de Telegram en Python es una de las cosas más gratificantes que puedes programar como proyecto personal. En menos de 30 líneas tienes un bot funcionando en tu móvil, respondiéndote a comandos, enviándote alertas, conectado a una API de IA. Y a partir de ahí lo extiendes hasta donde quieras: recordatorios, traductor, asistente personal, monitor de servidores, alarma cuando un script falla, lo que se te ocurra.
En esta entrada te enseño los pasos que importan: crear el bot en BotFather, escribir tu primer comando, manejar mensajes, integrarlo con OpenAI/Claude para hacer un asistente, y los errores típicos al empezar.
Contenido
- 1 Lo que vas a tener al final
- 2 Paso 1 — Crear el bot en BotFather
- 3 Paso 2 — Instalar la librería
- 4 Paso 3 — Tu primer bot funcional
- 5 Cómo funciona por dentro
- 6 Paso 4 — Manejar mensajes libres (no solo comandos)
- 7 Paso 5 — Bonus: bot conectado a un LLM (OpenAI)
- 8 Paso 6 — Mantener contexto de conversación
- 9 Errores típicos al empezar
- 10 Cuándo te merece la pena un bot de Telegram
- 11 Deploy básico
- 12 Resumen
- 13 Tu siguiente paso
Lo que vas a tener al final
Un bot que:
- Responde a
/startcon un saludo. - Atiende un comando custom como
/clima Madrid. - Responde a cualquier mensaje libre usando un LLM (bonus).
Todo en ~50 líneas Python con la librería oficial python-telegram-bot (v22+, async/await).
Paso 1 — Crear el bot en BotFather
Abre Telegram y busca @BotFather (la cuenta oficial). Le mandas:
/newbot
Te pedirá:
- Un nombre (lo que se ve en el chat):
Mi Asistente Python. - Un username terminado en
bot:mi_asistente_python_bot.
Te devolverá un token tipo:
1234567890:AAEhBP8q-ABCxyzWXYz1234abcdEFGHijk
Eso es la clave del bot. Igual que una contraseña: si se filtra, cualquiera puede controlar tu bot. Guárdala en una variable de entorno, nunca en código que vayas a subir a git.
Paso 2 — Instalar la librería
pip install python-telegram-bot
💡 ¿Sin
venvaún? Antes de instalar nada,venvypipsin liarte. Cinco comandos que te ahorran problemas.
📥 Llévate el cheatsheet de Python (gratis)
PDF de 6 páginas con lo esencial: tipos, condicionales, bucles, estructuras de datos, funciones y los errores que más vas a cometer. Para tener al lado mientras programas.
Sin spam. Te apuntas a la lista, descargas el cheatsheet y recibes contenido de Python cada semana.
Paso 3 — Tu primer bot funcional
import os
import logging
from telegram import Update
from telegram.ext import ApplicationBuilder, CommandHandler, ContextTypes
logging.basicConfig(level=logging.INFO)
TOKEN = os.environ["TELEGRAM_BOT_TOKEN"]
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
await update.message.reply_text(
f"Hola {update.effective_user.first_name}, soy tu bot. Prueba /ayuda."
)
async def ayuda(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
await update.message.reply_text(
"Comandos disponibles:\n"
"/start — Saludo inicial\n"
"/ayuda — Esta ayuda\n"
"/eco <texto> — Te repite el texto"
)
async def eco(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
texto = " ".join(context.args) if context.args else "(sin argumentos)"
await update.message.reply_text(f"Eco: {texto}")
def main() -> None:
app = ApplicationBuilder().token(TOKEN).build()
app.add_handler(CommandHandler("start", start))
app.add_handler(CommandHandler("ayuda", ayuda))
app.add_handler(CommandHandler("eco", eco))
app.run_polling()
if __name__ == "__main__":
main()
Antes de ejecutarlo:
export TELEGRAM_BOT_TOKEN="1234567890:AAEhBP8q..."
python bot.py
Y en Telegram, abre el chat con tu bot (busca su username) y prueba /start. Te responde. ¡Bot funcionando!
💡 ¿Por qué
if __name__ == "__main__"? Miraif __name__ == "__main__"en Python.
Cómo funciona por dentro
ApplicationBuilder().token(TOKEN).build()crea la app del bot conectándose a Telegram.CommandHandler("start", funcion)registra qué función responde al comando/start.run_polling()arranca el bot y empieza a escuchar mensajes (consultando Telegram cada poco tiempo).- Cada handler es una función
asyncque recibeupdate(el mensaje recibido) ycontext(estado del bot + argumentos).
⚡ Tip-friki:
python-telegram-botv22+ es async por dentro. Por eso los handlers sonasync def. Si vienes nuevo aasync, lee asyncio en Python desde cero primero.
Paso 4 — Manejar mensajes libres (no solo comandos)
Para que el bot responda a cualquier mensaje, no solo a /comandos:
from telegram.ext import MessageHandler, filters
async def manejar_mensaje(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
texto_recibido = update.message.text
await update.message.reply_text(f"Recibí: {texto_recibido}")
app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, manejar_mensaje))
filters.TEXT & ~filters.COMMAND = “mensajes de texto que NO sean comandos”. Así no se duplica con los CommandHandler ya registrados.
Paso 5 — Bonus: bot conectado a un LLM (OpenAI)
Aquí es donde se vuelve interesante. Ahora el bot responde inteligentemente con un LLM:
pip install openai
import os
from openai import OpenAI
from telegram import Update
from telegram.ext import ContextTypes
cliente_llm = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
async def manejar_con_llm(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
texto_usuario = update.message.text
respuesta = cliente_llm.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": "Eres un asistente útil y conciso, respondes en español."},
{"role": "user", "content": texto_usuario},
],
max_tokens=400,
)
await update.message.reply_text(respuesta.choices[0].message.content)
Con el handler de mensajes libres apuntando a manejar_con_llm, ya tienes un asistente personal en Telegram que responde con IA.
💡 ¿Quieres entender APIs de OpenAI/Claude a fondo? Mira API de OpenAI y Claude en Python.
Paso 6 — Mantener contexto de conversación
Por defecto cada mensaje es independiente. Para que el bot recuerde la conversación dentro del chat:
from collections import defaultdict
historial = defaultdict(list) # chat_id → lista de mensajes
async def manejar_con_historial(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
chat_id = update.effective_chat.id
texto_usuario = update.message.text
historial[chat_id].append({"role": "user", "content": texto_usuario})
respuesta = cliente_llm.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": "Eres un asistente útil."},
*historial[chat_id][-20:], # últimos 20 mensajes
],
)
contenido = respuesta.choices[0].message.content
historial[chat_id].append({"role": "assistant", "content": contenido})
await update.message.reply_text(contenido)
Limitamos a 20 mensajes de historial para que el contexto no crezca infinito (ni la factura de tokens).
⚡ Para producción seria, persiste el historial en BBDD (no en un dict en memoria). Si reinicias el bot, todo el historial se pierde.
Errores típicos al empezar
# 1. Hardcodear el token
TOKEN = "1234:abc..." # ❌ no subir a git
TOKEN = os.environ["TELEGRAM_BOT_TOKEN"] # ✓
# 2. Usar handlers síncronos en v22+
def start(update, context): # ❌ falta async
update.message.reply_text("Hola")
async def start(update, context):
await update.message.reply_text("Hola") # ✓
# 3. Olvidar await en métodos asíncronos
update.message.reply_text("X") # ❌ se queda pendiente
await update.message.reply_text("X") # ✓
# 4. Filters mal combinados (sin negar comandos)
MessageHandler(filters.TEXT, manejar)
# ❌ los comandos /start, /ayuda… también disparan este handler
MessageHandler(filters.TEXT & ~filters.COMMAND, manejar) # ✓
# 5. Bot no responde
# Razones típicas:
# - Token mal copiado (con espacios o salto de línea)
# - run_polling() falla silenciosamente sin logging
# Soluciona: logging.basicConfig(level=logging.INFO) al principio.
# 6. Lanzar dos instancias del mismo bot
# Telegram limita a UN polling activo por bot. La segunda da error 409.
# Para tener varios entornos, crea bots separados (uno dev, uno prod).
# 7. Pasar credenciales del bot a getUpdates público
# El token NO debe aparecer ni en URLs ni en logs. Si se filtra, regenerarlo
# en BotFather con /revoke.
Cuándo te merece la pena un bot de Telegram
Casos donde es el camino más rápido:
- Notificaciones de scripts/crons que corren en servidores. Más rápido y mejor que email para alertas.
- Asistente personal integrado con APIs (calendario, recordatorios, traductor).
- Bot de equipo que entiende preguntas y conecta con la BBDD interna.
- Comandos rápidos: “qué tiempo hace en X”, “cuánto vale Bitcoin”, “cómo va mi servidor”.
- Frontend para automatización: en vez de scripts CLI, le mandas comandos a Telegram.
Casos donde no suele tener sentido:
- Producto B2B con muchos usuarios (Telegram tiene límites de rate).
- Volumen masivo (miles de mensajes/segundo).
- UI compleja con tablas, formularios estilo web.
Deploy básico
Para que el bot esté siempre encendido (en lugar de en tu portátil):
- VPS sencillo (DigitalOcean, Hetzner): copia el código, instala dependencias, lanza con
systemdosupervisorpara que reinicie solo si se cae. - Docker container orquestado en un VPS o un PaaS gratuito (Render, Railway, Fly.io).
- Webhook + servidor web en lugar de polling: más eficiente para tráfico alto.
💡 Patrón de servicio systemd para bot 24/7 + alertas si se cae con email: combina con enviar emails desde Python con
smtpliby logging en Python para producción seria.
Resumen
| Tarea | Sintaxis |
|---|---|
| Crear bot | @BotFather → /newbot → token |
| Instalar | pip install python-telegram-bot |
| Comando | CommandHandler("start", funcion_async) |
| Mensaje libre | MessageHandler(filters.TEXT & ~filters.COMMAND, ...) |
| Argumentos | context.args (lista de strings) |
| Responder | await update.message.reply_text("...") |
| Arrancar | app.run_polling() |
| Token | os.environ["TELEGRAM_BOT_TOKEN"] |
| Contexto LLM | dict por chat_id + últimos N mensajes |
| Deploy 24/7 | VPS con systemd, o webhook |
¿Te ha valido esto?
Si te ha resultado útil, llévate el cheatsheet de Python en PDF — 6 páginas con tipos, condicionales, bucles, estructuras de datos, funciones y los errores típicos. Para tener al lado mientras programas. Gratis.
Sin spam. Email + cheatsheet + un correo por semana con tutoriales nuevos.
Tu siguiente paso
Si has llegado hasta aquí, ya tienes la base para escribir cualquier bot que se te ocurra. La gran mayoría de proyectos personales con bots se construyen sobre estos cuatro patrones (CommandHandler, MessageHandler, async/await, integración con APIs). Lo que cambia es la lógica de dentro.
Si quieres aprender Python desde la base hasta proyectos reales con APIs, deploy y bots, en El Pythonista lo enseño paso a paso.
Un abrazo,
Oscar
¿Quieres aprender Python en orden, no a saltos?
Esto que has leído es solo una pieza. En El Pythonista lo verás todo encadenado: 11 módulos, 35+ lecciones, código revisado, ejercicios y un proyecto real (MovieTracker) que crece contigo desde la primera variable hasta el deploy a producción.
35+ lecciones · Proyecto real · Acceso de por vida · 14 días de garantía
