# Copyright (c) 2025 Patrick Motsch
# All rights reserved.
"""
Messaging service for sending messages across different channels.
Provides subscription-based messaging functionality.
"""
import logging
import re
from typing import List, Optional, Callable
from modules.datamodels.datamodelMessaging import (
MessagingSubscription,
MessagingSubscriptionRegistration,
MessagingDelivery,
MessagingChannel,
MessagingEventParameters,
MessagingSendResult,
MessagingSubscriptionExecutionResult,
DeliveryStatus
)
from modules.interfaces.interfaceMessaging import getInterface as getMessagingInterface
from modules.shared.timeUtils import getUtcTimestamp
logger = logging.getLogger(__name__)
class MessagingService:
"""
Messaging service providing subscription-based messaging functionality.
"""
def __init__(self, services):
"""Initialize messaging service with service center access.
Args:
services: Service center instance providing access to interfaces
"""
self.services = services
self._messagingInterface = None
def sendMessage(
self,
subject: str,
message: str,
registration: MessagingSubscriptionRegistration
) -> MessagingSendResult:
"""
Sendet eine Nachricht über einen Channel an einen User.
Erstellt MessagingDelivery Record.
Args:
subject: Subject der Nachricht (für E-Mail, leer für SMS)
message: Nachrichtentext
registration: MessagingSubscriptionRegistration mit Channel-Info und userId
Returns:
MessagingSendResult mit Status und Delivery-ID
"""
# Erstelle Delivery Record
delivery = MessagingDelivery(
subscriptionId=registration.subscriptionId,
userId=registration.userId,
channel=registration.channel,
status=DeliveryStatus.PENDING
)
# Speichere Delivery Record
try:
deliveryRecord = self.services.interfaceDbComponent.createDelivery(delivery)
except Exception as e:
logger.error(f"Failed to create delivery record: {str(e)}")
return MessagingSendResult(
success=False,
errorMessage=f"Failed to create delivery record: {str(e)}"
)
try:
# Convert plain text to HTML for email channel
messageToSend = message
if registration.channel == MessagingChannel.EMAIL:
messageToSend = self._textToHtml(message)
# Versende über interfaceMessaging
success = self._getMessagingInterface().send(
channel=registration.channel,
recipient=registration.channelConfig,
subject=subject,
message=messageToSend
)
if success:
# Update Delivery Record
self.services.interfaceDbComponent.updateDelivery(
deliveryRecord["id"],
{
"status": DeliveryStatus.SENT,
"sentAt": getUtcTimestamp()
}
)
return MessagingSendResult(
success=True,
deliveryId=deliveryRecord["id"]
)
else:
# Update Delivery Record mit Fehler
self.services.interfaceDbComponent.updateDelivery(
deliveryRecord["id"],
{
"status": DeliveryStatus.FAILED,
"errorMessage": "Failed to send message"
}
)
return MessagingSendResult(
success=False,
deliveryId=deliveryRecord["id"],
errorMessage="Failed to send message"
)
except Exception as e:
logger.error(f"Error sending message: {str(e)}")
# Update Delivery Record mit Fehler
try:
self.services.interfaceDbComponent.updateDelivery(
deliveryRecord["id"],
{
"status": DeliveryStatus.FAILED,
"errorMessage": str(e)
}
)
except Exception as updateError:
logger.error(f"Failed to update delivery record: {str(updateError)}")
return MessagingSendResult(
success=False,
deliveryId=deliveryRecord["id"],
errorMessage=str(e)
)
def _textToHtml(self, text: str) -> str:
"""
Convert plain text to simple HTML for email display.
- Escapes HTML special characters
- Converts newlines to
tags
- Wraps URLs in clickable links
- Wraps in a basic HTML structure with nice styling
Args:
text: Plain text message
Returns:
HTML formatted message
"""
import html
# Check if already HTML (contains HTML tags)
if re.search(r'<[^>]+>', text):
return text
# Escape HTML special characters
escaped = html.escape(text)
# Convert URLs to clickable links (before converting newlines)
urlPattern = r'(https?://[^\s<>"\']+)'
escaped = re.sub(urlPattern, r'\1', escaped)
# Convert newlines to
tags
escaped = escaped.replace('\n', '
\n')
# Wrap in a nice HTML structure
htmlContent = f"""