component email gateway: Add support for XEP-0131 headers:
Some email headers (`Keywords` and `Importance` for now) are converted between XMPP and email. rel 451
This commit is contained in:
parent
5ccec8f755
commit
08cd3fd587
1 changed files with 47 additions and 7 deletions
|
|
@ -51,6 +51,7 @@ from libervia.backend.plugins.plugin_xep_0033 import (
|
||||||
)
|
)
|
||||||
from libervia.backend.plugins.plugin_xep_0077 import XEP_0077
|
from libervia.backend.plugins.plugin_xep_0077 import XEP_0077
|
||||||
from libervia.backend.plugins.plugin_xep_0106 import XEP_0106
|
from libervia.backend.plugins.plugin_xep_0106 import XEP_0106
|
||||||
|
from libervia.backend.plugins.plugin_xep_0131 import XEP_0131, HeadersData, Urgency
|
||||||
from libervia.backend.tools.utils import aio
|
from libervia.backend.tools.utils import aio
|
||||||
|
|
||||||
from .models import Credentials, UserData
|
from .models import Credentials, UserData
|
||||||
|
|
@ -87,6 +88,7 @@ email_pattern = re.compile(r"[^@]+@[^@]+\.[^@]+")
|
||||||
|
|
||||||
class SendMailExtra(BaseModel):
|
class SendMailExtra(BaseModel):
|
||||||
addresses: AddressesData | None = None
|
addresses: AddressesData | None = None
|
||||||
|
headers: HeadersData | None = None
|
||||||
|
|
||||||
|
|
||||||
class EmailGatewayComponent:
|
class EmailGatewayComponent:
|
||||||
|
|
@ -103,6 +105,7 @@ class EmailGatewayComponent:
|
||||||
self._on_registration_form, self._on_registration_submit
|
self._on_registration_form, self._on_registration_submit
|
||||||
)
|
)
|
||||||
self._e = cast(XEP_0106, host.plugins["XEP-0106"])
|
self._e = cast(XEP_0106, host.plugins["XEP-0106"])
|
||||||
|
self._shim = cast(XEP_0131, host.plugins["XEP-0131"])
|
||||||
# TODO: For the moment, all credentials are kept in cache; we should only keep the
|
# TODO: For the moment, all credentials are kept in cache; we should only keep the
|
||||||
# X latest.
|
# X latest.
|
||||||
self.users_data: dict[jid.JID, UserData] = {}
|
self.users_data: dict[jid.JID, UserData] = {}
|
||||||
|
|
@ -192,7 +195,7 @@ class EmailGatewayComponent:
|
||||||
if client != self.client:
|
if client != self.client:
|
||||||
return mess_data
|
return mess_data
|
||||||
from_jid = mess_data["from"].userhostJID()
|
from_jid = mess_data["from"].userhostJID()
|
||||||
extra = None
|
extra_kw = {}
|
||||||
if mess_data["type"] not in ("chat", "normal"):
|
if mess_data["type"] not in ("chat", "normal"):
|
||||||
log.warning(f"ignoring message with unexpected type: {mess_data}")
|
log.warning(f"ignoring message with unexpected type: {mess_data}")
|
||||||
return mess_data
|
return mess_data
|
||||||
|
|
@ -206,7 +209,7 @@ class EmailGatewayComponent:
|
||||||
return mess_data
|
return mess_data
|
||||||
else:
|
else:
|
||||||
to_email = None
|
to_email = None
|
||||||
extra = SendMailExtra(addresses=addresses)
|
extra_kw["addresses"] = addresses
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
to_email = self._e.unescape(mess_data["to"].user)
|
to_email = self._e.unescape(mess_data["to"].user)
|
||||||
|
|
@ -215,6 +218,11 @@ class EmailGatewayComponent:
|
||||||
f'Invalid "to" JID, can\'t send message: {message_elt.toXml()}.'
|
f'Invalid "to" JID, can\'t send message: {message_elt.toXml()}.'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self._shim.move_keywords_to_headers(mess_data["extra"])
|
||||||
|
headers = mess_data["extra"].get("headers")
|
||||||
|
if headers:
|
||||||
|
extra_kw["headers"] = headers
|
||||||
|
|
||||||
try:
|
try:
|
||||||
body_lang, body = next(iter(mess_data["message"].items()))
|
body_lang, body = next(iter(mess_data["message"].items()))
|
||||||
except (KeyError, StopIteration):
|
except (KeyError, StopIteration):
|
||||||
|
|
@ -235,7 +243,7 @@ class EmailGatewayComponent:
|
||||||
to_email=to_email,
|
to_email=to_email,
|
||||||
body=body,
|
body=body,
|
||||||
subject=subject,
|
subject=subject,
|
||||||
extra=extra,
|
extra=SendMailExtra(**extra_kw) if extra_kw else None,
|
||||||
)
|
)
|
||||||
except exceptions.UnknownEntityError:
|
except exceptions.UnknownEntityError:
|
||||||
log.warning(f"Can't send message, user {from_jid} is not registered.")
|
log.warning(f"Can't send message, user {from_jid} is not registered.")
|
||||||
|
|
@ -361,6 +369,17 @@ class EmailGatewayComponent:
|
||||||
|
|
||||||
sender_domain = credentials["user_email"].split("@", 1)[-1]
|
sender_domain = credentials["user_email"].split("@", 1)[-1]
|
||||||
|
|
||||||
|
if extra.headers:
|
||||||
|
if extra.headers.keywords:
|
||||||
|
msg["Keywords"] = extra.headers.keywords
|
||||||
|
if extra.headers.urgency:
|
||||||
|
urgency = extra.headers.urgency
|
||||||
|
if urgency == Urgency.medium:
|
||||||
|
importance = "normal"
|
||||||
|
else:
|
||||||
|
importance = urgency
|
||||||
|
msg["Importance"] = importance
|
||||||
|
|
||||||
await smtp.sendmail(
|
await smtp.sendmail(
|
||||||
credentials["smtp_host"].encode(),
|
credentials["smtp_host"].encode(),
|
||||||
credentials["user_email"].encode(),
|
credentials["user_email"].encode(),
|
||||||
|
|
@ -692,6 +711,7 @@ class EmailGatewayComponent:
|
||||||
):
|
):
|
||||||
addresses_data.noreply = True
|
addresses_data.noreply = True
|
||||||
break
|
break
|
||||||
|
extra = {}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
not addresses_data.replyto
|
not addresses_data.replyto
|
||||||
|
|
@ -702,7 +722,7 @@ class EmailGatewayComponent:
|
||||||
):
|
):
|
||||||
# The main recipient is the only one, and there is no other metadata: there is
|
# The main recipient is the only one, and there is no other metadata: there is
|
||||||
# no need to add addresses metadata.
|
# no need to add addresses metadata.
|
||||||
extra = None
|
pass
|
||||||
else:
|
else:
|
||||||
for address in addresses_data.addresses:
|
for address in addresses_data.addresses:
|
||||||
if address.jid and (
|
if address.jid and (
|
||||||
|
|
@ -712,9 +732,29 @@ class EmailGatewayComponent:
|
||||||
# other JID addresses will have to be delivered by us.
|
# other JID addresses will have to be delivered by us.
|
||||||
address.delivered = True
|
address.delivered = True
|
||||||
|
|
||||||
extra = {
|
extra["addresses"] = addresses_data.model_dump(mode="json", exclude_none=True)
|
||||||
"addresses": addresses_data.model_dump(mode="json", exclude_none=True)
|
|
||||||
}
|
# We look for interesting headers
|
||||||
|
headers = {}
|
||||||
|
keywords_headers = email.get_all("keywords")
|
||||||
|
if keywords_headers:
|
||||||
|
keywords = ",".join(keywords_headers)
|
||||||
|
headers["keywords"] = keywords
|
||||||
|
|
||||||
|
importance = email["importance"]
|
||||||
|
if importance:
|
||||||
|
# We convert to urgency
|
||||||
|
if importance in ("low", "high"):
|
||||||
|
headers["urgency"] = importance
|
||||||
|
elif importance == "normal":
|
||||||
|
headers["urgency"] = "medium"
|
||||||
|
else:
|
||||||
|
log.warning("Ignoring invalid importance header: {importance!r}")
|
||||||
|
|
||||||
|
if headers:
|
||||||
|
extra["headers"] = HeadersData(**headers).model_dump(
|
||||||
|
mode="json", exclude_none=True
|
||||||
|
)
|
||||||
|
|
||||||
client = self.client.get_virtual_client(from_jid)
|
client = self.client.get_virtual_client(from_jid)
|
||||||
await client.sendMessage(
|
await client.sendMessage(
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue