primer commit
This commit is contained in:
commit
83f704c85d
12 files changed
+717
No files matched your search
+179
@@ -0,0 +1,179 @@
|
||||
import requests
|
||||
import msal
|
||||
import os
|
||||
import base64
|
||||
import warnings
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from config_loader import CONFIG
|
||||
|
||||
warnings.filterwarnings("ignore")
|
||||
########################################################
|
||||
def get_access_token():
|
||||
"""Obtiene el token de acceso usando MSAL (Client Credentials Flow)."""
|
||||
outlook_config = CONFIG.outlook
|
||||
client_id = outlook_config.client_id
|
||||
client_secret = outlook_config.client_secret
|
||||
tenant_id = outlook_config.tenant_id
|
||||
authority = f"https://login.microsoftonline.com/{tenant_id}"
|
||||
|
||||
# Manejo de SSL
|
||||
verify_ssl = outlook_config.verify_ssl
|
||||
ca_bundle = outlook_config.ca_bundle
|
||||
|
||||
# MSAL usa requests internamente, podemos pasar proxies/verify
|
||||
# a través de la configuración si fuera necesario, pero msal tiene su propia forma
|
||||
# de configurar el http_client. Por simplicidad, si verify_ssl es False,
|
||||
# podemos intentar configurar el ambiente o pasar un cliente custom.
|
||||
|
||||
app = msal.ConfidentialClientApplication(
|
||||
client_id,
|
||||
authority=authority,
|
||||
client_credential=client_secret,
|
||||
verify=verify_ssl if ca_bundle is None else ca_bundle
|
||||
)
|
||||
|
||||
# El scope para Graph API en modo aplicación suele ser 'https://graph.microsoft.com/.default'
|
||||
scopes = ["https://graph.microsoft.com/.default"]
|
||||
|
||||
try:
|
||||
result = app.acquire_token_silent(scopes, account=None)
|
||||
if not result:
|
||||
result = app.acquire_token_for_client(scopes=scopes)
|
||||
|
||||
if "access_token" in result:
|
||||
return result["access_token"]
|
||||
else:
|
||||
raise Exception(f"Error obteniendo token: {result.get('error_description')}")
|
||||
except Exception as e:
|
||||
print(f"Error obteniendo token: {e}")
|
||||
return None
|
||||
|
||||
########################################################
|
||||
def get_messages(access_token, user_email, date_filter=None, end_date_filter=None):
|
||||
"""Obtiene todos los mensajes de un correo específico, manejando la paginación de Graph API."""
|
||||
url = f"https://graph.microsoft.com/v1.0/users/{user_email}/messages"
|
||||
headers = {
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
|
||||
outlook_config = CONFIG.outlook
|
||||
verify = outlook_config.verify_ssl if outlook_config.ca_bundle is None else outlook_config.ca_bundle
|
||||
|
||||
# Solo los mensajes que contengan adjuntos
|
||||
filter_query = 'hasAttachments eq true'
|
||||
|
||||
if date_filter:
|
||||
filter_query += f" and receivedDateTime ge {date_filter}"
|
||||
|
||||
if end_date_filter:
|
||||
filter_query += f" and receivedDateTime le {end_date_filter}"
|
||||
|
||||
params = {
|
||||
'$filter': filter_query,
|
||||
'$select': 'id,subject,receivedDateTime,hasAttachments,from',
|
||||
'$top': 50 # Podemos pedir hasta 999, pero 50 es un buen default para probar paginación
|
||||
}
|
||||
|
||||
all_messages = []
|
||||
next_link = url
|
||||
|
||||
while next_link:
|
||||
# En la primera iteración usamos url + params.
|
||||
# En las siguientes, next_link ya trae los filtros y el token de skip en la URL.
|
||||
if next_link == url:
|
||||
response = requests.get(next_link, headers=headers, params=params, verify=verify)
|
||||
else:
|
||||
response = requests.get(next_link, headers=headers, verify=verify)
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
messages = data.get('value', [])
|
||||
all_messages.extend(messages)
|
||||
next_link = data.get('@odata.nextLink')
|
||||
else:
|
||||
print(f"Error obteniendo mensajes para {user_email}: {response.text}")
|
||||
break
|
||||
|
||||
return all_messages
|
||||
|
||||
########################################################
|
||||
def send_email(access_token, from_email, to_recipients, subject, html_body, attachment_paths=None):
|
||||
"""
|
||||
Envía un correo electrónico con cuerpo HTML y múltiples adjuntos PDF.
|
||||
|
||||
:param access_token: Token de acceso de Microsoft Graph API.
|
||||
:param from_email: Correo electrónico del remitente (cuenta de Office 365).
|
||||
:param to_recipients: Lista de correos electrónicos de los destinatarios.
|
||||
:param subject: Asunto del correo.
|
||||
:param html_body: Contenido del correo en formato HTML.
|
||||
:param attachment_paths: Lista de rutas a los archivos PDF que se adjuntarán.
|
||||
:return: True si se envió correctamente, False en caso contrario.
|
||||
"""
|
||||
url = f"https://graph.microsoft.com/v1.0/users/{from_email}/sendMail"
|
||||
headers = {
|
||||
'Authorization': f'Bearer {access_token}',
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
|
||||
# Configuración de SSL desde CONFIG
|
||||
outlook_config = CONFIG.outlook
|
||||
verify = outlook_config.verify_ssl if outlook_config.ca_bundle is None else outlook_config.ca_bundle
|
||||
|
||||
# Construir lista de destinatarios para Graph API
|
||||
recipients_payload = []
|
||||
for email in to_recipients:
|
||||
recipients_payload.append({
|
||||
"emailAddress": {
|
||||
"address": email
|
||||
}
|
||||
})
|
||||
|
||||
# Construir lista de adjuntos
|
||||
attachments_payload = []
|
||||
if attachment_paths:
|
||||
for path in attachment_paths:
|
||||
if os.path.exists(path):
|
||||
with open(path, "rb") as f:
|
||||
content_bytes = f.read()
|
||||
|
||||
# Codificar el contenido en Base64
|
||||
encoded_content = base64.b64encode(content_bytes).decode('utf-8')
|
||||
filename = os.path.basename(path)
|
||||
|
||||
attachments_payload.append({
|
||||
"@odata.type": "#microsoft.graph.fileAttachment",
|
||||
"name": filename,
|
||||
"contentType": "application/pdf",
|
||||
"contentBytes": encoded_content
|
||||
})
|
||||
else:
|
||||
print(f"Advertencia: El archivo no existe y no se adjuntará: {path}")
|
||||
|
||||
# Estructura del mensaje para la API
|
||||
email_data = {
|
||||
"message": {
|
||||
"subject": subject,
|
||||
"body": {
|
||||
"contentType": "HTML",
|
||||
"content": html_body
|
||||
},
|
||||
"toRecipients": recipients_payload,
|
||||
"attachments": attachments_payload
|
||||
},
|
||||
"saveToSentItems": "true"
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.post(url, headers=headers, json=email_data, verify=verify)
|
||||
if response.status_code == 202:
|
||||
print(f"SUCCESS: Correo enviado exitosamente a: {', '.join(to_recipients)}")
|
||||
return True
|
||||
else:
|
||||
print(f"ERROR: Al enviar correo: {response.status_code} - {response.text}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"ERROR: Excepcion al enviar correo: {e}")
|
||||
return False
|
||||
|
||||
########################################################
|
||||
Reference in new issue
Block a user