Un bot de Telegram es un programa que recibe mensajes a través de la API de bots de Telegram y responde automáticamente: avisos, comandos de administración, atención al cliente o integraciones con otros servicios. Para que funcione las 24 horas necesita un servidor siempre encendido. En este tutorial crearás un bot con la librería python-telegram-bot, lo instalarás en un entorno virtual de Python en Ubuntu 24.04 y lo dejarás funcionando como servicio de systemd, con un usuario propio, el token fuera del código y reinicio automático.
Requisitos previos
Para seguir esta guía necesitas:
- Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath. Un bot sencillo funciona con 512 MB de RAM.
- Un usuario no root con privilegios
sudo. - Una cuenta de Telegram en el móvil o en el escritorio.
El bot usa long polling: es el propio bot el que consulta periódicamente a Telegram si hay mensajes nuevos. Por eso no necesitas dominio, certificado TLS ni abrir ningún puerto en el firewall.
Paso 1: Crear el bot con BotFather
Los bots se registran hablando con @BotFather, el bot oficial de Telegram para gestionar bots. Ábrelo en Telegram y sigue estos pasos:
- Envía
/newbot. - Escribe el nombre visible del bot, por ejemplo
Mi Bot de Avisos. - Escribe el nombre de usuario. Debe ser único y terminar en
bot, por ejemplomi_avisos_bot.
BotFather responde con el token de acceso a la API:
Use this token to access the HTTP API:
123456789:AAH-your_bot_token_example
Guárdalo en un lugar seguro. Quien tenga el token controla el bot por completo; si se filtra, revócalo desde BotFather con /revoke.
Comprueba desde el servidor que el token es válido llamando al método getMe de la API. Sustituye your_bot_token por el tuyo:
curl -s https://api.telegram.org/botyour_bot_token/getMe
{"ok":true,"result":{"id":123456789,"is_bot":true,"first_name":"Mi Bot de Avisos","username":"mi_avisos_bot",...}}
Si ves "ok":false con Unauthorized, el token está mal copiado.
Paso 2: Crear un usuario de sistema para el bot
El bot no necesita una shell ni acceso a tu usuario, así que se ejecutará con un usuario de sistema propio. Si el código tuviera un fallo aprovechable, el atacante quedaría limitado a lo que puede hacer ese usuario:
sudo adduser --system --group --home /opt/telegram-bot --shell /usr/sbin/nologin telegrambot
Comprueba que se ha creado:
id telegrambot
uid=110(telegrambot) gid=111(telegrambot) groups=111(telegrambot)
Paso 3: Instalar Python y las dependencias
Ubuntu 24.04 incluye Python 3.12, pero no permite instalar paquetes con pip fuera de un entorno virtual. Instala el módulo venv:
sudo apt update
sudo apt install python3-venv
Crea el entorno virtual dentro del directorio del bot, como el usuario telegrambot:
sudo -u telegrambot python3 -m venv /opt/telegram-bot/venv
Instala la librería python-telegram-bot, que envuelve la API de Telegram con una interfaz asíncrona:
sudo -u telegrambot /opt/telegram-bot/venv/bin/pip install python-telegram-bot
Comprueba la versión instalada:
sudo -u telegrambot /opt/telegram-bot/venv/bin/python -c "import telegram; print(telegram.__version__)"
22.4
Cualquier versión 21 o posterior sirve para el código de esta guía.
Paso 4: Guardar el token en un archivo de entorno
El token no debe estar escrito en el código: acabaría en el repositorio o en una copia de seguridad. Guárdalo en un archivo que systemd cargará al arrancar el servicio:
sudo nano /etc/telegram-bot.env
TELEGRAM_BOT_TOKEN=your_bot_token
Deja el archivo legible solo por root. systemd lo lee como root antes de cambiar al usuario del servicio, así que el bot recibe la variable sin poder leer el archivo:
sudo chown root:root /etc/telegram-bot.env
sudo chmod 600 /etc/telegram-bot.env
Paso 5: Escribir el bot
Crea el programa principal:
sudo nano /opt/telegram-bot/bot.py
Este bot responde a /start y /help, devuelve tu ID de chat con /id y repite cualquier otro texto que le envíes:
#!/usr/bin/env python3
import logging
import os
from telegram import Update
from telegram.ext import (
Application,
CommandHandler,
ContextTypes,
MessageHandler,
filters,
)
logging.basicConfig(
format="%(levelname)s %(name)s: %(message)s",
level=logging.INFO,
)
# httpx registra cada petición de polling; se silencia para no llenar el log
logging.getLogger("httpx").setLevel(logging.WARNING)
logger = logging.getLogger("bot")
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
user = update.effective_user
await update.message.reply_text(
f"Hola, {user.first_name}. Escribe /help para ver lo que sé hacer."
)
async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
await update.message.reply_text(
"/start - saludo\n"
"/id - muestra el ID de este chat\n"
"Cualquier otro texto te lo devuelvo tal cual."
)
async def chat_id(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
await update.message.reply_text(f"ID de este chat: {update.effective_chat.id}")
async def echo(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
await update.message.reply_text(update.message.text)
async def on_error(update: object, context: ContextTypes.DEFAULT_TYPE) -> None:
logger.error("Error procesando una actualización", exc_info=context.error)
def main() -> None:
token = os.environ["TELEGRAM_BOT_TOKEN"]
app = Application.builder().token(token).build()
app.add_handler(CommandHandler("start", start))
app.add_handler(CommandHandler("help", help_command))
app.add_handler(CommandHandler("id", chat_id))
app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, echo))
app.add_error_handler(on_error)
logger.info("Bot iniciado, esperando mensajes")
app.run_polling(allowed_updates=Update.ALL_TYPES)
if __name__ == "__main__":
main()
Puntos clave del código:
- Cada
CommandHandlerasocia un comando a una función asíncrona. ElMessageHandlerconfilters.TEXT & ~filters.COMMANDcaptura el texto que no es un comando. run_polling()gestiona el bucle de consultas, los reintentos de red y la parada limpia cuando systemd envíaSIGTERM.- Los logs van a la salida estándar, que systemd guarda en el journal. No hace falta configurar archivos de log ni rotación.
- El manejador de errores registra las excepciones sin que el bot se caiga.
Asigna el archivo al usuario del bot:
sudo chown telegrambot:telegrambot /opt/telegram-bot/bot.py
Paso 6: Probar el bot manualmente
Antes de crear el servicio, ejecútalo a mano para detectar errores. Este comando carga la variable del archivo de entorno y lanza el bot como telegrambot:
sudo bash -c 'set -a; . /etc/telegram-bot.env; exec sudo -E -u telegrambot /opt/telegram-bot/venv/bin/python /opt/telegram-bot/bot.py'
INFO bot: Bot iniciado, esperando mensajes
INFO telegram.ext.Application: Application started
Abre el chat con tu bot en Telegram (https://t.me/mi_avisos_bot), pulsa Iniciar y envía /start, /id y un texto cualquiera. Deberías recibir las tres respuestas. Detén el bot con Ctrl+C.
Paso 7: Ejecutar el bot como servicio systemd
Para que el bot arranque con el servidor y se reinicie si falla, crea una unidad de systemd:
sudo nano /etc/systemd/system/telegram-bot.service
[Unit]
Description=Bot de Telegram
Wants=network-online.target
After=network-online.target
[Service]
Type=simple
User=telegrambot
Group=telegrambot
WorkingDirectory=/opt/telegram-bot
EnvironmentFile=/etc/telegram-bot.env
Environment=PYTHONUNBUFFERED=1
ExecStart=/opt/telegram-bot/venv/bin/python /opt/telegram-bot/bot.py
Restart=on-failure
RestartSec=5
# Endurecimiento
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/opt/telegram-bot
[Install]
WantedBy=multi-user.target
Qué aporta cada bloque:
EnvironmentFilecarga el token yPYTHONUNBUFFERED=1hace que los logs aparezcan en el journal al instante.Restart=on-failureconRestartSec=5levanta el bot de nuevo si termina con error, por ejemplo tras un corte de red prolongado.ProtectSystem=strictmonta todo el sistema de archivos como solo lectura para el servicio, salvo/opt/telegram-bot, donde el bot podrá guardar datos (una base SQLite, por ejemplo).ProtectHome=truele oculta los directorios de los usuarios.
Carga la unidad, actívala y arráncala:
sudo systemctl daemon-reload
sudo systemctl enable --now telegram-bot
sudo systemctl status telegram-bot
● telegram-bot.service - Bot de Telegram
Loaded: loaded (/etc/systemd/system/telegram-bot.service; enabled; preset: enabled)
Active: active (running) since Thu 2026-09-25 11:02:37 UTC; 4s ago
Main PID: 2841 (python)
Envía otro mensaje al bot desde Telegram para confirmar que responde con el servicio en marcha.
Paso 8: Consultar los logs y actualizar el bot
Sigue los logs en tiempo real:
sudo journalctl -u telegram-bot -f
Para ver solo los errores de hoy:
sudo journalctl -u telegram-bot -p err --since today
Cuando cambies el código de bot.py, reinicia el servicio para cargarlo:
sudo systemctl restart telegram-bot
Para actualizar la librería a la última versión:
sudo -u telegrambot /opt/telegram-bot/venv/bin/pip install --upgrade python-telegram-bot
sudo systemctl restart telegram-bot
Si tu bot crece y usa más paquetes, guárdalos en un requirements.txt con versiones fijas y instálalos con pip install -r requirements.txt, para que una actualización no rompa el bot sin que lo sepas.
Solución de problemas
telegram.error.InvalidToken: el token del archivo /etc/telegram-bot.env es incorrecto o tiene espacios o comillas de más. La línea debe ser exactamente TELEGRAM_BOT_TOKEN= seguida del token. Tras corregirlo, reinicia el servicio.
Conflict: terminated by other getUpdates request: hay dos procesos consultando con el mismo token. Suele ocurrir si dejaste el bot ejecutándose a mano en otra terminal o si el mismo token está en uso en tu equipo local. Comprueba los procesos con pgrep -af bot.py y deja solo uno.
KeyError: 'TELEGRAM_BOT_TOKEN': el servicio no ha cargado el archivo de entorno. Revisa la ruta en EnvironmentFile y ejecuta sudo systemctl daemon-reload después de editar la unidad.
El bot no responde en grupos: por defecto los bots tienen activado el modo privacidad y en los grupos solo reciben comandos y respuestas a sus propios mensajes. Si necesitas que lea todo el texto del grupo, desactívalo en BotFather con /setprivacy.
Conclusión
Tienes un bot de Telegram en Python ejecutándose como servicio systemd en Ubuntu 24.04, con un usuario sin privilegios, el token fuera del código, el sistema de archivos protegido y reinicio automático ante fallos. Los logs quedan en el journal y actualizarlo es un solo reinicio.
Como siguientes pasos, puedes:
- Registrar la lista de comandos en BotFather con
/setcommandspara que Telegram los sugiera al escribir/. - Guardar datos de usuarios en una base SQLite dentro de
/opt/telegram-boto activar la persistencia integrada de la librería conPicklePersistence. - Programar mensajes periódicos con el
JobQueuedepython-telegram-bot, instalando el extra conpip install "python-telegram-bot[job-queue]".
