from typing import Dict, Any, Optional import logging from datetime import datetime, UTC from O365 import Account, MSGraphProtocol from modules.methods.methodBase import MethodBase, 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" @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], authData: Optional[Dict[str, Any]] = None) -> MethodResult: """Execute Outlook method""" try: # Validate parameters if not await self.validateParameters(action, parameters): return self._createResult( success=False, data={"error": f"Invalid parameters for {action}"} ) # Get UserConnection from auth_data if not authData or "userConnection" not in authData: return self._createResult( success=False, data={"error": "UserConnection required for Outlook operations"} ) userConnection: UserConnection = authData["userConnection"] # Execute action if action == "readMails": return await self._readMails(parameters, userConnection) elif action == "sendMail": return await self._sendMail(parameters, userConnection) else: return self._createResult( success=False, data={"error": f"Unknown action: {action}"} ) except Exception as e: logger.error(f"Error executing Outlook {action}: {e}") return self._createResult( success=False, data={"error": str(e)} ) async def _readMails(self, parameters: Dict[str, Any], userConnection: UserConnection) -> MethodResult: """Read emails from Outlook""" try: folder = parameters.get("folder", "inbox") query = parameters.get("query") maxResults = parameters.get("maxResults", 10) includeAttachments = parameters.get("includeAttachments", False) # Create Outlook account account = Account( credentials=(userConnection.authToken, userConnection.refreshToken), protocol=MSGraphProtocol() ) # Get mailbox mailbox = account.mailbox() # Get folder targetFolder = mailbox.folder(folder_name=folder) # Get messages if query: messages = targetFolder.get_messages(query=query, limit=maxResults) else: messages = targetFolder.get_messages(limit=maxResults) # Process messages results = [] for message in messages: msgData = { "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 includeAttachments and message.has_attachments: attachments = [] for attachment in message.attachments: attachments.append({ "name": attachment.name, "contentType": attachment.content_type, "size": attachment.size }) msgData["attachments"] = attachments results.append(msgData) return self._createResult( success=True, data={ "folder": folder, "query": query, "messages": results } ) except Exception as e: logger.error(f"Error reading Outlook emails: {e}") return self._createResult( success=False, data={"error": f"Read failed: {str(e)}"} ) async def _sendMail(self, parameters: Dict[str, Any], userConnection: UserConnection) -> MethodResult: """Send email through Outlook""" try: toAddresses = parameters["to"] subject = parameters["subject"] body = parameters["body"] ccAddresses = parameters.get("cc", []) bccAddresses = parameters.get("bcc", []) attachments = parameters.get("attachments", []) # Create Outlook account account = Account( credentials=(userConnection.authToken, userConnection.refreshToken), protocol=MSGraphProtocol() ) # Get mailbox mailbox = account.mailbox() # Create new message message = mailbox.new_message() message.to.add(toAddresses) if ccAddresses: message.cc.add(ccAddresses) if bccAddresses: message.bcc.add(bccAddresses) message.subject = subject message.body = body # Add attachments for attachmentPath in attachments: message.attachments.add(attachmentPath) # Send message message.send() return self._createResult( success=True, data={ "to": toAddresses, "subject": subject, "sent": datetime.now(UTC).isoformat() } ) except Exception as e: logger.error(f"Error sending Outlook email: {e}") return self._createResult( success=False, data={"error": f"Send failed: {str(e)}"} )