Added cpl-mail
Some checks failed
Build on push / prepare (push) Successful in 9s
Build on push / core (push) Successful in 18s
Build on push / query (push) Successful in 25s
Build on push / translation (push) Failing after 8s
Build on push / mail (push) Successful in 14s

This commit is contained in:
2025-09-15 20:56:07 +02:00
parent 3b120370b8
commit 25b4ca0696
344 changed files with 4567 additions and 4946 deletions

View File

@@ -0,0 +1,5 @@
from .email_model import EMail
from .email_client_service import EMailClient
from .abc.email_client_abc import EMailClientABC
from .email_client_settings import EMailClientSettings
from .email_client_settings_name_enum import EMailClientSettingsNameEnum

View File

View File

@@ -0,0 +1,24 @@
from abc import abstractmethod, ABC
from cpl.mail.email_model import EMail
class EMailClientABC(ABC):
"""ABC of :class:`cpl.mail.email_client_service.EMailClient`"""
@abstractmethod
def __init__(self):
ABC.__init__(self)
@abstractmethod
def connect(self):
r"""Connects to server"""
@abstractmethod
def send_mail(self, email: EMail):
r"""Sends email
Parameter:
email: :class:`cpl.mail.email.EMail`
Object of the E-Mail to send
"""

View File

@@ -0,0 +1,88 @@
import ssl
from smtplib import SMTP
from typing import Optional
from cpl.core.environment.application_environment_abc import ApplicationEnvironmentABC
from cpl.core.log.logger_abc import LoggerABC
from cpl.core.utils.credential_manager import CredentialManager
from cpl.mail.abc.email_client_abc import EMailClientABC
from cpl.mail.email_model import EMail
from cpl.mail.email_client_settings import EMailClientSettings
class EMailClient(EMailClientABC):
r"""Service to send emails
Parameter:
environment: :class:`cpl.core.environment.application_environment_abc.ApplicationEnvironmentABC`
Environment of the application
logger: :class:`cpl.core.log.logger_abc.LoggerABC`
The logger to use
mail_settings: :class:`cpl.mail.email_client_settings.EMailClientSettings`
Settings for mailing
"""
def __init__(self, environment: ApplicationEnvironmentABC, logger: LoggerABC, mail_settings: EMailClientSettings):
EMailClientABC.__init__(self)
self._environment = environment
self._mail_settings = mail_settings
self._logger = logger
self._server: Optional[SMTP] = None
self.create()
def create(self):
r"""Creates connection"""
self._logger.trace(__name__, f"Started {__name__}.create")
self.connect()
self._logger.trace(__name__, f"Stopped {__name__}.create")
def connect(self):
self._logger.trace(__name__, f"Started {__name__}.connect")
try:
self._logger.debug(__name__, f"Try to connect to {self._mail_settings.host}:{self._mail_settings.port}")
self._server = SMTP(self._mail_settings.host, self._mail_settings.port)
self._logger.info(__name__, f"Connected to {self._mail_settings.host}:{self._mail_settings.port}")
self._logger.debug(__name__, "Try to start tls")
self._server.starttls(context=ssl.create_default_context())
self._logger.info(__name__, "Started tls")
except Exception as e:
self._logger.error(__name__, "Cannot connect to mail server", e)
self._logger.trace(__name__, f"Stopped {__name__}.connect")
def login(self):
r"""Login to server"""
self._logger.trace(__name__, f"Started {__name__}.login")
try:
self._logger.debug(
__name__,
f"Try to login {self._mail_settings.user_name}@{self._mail_settings.host}:{self._mail_settings.port}",
)
self._server.login(
self._mail_settings.user_name, CredentialManager.decrypt(self._mail_settings.credentials)
)
self._logger.info(
__name__,
f"Logged on as {self._mail_settings.user_name} to {self._mail_settings.host}:{self._mail_settings.port}",
)
except Exception as e:
self._logger.error(__name__, "Cannot login to mail server", e)
self._logger.trace(__name__, f"Stopped {__name__}.login")
def send_mail(self, email: EMail):
self._logger.trace(__name__, f"Started {__name__}.send_mail")
try:
self.login()
self._logger.debug(__name__, f"Try to send email to {email.receiver_list}")
self._server.sendmail(
self._mail_settings.user_name, email.receiver_list, email.get_content(self._mail_settings.user_name)
)
self._logger.info(__name__, f"Sent email to {email.receiver_list}")
except Exception as e:
self._logger.error(__name__, f"Cannot send mail to {email.receiver_list}", e)
self._logger.trace(__name__, f"Stopped {__name__}.send_mail")

View File

@@ -0,0 +1,51 @@
from cpl.core.configuration.configuration_model_abc import ConfigurationModelABC
class EMailClientSettings(ConfigurationModelABC):
r"""Representation of mailing settings"""
def __init__(
self,
host: str = None,
port: int = None,
user_name: str = None,
credentials: str = None,
):
ConfigurationModelABC.__init__(self)
self._host: str = host
self._port: int = port
self._user_name: str = user_name
self._credentials: str = credentials
@property
def host(self) -> str:
return self._host
@host.setter
def host(self, host: str) -> None:
self._host = host
@property
def port(self) -> int:
return self._port
@port.setter
def port(self, port: int) -> None:
self._port = port
@property
def user_name(self) -> str:
return self._user_name
@user_name.setter
def user_name(self, user_name: str) -> None:
self._user_name = user_name
@property
def credentials(self) -> str:
return self._credentials
@credentials.setter
def credentials(self, credentials: str) -> None:
self._credentials = credentials

View File

@@ -0,0 +1,8 @@
from enum import Enum
class EMailClientSettingsNameEnum(Enum):
host = "Host"
port = "Port"
user_name = "UserName"
credentials = "Credentials"

View File

@@ -0,0 +1,139 @@
import re
class EMail:
r"""Represents an email
Parameter:
header: list[:class:`str`]
Header of the E-Mail
subject: :class:`str`
Subject of the E-Mail
body: :class:`str`
Body of the E-Mail
transceiver: :class:`str`
Transceiver of the E-Mail
receiver: list[:class:`str`]
Receiver of the E-Mail
"""
def __init__(
self,
header: list[str] = None,
subject: str = None,
body: str = None,
transceiver: str = None,
receiver: list[str] = None,
):
self._header: list[str] = header
self._subject: str = subject
self._body: str = body
self._transceiver: str = transceiver
self._receiver: list[str] = receiver
@property
def header(self) -> str:
return "\r\n".join(self._header)
@property
def header_list(self) -> list[str]:
return self._header
@header.setter
def header(self, header: list[str]):
self._header = header
@property
def subject(self) -> str:
return self._subject
@subject.setter
def subject(self, subject: str):
self._subject = subject
@property
def body(self) -> str:
return self._body
@body.setter
def body(self, body: str):
self._body = body
@property
def transceiver(self) -> str:
return self._transceiver
@transceiver.setter
def transceiver(self, transceiver: str):
if self.check_mail(transceiver):
self._transceiver = transceiver
else:
raise Exception(f"Invalid email: {transceiver}")
@property
def receiver(self) -> str:
return ",".join(self._receiver)
@property
def receiver_list(self) -> list[str]:
return self._receiver
@receiver.setter
def receiver(self, receiver: list[str]):
self._receiver = receiver
@staticmethod
def check_mail(address: str) -> bool:
r"""Checks if an email is valid
Parameter:
address: :class:`str`
The address to check
Returns:
Result if E-Mail is valid or not
"""
return bool(re.search("^\\w+([.-]?\\w+)*@\\w+([.-]?\\w+)*(.\\w{2,3})+$", address))
def add_header(self, header: str):
r"""Adds header
Parameter:
header: :class:`str`
The header of the E-Mail
"""
if self._header is None:
self._header = []
self._header.append(header)
def add_receiver(self, receiver: str):
r"""Adds receiver
Parameter:
receiver: :class:`str`
The receiver of the E-Mail
"""
if self._receiver is None:
self._receiver = []
if self.check_mail(receiver):
self._receiver.append(receiver)
else:
raise Exception(f"Invalid email: {receiver}")
def get_content(self, transceiver: str):
r"""Returns the mail as string
Parameter:
transceiver: :class:`str`
The transceiver of the E-Mail
Returns:
E-Mail as string
"""
return str(
f"From: {transceiver}\r\nTo: {self.receiver}\r\n{self.header}\r\nSubject: {self.subject}\r\n{self.body}"
).encode("utf-8")

View File

@@ -0,0 +1,29 @@
[build-system]
requires = ["setuptools>=70.1.0", "wheel>=0.43.0"]
build-backend = "setuptools.build_meta"
[project]
name = "cpl-mail"
version = "2024.7.0"
description = "CPL mail"
readme = "CPL mail package"
requires-python = ">=3.12"
license = { text = "MIT" }
authors = [
{ name = "Sven Heidemann", email = "sven.heidemann@sh-edraft.de" }
]
keywords = ["cpl", "mail", "backend", "shared", "library"]
dynamic = ["dependencies", "optional-dependencies"]
[project.urls]
Homepage = "https://www.sh-edraft.de"
[tool.setuptools.packages.find]
where = ["."]
include = ["cpl*"]
[tool.setuptools.dynamic]
dependencies = { file = ["requirements.txt"] }
optional-dependencies.dev = { file = ["requirements.dev.txt"] }

View File

@@ -0,0 +1 @@
black==25.1.0

View File

@@ -0,0 +1 @@
cpl-core