248 lines
11 KiB
Python
248 lines
11 KiB
Python
# Copyright (c) 2025 Patrick Motsch
|
|
# All rights reserved.
|
|
|
|
import logging
|
|
from datetime import datetime, UTC
|
|
from modules.workflows.methods.methodBase import MethodBase
|
|
from modules.datamodels.datamodelWorkflowActions import WorkflowActionDefinition, WorkflowActionParameter
|
|
from modules.shared.frontendTypes import FrontendType
|
|
|
|
# Import helpers
|
|
from .helpers.connection import ConnectionHelper
|
|
from .helpers.emailProcessing import EmailProcessingHelper
|
|
from .helpers.folderManagement import FolderManagementHelper
|
|
|
|
# Import actions
|
|
from .actions.readEmails import readEmails
|
|
from .actions.searchEmails import searchEmails
|
|
from .actions.composeAndDraftEmailWithContext import composeAndDraftEmailWithContext
|
|
from .actions.sendDraftEmail import sendDraftEmail
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
class MethodOutlook(MethodBase):
|
|
"""Outlook method implementation for email operations"""
|
|
|
|
def __init__(self, services):
|
|
"""Initialize the Outlook method"""
|
|
super().__init__(services)
|
|
self.name = "outlook"
|
|
self.description = "Handle Microsoft Outlook email operations"
|
|
|
|
# Initialize helper modules
|
|
self.connection = ConnectionHelper(self)
|
|
self.emailProcessing = EmailProcessingHelper(self)
|
|
self.folderManagement = FolderManagementHelper(self)
|
|
|
|
# RBAC-Integration: Action-Definitionen mit actionId
|
|
self._actions = {
|
|
"readEmails": WorkflowActionDefinition(
|
|
actionId="outlook.readEmails",
|
|
description="Read emails and metadata from a mailbox folder",
|
|
dynamicMode=True,
|
|
parameters={
|
|
"connectionReference": WorkflowActionParameter(
|
|
name="connectionReference",
|
|
type="str",
|
|
frontendType=FrontendType.USER_CONNECTION,
|
|
required=True,
|
|
description="Microsoft connection label"
|
|
),
|
|
"folder": WorkflowActionParameter(
|
|
name="folder",
|
|
type="str",
|
|
frontendType=FrontendType.SELECT,
|
|
frontendOptions="outlook.folder",
|
|
required=False,
|
|
default="Inbox",
|
|
description="Folder to read from"
|
|
),
|
|
"limit": WorkflowActionParameter(
|
|
name="limit",
|
|
type="int",
|
|
frontendType=FrontendType.NUMBER,
|
|
required=False,
|
|
default=1000,
|
|
description="Maximum items to return",
|
|
validation={"min": 1, "max": 10000}
|
|
),
|
|
"filter": WorkflowActionParameter(
|
|
name="filter",
|
|
type="str",
|
|
frontendType=FrontendType.TEXT,
|
|
required=False,
|
|
description="Sender, query operators, or subject text"
|
|
),
|
|
"outputMimeType": WorkflowActionParameter(
|
|
name="outputMimeType",
|
|
type="str",
|
|
frontendType=FrontendType.SELECT,
|
|
frontendOptions=["application/json", "text/plain", "text/csv"],
|
|
required=False,
|
|
default="application/json",
|
|
description="MIME type for output file"
|
|
)
|
|
},
|
|
execute=readEmails.__get__(self, self.__class__)
|
|
),
|
|
"searchEmails": WorkflowActionDefinition(
|
|
actionId="outlook.searchEmails",
|
|
description="Search emails by query and return matching items with metadata",
|
|
dynamicMode=True,
|
|
parameters={
|
|
"connectionReference": WorkflowActionParameter(
|
|
name="connectionReference",
|
|
type="str",
|
|
frontendType=FrontendType.USER_CONNECTION,
|
|
required=True,
|
|
description="Microsoft connection label"
|
|
),
|
|
"query": WorkflowActionParameter(
|
|
name="query",
|
|
type="str",
|
|
frontendType=FrontendType.TEXT,
|
|
required=True,
|
|
description="Search expression"
|
|
),
|
|
"folder": WorkflowActionParameter(
|
|
name="folder",
|
|
type="str",
|
|
frontendType=FrontendType.SELECT,
|
|
frontendOptions="outlook.folder",
|
|
required=False,
|
|
default="All",
|
|
description="Folder scope or All"
|
|
),
|
|
"limit": WorkflowActionParameter(
|
|
name="limit",
|
|
type="int",
|
|
frontendType=FrontendType.NUMBER,
|
|
required=False,
|
|
default=1000,
|
|
description="Maximum items to return",
|
|
validation={"min": 1, "max": 10000}
|
|
),
|
|
"outputMimeType": WorkflowActionParameter(
|
|
name="outputMimeType",
|
|
type="str",
|
|
frontendType=FrontendType.SELECT,
|
|
frontendOptions=["application/json", "text/plain", "text/csv"],
|
|
required=False,
|
|
default="application/json",
|
|
description="MIME type for output file"
|
|
)
|
|
},
|
|
execute=searchEmails.__get__(self, self.__class__)
|
|
),
|
|
"composeAndDraftEmailWithContext": WorkflowActionDefinition(
|
|
actionId="outlook.composeAndDraftEmailWithContext",
|
|
description="Compose email content using AI from context and optional documents, then create a draft",
|
|
dynamicMode=True,
|
|
parameters={
|
|
"connectionReference": WorkflowActionParameter(
|
|
name="connectionReference",
|
|
type="str",
|
|
frontendType=FrontendType.USER_CONNECTION,
|
|
required=True,
|
|
description="Microsoft connection label"
|
|
),
|
|
"to": WorkflowActionParameter(
|
|
name="to",
|
|
type="List[str]",
|
|
frontendType=FrontendType.MULTISELECT,
|
|
required=False,
|
|
description="Recipient email addresses (optional for drafts)"
|
|
),
|
|
"context": WorkflowActionParameter(
|
|
name="context",
|
|
type="str",
|
|
frontendType=FrontendType.TEXTAREA,
|
|
required=False,
|
|
description="Detailed context for AI composition (omit when emailContent provided)"
|
|
),
|
|
"emailContent": WorkflowActionParameter(
|
|
name="emailContent",
|
|
type="dict",
|
|
frontendType=FrontendType.HIDDEN,
|
|
required=False,
|
|
description="Direct subject/body/to from upstream (skips AI composition)"
|
|
),
|
|
"documentList": WorkflowActionParameter(
|
|
name="documentList",
|
|
type="List[Any]",
|
|
frontendType=FrontendType.DOCUMENT_REFERENCE,
|
|
required=False,
|
|
description="Document references or inline ActionDocuments for attachments"
|
|
),
|
|
"cc": WorkflowActionParameter(
|
|
name="cc",
|
|
type="List[str]",
|
|
frontendType=FrontendType.MULTISELECT,
|
|
required=False,
|
|
description="CC recipients"
|
|
),
|
|
"bcc": WorkflowActionParameter(
|
|
name="bcc",
|
|
type="List[str]",
|
|
frontendType=FrontendType.MULTISELECT,
|
|
required=False,
|
|
description="BCC recipients"
|
|
),
|
|
"emailStyle": WorkflowActionParameter(
|
|
name="emailStyle",
|
|
type="str",
|
|
frontendType=FrontendType.SELECT,
|
|
frontendOptions=["formal", "casual", "business"],
|
|
required=False,
|
|
default="business",
|
|
description="Email style: formal, casual, or business"
|
|
),
|
|
"maxLength": WorkflowActionParameter(
|
|
name="maxLength",
|
|
type="int",
|
|
frontendType=FrontendType.NUMBER,
|
|
required=False,
|
|
default=1000,
|
|
description="Maximum length for generated content",
|
|
validation={"min": 100, "max": 10000}
|
|
)
|
|
},
|
|
execute=composeAndDraftEmailWithContext.__get__(self, self.__class__)
|
|
),
|
|
"sendDraftEmail": WorkflowActionDefinition(
|
|
actionId="outlook.sendDraftEmail",
|
|
description="Send draft email(s) using draft email JSON document(s) from action outlook.composeAndDraftEmailWithContext",
|
|
dynamicMode=True,
|
|
parameters={
|
|
"connectionReference": WorkflowActionParameter(
|
|
name="connectionReference",
|
|
type="str",
|
|
frontendType=FrontendType.USER_CONNECTION,
|
|
required=True,
|
|
description="Microsoft connection label"
|
|
),
|
|
"documentList": WorkflowActionParameter(
|
|
name="documentList",
|
|
type="List[str]",
|
|
frontendType=FrontendType.DOCUMENT_REFERENCE,
|
|
required=True,
|
|
description="Document reference(s) to draft emails in JSON format (outputs from outlook.composeAndDraftEmailWithContext function)"
|
|
)
|
|
},
|
|
execute=sendDraftEmail.__get__(self, self.__class__)
|
|
)
|
|
}
|
|
|
|
# Validate actions after definition
|
|
self._validateActions()
|
|
|
|
# Register actions as methods (optional, für direkten Zugriff)
|
|
self.readEmails = readEmails.__get__(self, self.__class__)
|
|
self.searchEmails = searchEmails.__get__(self, self.__class__)
|
|
self.composeAndDraftEmailWithContext = composeAndDraftEmailWithContext.__get__(self, self.__class__)
|
|
self.sendDraftEmail = sendDraftEmail.__get__(self, self.__class__)
|
|
|
|
def _format_timestamp_for_filename(self) -> str:
|
|
"""Format current timestamp as YYYYMMDD-hhmmss for filenames."""
|
|
return datetime.now(UTC).strftime("%Y%m%d-%H%M%S")
|
|
|