from typing import Dict, Any, Optional import logging from datetime import datetime, UTC from O365 import Account, MSGraphProtocol from modules.methods.methodBase import MethodBase, AuthSource, MethodResult from modules.models.userConnection import UserConnection logger = logging.getLogger(__name__) class MethodOutlook(MethodBase): """Outlook method implementation for email operations""" def __init__(self): super().__init__() self.name = "outlook" self.description = "Handle Outlook email operations like reading and sending emails" self.auth_source = AuthSource.MICROSOFT @property def actions(self) -> Dict[str, Dict[str, Any]]: """Available actions and their parameters""" return { "readMails": { "description": "Read emails from Outlook", "retryMax": 2, "timeout": 30, "parameters": { "folder": {"type": "string", "required": False}, "query": {"type": "string", "required": False}, "maxResults": {"type": "number", "required": False}, "includeAttachments": {"type": "boolean", "required": False} } }, "sendMail": { "description": "Send email through Outlook", "retryMax": 2, "timeout": 30, "parameters": { "to": {"type": "array", "items": "string", "required": True}, "subject": {"type": "string", "required": True}, "body": {"type": "string", "required": True}, "cc": {"type": "array", "items": "string", "required": False}, "bcc": {"type": "array", "items": "string", "required": False}, "attachments": {"type": "array", "items": "string", "required": False} } } } async def execute(self, action: str, parameters: Dict[str, Any], auth_data: Optional[Dict[str, Any]] = None) -> MethodResult: """Execute Outlook method""" try: # Validate parameters if not await self.validate_parameters(action, parameters): return self._create_result( success=False, data={"error": f"Invalid parameters for {action}"} ) # Get UserConnection from auth_data if not auth_data or "userConnection" not in auth_data: return self._create_result( success=False, data={"error": "UserConnection required for Outlook operations"} ) user_connection: UserConnection = auth_data["userConnection"] # Execute action if action == "readMails": return await self._read_mails(parameters, user_connection) elif action == "sendMail": return await self._send_mail(parameters, user_connection) else: return self._create_result( success=False, data={"error": f"Unknown action: {action}"} ) except Exception as e: logger.error(f"Error executing Outlook {action}: {e}") return self._create_result( success=False, data={"error": str(e)} ) async def _read_mails(self, parameters: Dict[str, Any], user_connection: UserConnection) -> MethodResult: """Read emails from Outlook""" try: folder = parameters.get("folder", "inbox") query = parameters.get("query") max_results = parameters.get("maxResults", 10) include_attachments = parameters.get("includeAttachments", False) # Create Outlook account account = Account( credentials=(user_connection.authToken, user_connection.refreshToken), protocol=MSGraphProtocol() ) # Get mailbox mailbox = account.mailbox() # Get folder target_folder = mailbox.folder(folder_name=folder) # Get messages if query: messages = target_folder.get_messages(query=query, limit=max_results) else: messages = target_folder.get_messages(limit=max_results) # Process messages results = [] for message in messages: msg_data = { "id": message.object_id, "subject": message.subject, "from": message.sender.address, "to": [to.address for to in message.to], "cc": [cc.address for cc in message.cc], "received": message.received.strftime("%Y-%m-%d %H:%M:%S"), "body": message.body, "hasAttachments": message.has_attachments } if include_attachments and message.has_attachments: attachments = [] for attachment in message.attachments: attachments.append({ "name": attachment.name, "contentType": attachment.content_type, "size": attachment.size }) msg_data["attachments"] = attachments results.append(msg_data) return self._create_result( success=True, data={ "folder": folder, "query": query, "messages": results } ) except Exception as e: logger.error(f"Error reading Outlook emails: {e}") return self._create_result( success=False, data={"error": f"Read failed: {str(e)}"} ) async def _send_mail(self, parameters: Dict[str, Any], user_connection: UserConnection) -> MethodResult: """Send email through Outlook""" try: to_addresses = parameters["to"] subject = parameters["subject"] body = parameters["body"] cc_addresses = parameters.get("cc", []) bcc_addresses = parameters.get("bcc", []) attachments = parameters.get("attachments", []) # Create Outlook account account = Account( credentials=(user_connection.authToken, user_connection.refreshToken), protocol=MSGraphProtocol() ) # Get mailbox mailbox = account.mailbox() # Create new message message = mailbox.new_message() message.to.add(to_addresses) if cc_addresses: message.cc.add(cc_addresses) if bcc_addresses: message.bcc.add(bcc_addresses) message.subject = subject message.body = body # Add attachments for attachment_path in attachments: message.attachments.add(attachment_path) # Send message message.send() return self._create_result( success=True, data={ "to": to_addresses, "subject": subject, "sent": datetime.now(UTC).isoformat() } ) except Exception as e: logger.error(f"Error sending Outlook email: {e}") return self._create_result( success=False, data={"error": f"Send failed: {str(e)}"} )