gateway/modules/methods/methodOutlook.py
2025-07-04 15:10:26 +02:00

445 lines
No EOL
16 KiB
Python

"""
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
from modules.workflow.methodBase import MethodBase, ActionResult, action
logger = logging.getLogger(__name__)
class OutlookService:
"""Service for Microsoft Outlook operations using Graph API"""
def __init__(self, serviceContainer: Any):
self.serviceContainer = serviceContainer
def _getMicrosoftConnection(self, connectionReference: str) -> Optional[Dict[str, Any]]:
"""Get Microsoft connection from connection reference"""
try:
userConnection = self.serviceContainer.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.serviceContainer.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
async def readMails(self, connectionReference: str, folder: str = "inbox", query: str = None, maxResults: int = 10, includeAttachments: bool = False) -> Dict[str, Any]:
"""Read emails from Outlook using Microsoft Graph API"""
try:
connection = self._getMicrosoftConnection(connectionReference)
if not connection:
return {
"error": "No valid Microsoft connection found for the provided connection reference",
"connectionReference": connectionReference
}
# For now, simulate email reading
# In a real implementation, you would use Microsoft Graph API
mail_prompt = f"""
Read emails from Outlook.
Folder: {folder}
Query: {query or 'All emails'}
Max Results: {maxResults}
Include Attachments: {includeAttachments}
Please provide:
1. Email messages with subject, sender, and content
2. Timestamps and priority levels
3. Attachment information if requested
4. Email threading and conversations
5. Categorization and flags
"""
# Use AI to simulate email data
mail_data = await self.serviceContainer.interfaceAiCalls.callAiTextAdvanced(mail_prompt)
return {
"folder": folder,
"query": query,
"maxResults": maxResults,
"includeAttachments": includeAttachments,
"messages": mail_data,
"connection": {
"id": connection["id"],
"authority": "microsoft",
"reference": connectionReference
}
}
except Exception as e:
logger.error(f"Error reading emails: {str(e)}")
return {
"error": str(e)
}
async def sendMail(self, connectionReference: str, to: List[str], subject: str, body: str, attachments: List[str] = None) -> Dict[str, Any]:
"""Send email using Outlook using Microsoft Graph API"""
try:
connection = self._getMicrosoftConnection(connectionReference)
if not connection:
return {
"error": "No valid Microsoft connection found for the provided connection reference",
"connectionReference": connectionReference
}
# For now, simulate email sending
# In a real implementation, you would use Microsoft Graph API
send_prompt = f"""
Send email using Outlook.
To: {', '.join(to)}
Subject: {subject}
Body: {body}
Attachments: {attachments or 'None'}
Please provide:
1. Email composition details
2. Recipient validation
3. Attachment processing
4. Send confirmation
5. Message tracking information
"""
# Use AI to simulate email sending
send_result = await self.serviceContainer.interfaceAiCalls.callAiTextAdvanced(send_prompt)
return {
"to": to,
"subject": subject,
"body": body,
"attachments": attachments,
"result": send_result,
"connection": {
"id": connection["id"],
"authority": "microsoft",
"reference": connectionReference
}
}
except Exception as e:
logger.error(f"Error sending email: {str(e)}")
return {
"error": str(e)
}
async def createFolder(self, connectionReference: str, name: str, parentFolderId: str = None) -> Dict[str, Any]:
"""Create folder in Outlook using Microsoft Graph API"""
try:
connection = self._getMicrosoftConnection(connectionReference)
if not connection:
return {
"error": "No valid Microsoft connection found for the provided connection reference",
"connectionReference": connectionReference
}
# For now, simulate folder creation
# In a real implementation, you would use Microsoft Graph API
folder_prompt = f"""
Create folder in Outlook.
Name: {name}
Parent Folder ID: {parentFolderId or 'Root'}
Please provide:
1. Folder creation details
2. Permission settings
3. Folder structure and hierarchy
4. Creation confirmation
5. Folder properties and metadata
"""
# Use AI to simulate folder creation
folder_result = await self.serviceContainer.interfaceAiCalls.callAiTextAdvanced(folder_prompt)
return {
"name": name,
"parentFolderId": parentFolderId,
"result": folder_result,
"connection": {
"id": connection["id"],
"authority": "microsoft",
"reference": connectionReference
}
}
except Exception as e:
logger.error(f"Error creating folder: {str(e)}")
return {
"error": str(e)
}
async def moveMail(self, connectionReference: str, messageId: str, targetFolderId: str) -> Dict[str, Any]:
"""Move email to different folder using Microsoft Graph API"""
try:
connection = self._getMicrosoftConnection(connectionReference)
if not connection:
return {
"error": "No valid Microsoft connection found for the provided connection reference",
"connectionReference": connectionReference
}
# For now, simulate mail moving
# In a real implementation, you would use Microsoft Graph API
move_prompt = f"""
Move email to different folder.
Message ID: {messageId}
Target Folder ID: {targetFolderId}
Please provide:
1. Move operation details
2. Source and destination folder information
3. Message preservation and metadata
4. Move confirmation
5. Updated folder structure
"""
# Use AI to simulate mail moving
move_result = await self.serviceContainer.interfaceAiCalls.callAiTextAdvanced(move_prompt)
return {
"messageId": messageId,
"targetFolderId": targetFolderId,
"result": move_result,
"connection": {
"id": connection["id"],
"authority": "microsoft",
"reference": connectionReference
}
}
except Exception as e:
logger.error(f"Error moving email: {str(e)}")
return {
"error": str(e)
}
class MethodOutlook(MethodBase):
"""Outlook method implementation for email operations"""
def __init__(self, serviceContainer: Any):
"""Initialize the Outlook method"""
super().__init__(serviceContainer)
self.name = "outlook"
self.description = "Handle Outlook email operations like reading and sending emails"
self.outlookService = OutlookService(serviceContainer)
@action
async def readMails(self, parameters: Dict[str, Any]) -> ActionResult:
"""
Read emails from Outlook
Parameters:
connectionReference (str): Reference to the Microsoft connection
folder (str, optional): Folder to read from (default: "inbox")
query (str, optional): Search query to filter emails
maxResults (int, optional): Maximum number of results (default: 10)
includeAttachments (bool, optional): Whether to include attachments (default: False)
"""
try:
connectionReference = parameters.get("connectionReference")
folder = parameters.get("folder", "inbox")
query = parameters.get("query")
maxResults = parameters.get("maxResults", 10)
includeAttachments = parameters.get("includeAttachments", False)
if not connectionReference:
return self._createResult(
success=False,
data={},
error="Connection reference is required"
)
# Read emails
messages = await self.outlookService.readMails(
connectionReference=connectionReference,
folder=folder,
query=query,
maxResults=maxResults,
includeAttachments=includeAttachments
)
return self._createResult(
success=True,
data=messages
)
except Exception as e:
logger.error(f"Error reading emails: {str(e)}")
return self._createResult(
success=False,
data={},
error=str(e)
)
@action
async def sendMail(self, parameters: Dict[str, Any]) -> ActionResult:
"""
Send email using 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
attachments (List[str], optional): List of attachment file IDs
"""
try:
connectionReference = parameters.get("connectionReference")
to = parameters.get("to", [])
subject = parameters.get("subject")
body = parameters.get("body")
attachments = parameters.get("attachments", [])
if not connectionReference:
return self._createResult(
success=False,
data={},
error="Connection reference is required"
)
if not to or not subject or not body:
return self._createResult(
success=False,
data={},
error="To, subject, and body are required"
)
# Send email
result = await self.outlookService.sendMail(
connectionReference=connectionReference,
to=to,
subject=subject,
body=body,
attachments=attachments
)
return self._createResult(
success=True,
data=result
)
except Exception as e:
logger.error(f"Error sending email: {str(e)}")
return self._createResult(
success=False,
data={},
error=str(e)
)
@action
async def createFolder(self, parameters: Dict[str, Any]) -> ActionResult:
"""
Create folder in Outlook
Parameters:
connectionReference (str): Reference to the Microsoft connection
name (str): Folder name
parentFolderId (str, optional): Parent folder ID
"""
try:
connectionReference = parameters.get("connectionReference")
name = parameters.get("name")
parentFolderId = parameters.get("parentFolderId")
if not connectionReference:
return self._createResult(
success=False,
data={},
error="Connection reference is required"
)
if not name:
return self._createResult(
success=False,
data={},
error="Folder name is required"
)
# Create folder
folder = await self.outlookService.createFolder(
connectionReference=connectionReference,
name=name,
parentFolderId=parentFolderId
)
return self._createResult(
success=True,
data=folder
)
except Exception as e:
logger.error(f"Error creating folder: {str(e)}")
return self._createResult(
success=False,
data={},
error=str(e)
)
@action
async def moveMail(self, parameters: Dict[str, Any]) -> ActionResult:
"""
Move email to different folder
Parameters:
connectionReference (str): Reference to the Microsoft connection
messageId (str): ID of the message to move
targetFolderId (str): ID of the target folder
"""
try:
connectionReference = parameters.get("connectionReference")
messageId = parameters.get("messageId")
targetFolderId = parameters.get("targetFolderId")
if not connectionReference:
return self._createResult(
success=False,
data={},
error="Connection reference is required"
)
if not messageId or not targetFolderId:
return self._createResult(
success=False,
data={},
error="Message ID and target folder ID are required"
)
# Move email
result = await self.outlookService.moveMail(
connectionReference=connectionReference,
messageId=messageId,
targetFolderId=targetFolderId
)
return self._createResult(
success=True,
data=result
)
except Exception as e:
logger.error(f"Error moving email: {str(e)}")
return self._createResult(
success=False,
data={},
error=str(e)
)