Lección 4 de 6Gratis16 min

Taller 4 (opcional): Telegram como canal de prueba

Lee el adaptador Telegram, practica dry-run y separa polling, allowlist y envío autorizado.

Última actualización: 14 de septiembre de 2026

Esta es una ampliación opcional de mensajería. No necesitas Telegram para aprender el núcleo ni para completar los talleres locales. El adaptador incluido lee updates, filtra un chat privado y prepara una intención; no convierte el bot en un servicio de producción.

Objetivo: leer el adaptador de Telegram, practicar la inspección sin envío y separar polling, allowlist y envío autorizado.

Requisitos y seguridad

Necesitas el kit extraído, Python 3.10+, una cuenta de Telegram y un chat privado de prueba si decides continuar. Los pasos reales requieren red y un token propio; no están comprobados por el curso. No uses chats de clientes.

En el flujo de Telegram:

  • Polling pregunta a getUpdates por mensajes pendientes.
  • Allowlist compara TELEGRAM_ALLOWED_CHAT_ID; chats no autorizados se ignoran.
  • Dry-run (TELEGRAM_ALLOW_SEND=0, sin --send) crea una intención pendiente local.
  • Envío exige dos señales: --send y TELEGRAM_ALLOW_SEND=1.

Crear el bot y encontrar el chat (opcional)

Abre @BotFather, usa /newbot, guarda el token en un gestor privado y escribe /start desde tu chat de prueba. Nunca lo pegues en Git, capturas, URLs o comandos que queden en historial.

En una terminal Bash, carga el token sin escribirlo en el historial. Mantén abierta esta terminal para los siguientes pasos: una variable creada dentro de Python no se exportaría a tu shell.

read -r -s -p 'Token del bot (oculto): ' TELEGRAM_BOT_TOKEN
printf '\n'
export TELEGRAM_BOT_TOKEN
export TELEGRAM_ALLOW_SEND=0

No ejecutes esto dentro de una grabación ni actives set -x, que mostraría valores sensibles. Son pasos reales opcionales, no una prueba ejecutada contra Telegram.

Para obtener chat_id, este ejemplo usa el token desde el entorno y solo muestra update_id, chat_id y tipo; nunca imprime el payload completo. Ejecútalo únicamente con tu bot de prueba:

python3 - <<'PY'
import json, os, urllib.request

token = os.environ["TELEGRAM_BOT_TOKEN"]
request = urllib.request.Request(
    "https://api.telegram.org/bot" + token + "/getUpdates",
    data=json.dumps({"allowed_updates": ["message"], "limit": 100}).encode(),
    headers={"Content-Type": "application/json"},
    method="POST",
)
try:
    with urllib.request.urlopen(request, timeout=20) as response:
        payload = json.load(response)
except (OSError, ValueError):
    raise SystemExit("No se pudo consultar Telegram. Revisa conexión y token sin imprimirlo.") from None
if not isinstance(payload, dict) or payload.get("ok") is not True:
    raise SystemExit("Telegram no confirmó la consulta; no imprimas el cuerpo completo.")
for update in payload.get("result", []):
    message = update.get("message", {})
    chat = message.get("chat", {})
    if chat.get("type") == "private":
        print({"update_id": update.get("update_id"), "chat_id": chat.get("id"), "type": chat.get("type")})
PY

Si no aparece ningún update, envía /start y revisa que otro proceso no lo haya consumido.

Leer primero, enviar después

Copia tu chat_id privado de la salida anterior. En la misma terminal, autorízalo e introduce en tu chat el comando stock A-100:

read -r -p 'chat_id privado autorizado: ' TELEGRAM_ALLOWED_CHAT_ID
export TELEGRAM_ALLOWED_CHAT_ID
export TELEGRAM_ALLOW_SEND=0
python3 telegram.py poll --once

Esto sí podría leer red si ejecutas el comando real. El curso no lo ejecuta. Para revisar el estado local sin red usa el valor por defecto seguro:

python3 telegram.py inspect

También puedes indicar una ruta privada explícita, por ejemplo --db "$HOME/.local/share/agente-catalogo/telegram.sqlite3"; no pases una variable vacía. inspect no garantiza que exista una respuesta pendiente: si todavía no hubo un update aceptado, pending estará vacío. «Inspeccionar» no significa que haya una previsualización disponible.

Solo tras revisar el chat y la respuesta, y con permiso explícito, se podría ejecutar:

export TELEGRAM_ALLOW_SEND=1
python3 telegram.py poll --once --send

--once significa una vuelta de lectura, no un único mensaje: puede haber varias intenciones pendientes. Mantén el bot limitado a tu chat de prueba. Un timeout de sendMessage queda unknown; no repitas el POST a ciegas. Resuelve manualmente después de obtener confirmación externa.

Al terminar, elimina las variables de esta sesión con unset TELEGRAM_BOT_TOKEN TELEGRAM_ALLOWED_CHAT_ID TELEGRAM_ALLOW_SEND. SQLite conserva estado y respuestas; trata esa base como información privada.

Ejercicio: inspección sin Telegram

Comprueba qué contiene una base nueva antes de recibir mensajes:

DB="$(mktemp -d)/telegram.sqlite3"
python3 telegram.py inspect --db "$DB" > inspect.json
python3 - <<'PY'
import json
state=json.load(open("inspect.json", encoding="utf-8"))
assert state["offset"] == 0
assert state["updates"] == []
assert state["pending"] == []
print("Telegram inspect offline OK: sin update no hay preview pendiente")
PY

Resultado observable

El snippet solo crea una SQLite local privada y comprueba el estado vacío. No prueba BotFather, Telegram, red, entrega exactly-once ni un mensaje real. El núcleo sigue siendo la fuente de los hechos de Tienda Luna; el canal solo transporta texto.

Error habitual y recuperación

Si aparece pending: [], no significa que se haya enviado nada: no hay una respuesta pendiente en esa base. Comprueba el chat_id, envía stock A-100 desde el chat autorizado y repite la lectura solo si decidiste hacer el ensayo real. Si cambiaste de terminal, vuelve a cargar sus variables; no copies el token dentro del código para arreglarlo.

Fuentes