Added cpl-mail
This commit is contained in:
5
src/cpl-mail/cpl/mail/__init__.py
Normal file
5
src/cpl-mail/cpl/mail/__init__.py
Normal 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
|
||||
0
src/cpl-mail/cpl/mail/abc/__init__.py
Normal file
0
src/cpl-mail/cpl/mail/abc/__init__.py
Normal file
24
src/cpl-mail/cpl/mail/abc/email_client_abc.py
Normal file
24
src/cpl-mail/cpl/mail/abc/email_client_abc.py
Normal 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
|
||||
"""
|
||||
88
src/cpl-mail/cpl/mail/email_client_service.py
Normal file
88
src/cpl-mail/cpl/mail/email_client_service.py
Normal 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")
|
||||
51
src/cpl-mail/cpl/mail/email_client_settings.py
Normal file
51
src/cpl-mail/cpl/mail/email_client_settings.py
Normal 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
|
||||
8
src/cpl-mail/cpl/mail/email_client_settings_name_enum.py
Normal file
8
src/cpl-mail/cpl/mail/email_client_settings_name_enum.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class EMailClientSettingsNameEnum(Enum):
|
||||
host = "Host"
|
||||
port = "Port"
|
||||
user_name = "UserName"
|
||||
credentials = "Credentials"
|
||||
139
src/cpl-mail/cpl/mail/email_model.py
Normal file
139
src/cpl-mail/cpl/mail/email_model.py
Normal 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")
|
||||
29
src/cpl-mail/pyproject.toml
Normal file
29
src/cpl-mail/pyproject.toml
Normal 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"] }
|
||||
|
||||
1
src/cpl-mail/requirements.dev.txt
Normal file
1
src/cpl-mail/requirements.dev.txt
Normal file
@@ -0,0 +1 @@
|
||||
black==25.1.0
|
||||
1
src/cpl-mail/requirements.txt
Normal file
1
src/cpl-mail/requirements.txt
Normal file
@@ -0,0 +1 @@
|
||||
cpl-core
|
||||
Reference in New Issue
Block a user