445 lines
No EOL
16 KiB
Python
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)
|
|
) |