Files
GeorgeandClaude Sonnet 5.5 4deb9d4e4d se responde al remitente con las plantillas de aceptado/rechazado
El sistema contesta a quien envió el correo con la plantilla que corresponda.
Por defecto (NOTIFICACION_MODO=prueba) la respuesta va solo al equipo con el
asunto "[PRUEBA → remitente]"; en modo real va al remitente con copia oculta
al equipo. El correo técnico (tablas de SAP, JSON) pasa a ir solo al equipo.

- respuesta_proveedor.py: contextos de las plantillas, envío y protecciones
  (remitente desconocido o sin dominio, propio buzón, no-reply, mensajes
  automáticos, respuestas a notificaciones, límite de 3 por remitente/24 h).
- observaciones.py: motivos de rechazo con título, filas y cómo corregir
  (borrador de textos); también para correos sin comprobante válido.
- Un error de SAP/HANA o un fallo inesperado no se le comunica al proveedor y
  ya no tumba el ciclo; "guía no encontrada" exige que HANA responda.
- outlook_utilities: es_mensaje_automatico y copia oculta en send_email.
- seguimiento: notificacion_destinatario (con migración) y
  contar_respuestas_correo.
- config_validator anuncia el modo activo; documentación y pruebas.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-22 08:55:54 -05:00

43 lines
3.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# outlook_utilities.py — Microsoft Graph (Outlook)
Cliente mínimo de Microsoft Graph para leer la bandeja de entrada, bajar adjuntos y enviar las notificaciones. Autenticación con MSAL (client credentials, permisos de aplicación). Cómo obtener las credenciales: [configurar_msal.md](configurar_msal.md).
## Estructura
```
outlook_utilities.py
├── CONFIG (.env): CLIENT_ID, CLIENT_SECRET, TENANT_ID, VERIFY_SSL (false por defecto)
├── get_access_token() → str
├── get_emails(user_email, token, fecha_inicio, fecha_fin) → list[dict]
├── download_attachments(user_email, message_id, token, download_path) → list[str]
└── send_email(access_token, from_email, to_recipients, subject, html_body,
attachment_paths=None, cc_recipients=None) → bool
```
## Funciones
| Función | Endpoint Graph | Qué hace |
|---------|----------------|----------|
| `get_access_token` | `login.microsoftonline.com/{TENANT_ID}` (scope `.default`) | Obtiene el token de aplicación. Lanza `Exception` si MSAL devuelve error. |
| `get_emails` | `GET /users/{user}/mailFolders/Inbox/messages` | Correos de la bandeja de entrada del rango, con paginación (`$top=50`, sigue `@odata.nextLink`). Devuelve `id`, `fecha`, `sender`, `subject`, `has_attach` y `es_automatico` (calculado con los encabezados del mensaje). Lanza `Exception` si el status no es 200. |
| `download_attachments` | `GET /users/{user}/messages/{id}/attachments` | Guarda en `download_path` los adjuntos tipo archivo y devuelve sus nombres. Crea la carpeta si no existe. |
| `send_email` | `POST /users/{from}/sendMail` | Envía HTML con adjuntos, CC y copia oculta (`bcc_recipients`); guarda copia en Enviados. Devuelve `True` si Graph responde 202; `False` (con `ERROR` en el log) en cualquier otro caso. No lanza excepciones. |
## Zona horaria
`get_emails` asume UTC-5 (Perú, sin horario de verano): el rango `fecha_inicio`–`fecha_fin` se convierte a UTC sumando 5 h (el fin incluye todo el día), y el campo `fecha` devuelto resta 5 h a `receivedDateTime`.
## Adjuntos de `send_email`
Los adjuntos se leen de las rutas indicadas. Una ruta inexistente se omite con un `WARNING`. El `contentType` se deduce de la extensión (`.pdf`, `.xml`, `.json`; cualquier otra, `application/octet-stream`).
## Notas
- `download_attachments` solo guarda adjuntos tipo `fileAttachment` con contenido; los adjuntos de tipo mensaje o referencia se ignoran.
- Dos correos con un adjunto del mismo nombre se sobrescriben entre sí si comparten `download_path` (el pipeline usa una única carpeta, `DOWNLOAD_FOLDER`).
- `VERIFY_SSL` se respeta en `get_access_token`, `get_emails` y `download_attachments`, pero `send_email` tiene `verify_ssl = False` fijo y lo ignora.
- El módulo ejecuta `warnings.filterwarnings("ignore")`, lo que silencia todas las advertencias de Python del proceso (incluidas las de certificado SSL).
- `get_access_token` retorna `None` (sin lanzar error) si MSAL devuelve un resultado vacío.
- `es_mensaje_automatico(headers)` marca como automáticos los mensajes con `Auto-Submitted` distinto de `no`, `X-Auto-Response-Suppress`, `X-Autoreply`, `Precedence` (`bulk`, `junk`, `list`, `auto_reply`) o `Content-Type` de informe de entrega (rebotes). El sistema no responde a esos mensajes.