""" Outlook method module. Handles Outlook operations using the Outlook service. """ import logging from typing import Dict, Any, List, Optional from datetime import datetime, UTC import json import uuid from modules.chat.methodBase import MethodBase, ActionResult, action logger = logging.getLogger(__name__) class MethodOutlook(MethodBase): """Outlook method implementation for email operations""" def __init__(self, serviceCenter: Any): """Initialize the Outlook method""" super().__init__(serviceCenter) self.name = "outlook" self.description = "Handle Microsoft Outlook email operations" def _getMicrosoftConnection(self, connectionReference: str) -> Optional[Dict[str, Any]]: """Get Microsoft connection from connection reference""" try: userConnection = self.service.getUserConnectionFromConnectionReference(connectionReference) if not userConnection or userConnection.authority != "msft" or userConnection.status != "active": return None # Get the corresponding token for this user and authority token = self.service.interfaceApp.getToken(userConnection.authority) if not token: logger.warning(f"No token found for user {userConnection.userId} and authority {userConnection.authority}") return None return { "id": userConnection.id, "accessToken": token.tokenAccess, "refreshToken": token.tokenRefresh, "scopes": ["Mail.ReadWrite", "User.Read"] # Default Microsoft scopes } except Exception as e: logger.error(f"Error getting Microsoft connection: {str(e)}") return None @action async def readEmails(self, parameters: Dict[str, Any]) -> ActionResult: """ Read emails from Outlook Parameters: connectionReference (str): Reference to the Microsoft connection folder (str, optional): Email folder to read from (default: "Inbox") limit (int, optional): Maximum number of emails to read (default: 10) filter (str, optional): Filter criteria for emails expectedDocumentFormats (list, optional): Expected document formats with extension, mimeType, description """ try: connectionReference = parameters.get("connectionReference") folder = parameters.get("folder", "Inbox") limit = parameters.get("limit", 10) filter = parameters.get("filter") expectedDocumentFormats = parameters.get("expectedDocumentFormats", []) if not connectionReference: return self._createResult( success=False, data={}, error="Connection reference is required" ) # Get Microsoft connection connection = self._getMicrosoftConnection(connectionReference) if not connection: return self._createResult( success=False, data={}, error="No valid Microsoft connection found for the provided connection reference" ) # Create email reading prompt email_prompt = f""" Simulate reading emails from Microsoft Outlook. Connection: {connection['id']} Folder: {folder} Limit: {limit} Filter: {filter or 'None'} Please provide: 1. List of emails with subject, sender, date, and content 2. Summary of email statistics 3. Important or urgent emails highlighted 4. Email categorization if possible """ # Use AI to simulate email reading email_data = await self.service.interfaceAiCalls.callAiTextAdvanced(email_prompt) # Create result data result_data = { "connectionReference": connectionReference, "folder": folder, "limit": limit, "filter": filter, "emails": email_data, "connection": { "id": connection["id"], "authority": "microsoft", "reference": connectionReference }, "timestamp": datetime.now(UTC).isoformat() } # Determine output format based on expected formats output_extension = ".json" # Default output_mime_type = "application/json" # Default if expectedDocumentFormats and len(expectedDocumentFormats) > 0: # Use the first expected format expected_format = expectedDocumentFormats[0] output_extension = expected_format.get("extension", ".json") output_mime_type = expected_format.get("mimeType", "application/json") logger.info(f"Using expected format: {output_extension} ({output_mime_type})") else: logger.info("No expected format specified, using default .json format") return self._createResult( success=True, data={ "documents": [ { "documentName": f"outlook_emails_{datetime.now(UTC).strftime('%Y%m%d_%H%M%S')}{output_extension}", "documentData": result_data, "mimeType": output_mime_type } ] } ) except Exception as e: logger.error(f"Error reading emails: {str(e)}") return self._createResult( success=False, data={}, error=str(e) ) @action async def sendEmail(self, parameters: Dict[str, Any]) -> ActionResult: """ Send email via Outlook Parameters: connectionReference (str): Reference to the Microsoft connection to (List[str]): List of recipient email addresses subject (str): Email subject body (str): Email body content cc (List[str], optional): CC recipients bcc (List[str], optional): BCC recipients expectedDocumentFormats (list, optional): Expected document formats with extension, mimeType, description """ try: connectionReference = parameters.get("connectionReference") to = parameters.get("to") subject = parameters.get("subject") body = parameters.get("body") cc = parameters.get("cc", []) bcc = parameters.get("bcc", []) expectedDocumentFormats = parameters.get("expectedDocumentFormats", []) if not connectionReference or not to or not subject or not body: return self._createResult( success=False, data={}, error="Connection reference, to, subject, and body are required" ) # Get Microsoft connection connection = self._getMicrosoftConnection(connectionReference) if not connection: return self._createResult( success=False, data={}, error="No valid Microsoft connection found for the provided connection reference" ) # Create email sending prompt send_prompt = f""" Simulate sending an email via Microsoft Outlook. Connection: {connection['id']} To: {to} Subject: {subject} Body: {body} CC: {cc} BCC: {bcc} Please provide: 1. Email composition details 2. Validation of email addresses 3. Email formatting and structure 4. Delivery confirmation simulation """ # Use AI to simulate email sending send_result = await self.service.interfaceAiCalls.callAiTextAdvanced(send_prompt) # Create result data result_data = { "connectionReference": connectionReference, "to": to, "subject": subject, "body": body, "cc": cc, "bcc": bcc, "sendResult": send_result, "connection": { "id": connection["id"], "authority": "microsoft", "reference": connectionReference }, "timestamp": datetime.now(UTC).isoformat() } # Determine output format based on expected formats output_extension = ".json" # Default output_mime_type = "application/json" # Default if expectedDocumentFormats and len(expectedDocumentFormats) > 0: # Use the first expected format expected_format = expectedDocumentFormats[0] output_extension = expected_format.get("extension", ".json") output_mime_type = expected_format.get("mimeType", "application/json") logger.info(f"Using expected format: {output_extension} ({output_mime_type})") else: logger.info("No expected format specified, using default .json format") return self._createResult( success=True, data={ "documents": [ { "documentName": f"outlook_email_sent_{datetime.now(UTC).strftime('%Y%m%d_%H%M%S')}{output_extension}", "documentData": result_data, "mimeType": output_mime_type } ] } ) except Exception as e: logger.error(f"Error sending email: {str(e)}") return self._createResult( success=False, data={}, error=str(e) ) @action async def searchEmails(self, parameters: Dict[str, Any]) -> ActionResult: """ Search emails in Outlook Parameters: connectionReference (str): Reference to the Microsoft connection query (str): Search query folder (str, optional): Folder to search in (default: "All") limit (int, optional): Maximum number of results (default: 20) expectedDocumentFormats (list, optional): Expected document formats with extension, mimeType, description """ try: connectionReference = parameters.get("connectionReference") query = parameters.get("query") folder = parameters.get("folder", "All") limit = parameters.get("limit", 20) expectedDocumentFormats = parameters.get("expectedDocumentFormats", []) if not connectionReference or not query: return self._createResult( success=False, data={}, error="Connection reference and query are required" ) # Get Microsoft connection connection = self._getMicrosoftConnection(connectionReference) if not connection: return self._createResult( success=False, data={}, error="No valid Microsoft connection found for the provided connection reference" ) # Create email search prompt search_prompt = f""" Simulate searching emails in Microsoft Outlook. Connection: {connection['id']} Query: {query} Folder: {folder} Limit: {limit} Please provide: 1. Search results with relevant emails 2. Search statistics and relevance scores 3. Email previews and key information 4. Search suggestions and refinements """ # Use AI to simulate email search search_result = await self.service.interfaceAiCalls.callAiTextAdvanced(search_prompt) # Create result data result_data = { "connectionReference": connectionReference, "query": query, "folder": folder, "limit": limit, "searchResults": search_result, "connection": { "id": connection["id"], "authority": "microsoft", "reference": connectionReference }, "timestamp": datetime.now(UTC).isoformat() } # Determine output format based on expected formats output_extension = ".json" # Default output_mime_type = "application/json" # Default if expectedDocumentFormats and len(expectedDocumentFormats) > 0: # Use the first expected format expected_format = expectedDocumentFormats[0] output_extension = expected_format.get("extension", ".json") output_mime_type = expected_format.get("mimeType", "application/json") logger.info(f"Using expected format: {output_extension} ({output_mime_type})") else: logger.info("No expected format specified, using default .json format") return self._createResult( success=True, data={ "documents": [ { "documentName": f"outlook_email_search_{datetime.now(UTC).strftime('%Y%m%d_%H%M%S')}{output_extension}", "documentData": result_data, "mimeType": output_mime_type } ] } ) except Exception as e: logger.error(f"Error searching emails: {str(e)}") return self._createResult( success=False, data={}, error=str(e) )