import msal import requests import warnings import base64 import os from datetime import datetime, timedelta from config_loader import CONFIG warnings.filterwarnings("ignore") ######################################################## def get_access_token(): """ Obtiene un token de acceso de Microsoft Graph usando las credenciales configuradas en config.json (Client ID, Client Secret y Tenant ID). """ if not CONFIG or not CONFIG.outlook: raise ValueError("La configuración de Outlook no está cargada correctamente.") authority = f"https://login.microsoftonline.com/{CONFIG.outlook.tenant_id}" app = msal.ConfidentialClientApplication( CONFIG.outlook.client_id, authority=authority, client_credential=CONFIG.outlook.client_secret, verify=CONFIG.outlook.verify_ssl ) scopes = ["https://graph.microsoft.com/.default"] result = app.acquire_token_for_client(scopes=scopes) if "access_token" in result: return result["access_token"] else: error = result.get("error") error_description = result.get("error_description") correlation_id = result.get("correlation_id") raise Exception(f"Error al obtener token: {error} - {error_description} (ID: {correlation_id})") ######################################################## def get_emails(user_email, token, fecha_inicio, fecha_fin): """ Obtiene los correos de una cuenta específica en un rango de fechas. Retorna una lista de JSON con: id, fecha (ajustada -5h), sender, subject y has_attach. """ headers = { "Authorization": f"Bearer {token}", "Content-Type": "application/json" } dt_inicio_utc = datetime.strptime(fecha_inicio, "%Y-%m-%d") + timedelta(hours=5) dt_fin_utc = datetime.strptime(fecha_fin, "%Y-%m-%d") + timedelta(days=1, hours=5) filter_query = ( f"receivedDateTime ge {dt_inicio_utc.strftime('%Y-%m-%dT%H:%M:%SZ')} and " f"receivedDateTime lt {dt_fin_utc.strftime('%Y-%m-%dT%H:%M:%SZ')}" ) params = { "$filter": filter_query, "$select": "id,receivedDateTime,hasAttachments,subject,from", "$top": 50 } url = f"https://graph.microsoft.com/v1.0/users/{user_email}/messages" all_mails = [] while url: response = requests.get( url, headers=headers, params=params, verify=CONFIG.outlook.verify_ssl ) if response.status_code != 200: raise Exception(f"Error Graph API ({user_email}): {response.status_code} - {response.text}") data = response.json() messages = data.get("value", []) for msg in messages: utc_str = msg.get("receivedDateTime") dt_utc = datetime.fromisoformat(utc_str.replace("Z", "+00:00")) dt_local = dt_utc - timedelta(hours=5) from_obj = msg.get("from", {}) sender = from_obj.get("emailAddress", {}).get("address", "Desconocido") all_mails.append({ "id": msg.get("id"), "fecha": dt_local.strftime("%Y-%m-%d %H:%M:%S"), "sender": sender, "subject": msg.get("subject", ""), "has_attach": msg.get("hasAttachments") }) url = data.get("@odata.nextLink") params = None return all_mails ######################################################## def download_attachments(user_email, message_id, token, download_path): """ Descarga los adjuntos de un mensaje específico y los guarda en la ruta indicada. Retorna una lista con los nombres de los archivos descargados. """ if not os.path.exists(download_path): os.makedirs(download_path, exist_ok=True) headers = { "Authorization": f"Bearer {token}", "Content-Type": "application/json" } url = f"https://graph.microsoft.com/v1.0/users/{user_email}/messages/{message_id}/attachments" response = requests.get(url, headers=headers, verify=CONFIG.outlook.verify_ssl) if response.status_code != 200: raise Exception(f"Error al obtener adjuntos ({message_id}): {response.status_code} - {response.text}") attachments = response.json().get("value", []) downloaded_files = [] for attachment in attachments: if attachment.get("@odata.type") == "#microsoft.graph.fileAttachment": file_name = attachment.get("name") content_bytes = attachment.get("contentBytes") if content_bytes: file_path = os.path.join(download_path, file_name) with open(file_path, "wb") as f: f.write(base64.b64decode(content_bytes)) downloaded_files.append(file_name) return downloaded_files ######################################################## if __name__ == "__main__": try: print("Intentando obtener token de acceso...") token = get_access_token() print("✅ Token obtenido exitosamente.") if CONFIG.mails: test_email = CONFIG.mails[0].email hoy = datetime.now() ayer = hoy - timedelta(days=1) f_inicio = ayer.strftime("%Y-%m-%d") f_fin = hoy.strftime("%Y-%m-%d") print(f"\nBuscando correos para {test_email}...") emails = get_emails(test_email, token, f_inicio, f_fin) print(f"✅ Se encontraron {len(emails)} correos.") for e in emails: if e['has_attach']: print(f"Probando descarga para correo ID: {e['id'][:10]}...") archivos = download_attachments(test_email, e['id'], token, "./test_download") print(f"Descargados: {archivos}") break except Exception as e: print(f"❌ Error: {e}")