Bot de Telegram en Python que recibe fotos o PDFs de facturas, convierte los PDFs a imágenes y extrae los datos principales usando Azure OpenAI (visión + Structured Outputs). Devuelve un JSON con propiedades en inglés (por ejemplo: invoice_number, seller.name, total_amount).
Telegram (imagen/PDF) → Bot (PDF→imágenes) → Azure OpenAI (visión) → JSON → Telegram
- Requisitos
- Instalación
- Configuración (.env)
- Ejecución
- Cómo usarlo
- Ejemplo de salida JSON
- Arquitectura
- Variables de entorno (detalle)
- Logging
- Solución de problemas
- Rendimiento y costos
- Personalización
- Estructura sugerida del proyecto
- Licencia
- Contribuir
- Python 3.10 – 3.12
- Cuenta en Azure OpenAI con un deployment compatible con visión (por ej.
gpt-4o) - Un bot de Telegram (token vía @BotFather)
- Sistema operativo: Windows, macOS o Linux
git clone <tu-repo>
cd <tu-repo>
python -m venv .venv
# Windows
.venv\Scripts\activate
# macOS/Linux
source .venv/bin/activate
pip install -r requirements.txtrequirements.txt (referencia):
python-telegram-bot==21.8
openai>=1.42.0
pydantic>=2.8.2
python-dotenv>=1.0.1
PyMuPDF>=1.24.10
Crea un archivo .env en la raíz del proyecto:
# Telegram
TELEGRAM_TOKEN=123456:ABC-DEF...
# Azure OpenAI
AZURE_OPENAI_ENDPOINT=https://<tu-recurso>.openai.azure.com
AZURE_OPENAI_API_KEY=<tu-api-key>
AZURE_OPENAI_API_VERSION=2024-10-21
AZURE_OPENAI_DEPLOYMENT=gpt-4o
# Conversión PDF → imágenes (opcional)
MAX_PDF_PAGES=5
PDF_DPI=200
PDF_JPEG_QUALITY=85
Notas
AZURE_OPENAI_DEPLOYMENTes el nombre del deployment que creaste en Azure (no el nombre genérico del modelo).- Ajusta
MAX_PDF_PAGES,PDF_DPIyPDF_JPEG_QUALITYpara balancear calidad / costo / tiempo.
python bot.pySalida esperada (logs):
YYYY-mm-dd HH:MM:SS | INFO | invoice-bot | Iniciando bot | Endpoint=... | Deployment=gpt-4o | APIv=2024-10-21 | MAX_PDF_PAGES=5 | PDF_DPI=200 | JPEG_Q=85
YYYY-mm-dd HH:MM:SS | INFO | invoice-bot | Bot corriendo. Presioná Ctrl+C para salir.
En Telegram, envía al bot:
- Una foto de la factura (como Photo o Document de imagen); o
- Un PDF con la factura: el bot lo convertirá a imágenes (hasta
MAX_PDF_PAGES) y enviará todas las páginas al modelo en una sola solicitud.
El bot responderá con un bloque de JSON con las propiedades en inglés.
{
"is_invoice": true,
"document_type": "invoice",
"language": "es",
"ocr_confidence": 0.95,
"invoice_number": "A-0001-00001234",
"issue_date": "2025-08-09",
"due_date": "2025-08-24",
"po_number": null,
"currency": "ARS",
"seller": {
"name": "ACME S.A.",
"tax_id": "30-12345678-9",
"address": "Av. Siempre Viva 123, CABA"
},
"buyer": {
"name": "Cliente SRL",
"tax_id": "30-87654321-0",
"address": "Calle Falsa 742, CABA"
},
"subtotal_amount": 100000.0,
"total_tax_amount": 21000.0,
"total_amount": 121000.0,
"taxes": [
{ "name": "VAT", "amount": 21000.0, "rate": 0.21 }
],
"line_items": [
{ "description": "Servicio X", "quantity": 1, "unit_price": 100000.0, "amount": 100000.0 }
],
"payment_terms": "Pago a 15 días",
"notes": null
}Si un dato no está presente en la factura, el bot devuelve
null. El campois_invoicepuede serfalsesi el documento no es una factura.
┌───────────────────┐
│ Usuario (TG) │
│ Imagen / PDF │
└─────────┬─────────┘
│
▼
┌───────────────────┐ Convierte PDF→imágenes (PyMuPDF)
│ Bot de Telegram │ ──────────────────────────────────────► data URLs (JPEG)
│ (python-telegram-bot) │
└─────────┬─────────┘
│ mensajes con partes {text, image_url}
▼
┌─────────────────────────────────────────────────────────┐
│ Azure OpenAI (Chat Completions + Visión) │
│ Structured Outputs (Pydantic/JSON Schema) │
└─────────┬──────────────────────────────────────────────┘
│ JSON validado
▼
┌───────────────────┐
│ Respuesta en TG │
│ (bloque JSON) │
└───────────────────┘
TELEGRAM_TOKEN: token del bot de Telegram.AZURE_OPENAI_ENDPOINT: endpoint de tu recurso (por ej.https://…azure.com).AZURE_OPENAI_API_KEY: API key del recurso.AZURE_OPENAI_API_VERSION: versión de la API (ej.2024-10-21).AZURE_OPENAI_DEPLOYMENT: nombre del deployment (ej.gpt-4o).MAX_PDF_PAGES: máximo de páginas a procesar por PDF (predeterminado5).PDF_DPI: resolución al renderizar el PDF (predeterminado200).PDF_JPEG_QUALITY: calidad JPEG 1–100 (predeterminado85).
-
Formato de logs:
timestamp | nivel | logger | mensaje. -
Mensajes clave:
- Inicio de la app y parámetros efectivos.
- Recepción/descarga de fotos y PDFs (tamaño, mime).
- Progreso de PDF→imágenes (páginas renderizadas).
- Llamada a Azure y parseo final (
is_invoice,invoice_number,total_amount). - Errores con
traceback.
Para ver más detalle:
logging.basicConfig(level=logging.DEBUG, format="%(asctime)s | %(levelname)s | %(name)s | %(message)s")-
No toma el
.envAsegúrate de tenerpython-dotenvy que se llama aload_dotenv(find_dotenv(usecwd=True)). Ejecuta el bot desde la raíz del proyecto o usa rutas absolutas. -
Error
There is no current event loop in thread 'asyncio_0'No envuelvasapp.run_polling()enasyncio.run(...)ni enasyncio.to_thread(...). Invócalo directamente enmain(). -
max_tokensno soportado en Azure Algunos despliegues no aceptanmax_tokensy usanmax_completion_tokens. En este proyecto están comentados (no son necesarios). -
BaseModel.model_dump_json(..., ensure_ascii=...)En Pydantic v2 no existeensure_asciienmodel_dump_json. Se utilizamodel_dump_json(indent=2). -
PDFs muy pesados / tiempo alto Reduce
MAX_PDF_PAGES, bajaPDF_DPIo sube la compresiónPDF_JPEG_QUALITY. -
Rate limits o costos altos en Azure Considera usar menos páginas, menor DPI, agrupar menos imágenes por solicitud, o revisar cuotas en Azure.
- Más páginas y mayor DPI ⇒ más tokens ⇒ mayor costo y latencia.
- JPEG con menor calidad reduce tamaño sin afectar demasiado el OCR.
- Envío de múltiples imágenes en una sola llamada mejora coherencia entre páginas, pero aumenta tokens.
- Guardar JSON en disco: agrega escritura a archivo (por fecha/chat) y loguea la ruta.
- Campos adicionales: amplía el esquema Pydantic (
Invoice) y ajusta el system prompt. - Álbum (media group): agrupa múltiples fotos en una sola llamada.
- Exportar a CSV/Excel: mapea
line_itemsy genera archivos para descarga. - Validaciones locales: normalización de CUIT/CUIL, reglas fiscales, etc.
.
├─ bot.py
├─ requirements.txt
├─ .env.example
└─ README.md
.env.example:
TELEGRAM_TOKEN=
AZURE_OPENAI_ENDPOINT=
AZURE_OPENAI_API_KEY=
AZURE_OPENAI_API_VERSION=2024-10-21
AZURE_OPENAI_DEPLOYMENT=gpt-4o
MAX_PDF_PAGES=5
PDF_DPI=200
PDF_JPEG_QUALITY=85
Este proyecto puede distribuirse bajo MIT (ajústalo según tu necesidad).
¡PRs y sugerencias bienvenidas! Ideas útiles:
- Agregar bases de datos o almacenamiento en la nube.
- Post-procesamiento de totales e IVA.
- Integración con ERPs o renombrado/ordenado de archivos según CUIT/razón social.