mirror of
https://github.com/zulip/zulip.git
synced 2025-10-23 04:52:12 +00:00
send_email: Set the Date header according to local enqueue time.
Email clients tend to sort emails by the "Date" header, which is not when the email was received -- emails can be arbitrarily delayed during relaying. Messages without a Date header (as all Zulip messages previously) have one inserted by the first mailserver they encounter. As there are now multiple email-sending queues, we would like the view of the database, as presented by the emails that are sent out, to be consistent based on the Date header, which may not be the same as when the client gets the email in their inbox. Insert a Date header of when the Zulip system inserted the data into the local queue, as that encodes when the full information was pulled from the database. This also opens the door to multiple workers servicing the email_senders queue, to limit backlogging during large notifications, without having to worry about inconsistent delivery order between those two workers. Messages which are sent synchronously via `send_email()` get a Date header of when we attempt to send the message; this is, in practice, no different from Django's default behaviour of doing so, but makes the behaviour slightly more consistent.
This commit is contained in:
committed by
Tim Abbott
parent
0e152a128f
commit
721fd26442
@@ -2,6 +2,7 @@
|
||||
<h4>Envelope-From: {{ envelope_from }}</h4>
|
||||
{% endif %}
|
||||
<h4>From: {{ from_email }}</h4>
|
||||
<h4>Date: {{ date }}</h4>
|
||||
{% if reply_to %}
|
||||
<h4>Reply to:
|
||||
{% for email in reply_to %}
|
||||
|
@@ -30,6 +30,7 @@ from zerver.lib.exceptions import InvitationError
|
||||
from zerver.lib.invites import notify_invites_changed
|
||||
from zerver.lib.queue import queue_event_on_commit
|
||||
from zerver.lib.send_email import (
|
||||
EMAIL_DATE_FORMAT,
|
||||
FromAddress,
|
||||
clear_scheduled_invitation_emails,
|
||||
maybe_remove_from_suppression_list,
|
||||
@@ -468,6 +469,7 @@ def do_send_user_invite_email(
|
||||
"corporate_enabled": settings.CORPORATE_ENABLED,
|
||||
},
|
||||
"realm_id": realm.id,
|
||||
"date": event_time.strftime(EMAIL_DATE_FORMAT),
|
||||
}
|
||||
queue_event_on_commit("email_senders", event)
|
||||
|
||||
|
@@ -27,7 +27,7 @@ from zerver.lib.markdown.fenced_code import FENCE_RE
|
||||
from zerver.lib.message import bulk_access_messages
|
||||
from zerver.lib.notification_data import get_mentioned_user_group
|
||||
from zerver.lib.queue import queue_event_on_commit
|
||||
from zerver.lib.send_email import FromAddress, send_future_email
|
||||
from zerver.lib.send_email import EMAIL_DATE_FORMAT, FromAddress, send_future_email
|
||||
from zerver.lib.soft_deactivation import soft_reactivate_if_personal_notification
|
||||
from zerver.lib.tex import change_katex_to_raw_latex
|
||||
from zerver.lib.timezone import canonicalize_timezone
|
||||
@@ -586,6 +586,8 @@ def do_send_missedmessage_events_reply_in_zulip(
|
||||
)
|
||||
from_address = FromAddress.NOREPLY
|
||||
|
||||
user_tz = user_profile.timezone or settings.TIME_ZONE
|
||||
local_time = timezone_now().astimezone(zoneinfo.ZoneInfo(canonicalize_timezone(user_tz)))
|
||||
email_dict = {
|
||||
"template_prefix": "zerver/emails/missed_message",
|
||||
"to_user_ids": [user_profile.id],
|
||||
@@ -593,6 +595,7 @@ def do_send_missedmessage_events_reply_in_zulip(
|
||||
"from_address": from_address,
|
||||
"reply_to_email": str(Address(display_name=reply_to_name, addr_spec=reply_to_address)),
|
||||
"context": context,
|
||||
"date": local_time.strftime(EMAIL_DATE_FORMAT),
|
||||
}
|
||||
queue_event_on_commit("email_senders", email_dict)
|
||||
|
||||
|
@@ -43,6 +43,7 @@ from zproject.email_backends import EmailLogBackEnd, get_forward_address
|
||||
if settings.ZILENCER_ENABLED:
|
||||
from zilencer.models import RemoteZulipServer
|
||||
|
||||
EMAIL_DATE_FORMAT = "%a, %d %b %Y %H:%M:%S %z"
|
||||
MAX_CONNECTION_TRIES = 3
|
||||
|
||||
## Logging setup ##
|
||||
@@ -94,6 +95,7 @@ def build_email(
|
||||
from_address: str | None = None,
|
||||
reply_to_email: str | None = None,
|
||||
language: str | None = None,
|
||||
date: str | None = None,
|
||||
context: Mapping[str, Any] = {},
|
||||
realm: Realm | None = None,
|
||||
) -> EmailMultiAlternatives:
|
||||
@@ -124,6 +126,14 @@ def build_email(
|
||||
# commonly-recognized.
|
||||
extra_headers = {"X-Auto-Response-Suppress": "All"}
|
||||
|
||||
if date is None:
|
||||
# Messages enqueued via the `email_senders` queue provide a
|
||||
# Date header of when they were enqueued; Django would also
|
||||
# add a default-now header if we left this off, but doing so
|
||||
# ourselves here explicitly makes it slightly more consistent.
|
||||
date = timezone_now().strftime(EMAIL_DATE_FORMAT)
|
||||
extra_headers["Date"] = date
|
||||
|
||||
if realm is not None:
|
||||
# formaddr is meant for formatting (display_name, email_address) pair for headers like "To",
|
||||
# but we can use its utility for formatting the List-Id header, as it follows the same format,
|
||||
@@ -255,6 +265,7 @@ def send_email(
|
||||
from_address: str | None = None,
|
||||
reply_to_email: str | None = None,
|
||||
language: str | None = None,
|
||||
date: str | None = None,
|
||||
context: Mapping[str, Any] = {},
|
||||
realm: Realm | None = None,
|
||||
connection: BaseEmailBackend | None = None,
|
||||
@@ -269,6 +280,7 @@ def send_email(
|
||||
from_address=from_address,
|
||||
reply_to_email=reply_to_email,
|
||||
language=language,
|
||||
date=date,
|
||||
context=context,
|
||||
realm=realm,
|
||||
)
|
||||
|
@@ -10,7 +10,7 @@ from django.utils.translation import gettext as _
|
||||
|
||||
from confirmation.models import one_click_unsubscribe_link
|
||||
from zerver.lib.queue import queue_json_publish_rollback_unsafe
|
||||
from zerver.lib.send_email import FromAddress
|
||||
from zerver.lib.send_email import EMAIL_DATE_FORMAT, FromAddress
|
||||
from zerver.lib.timezone import canonicalize_timezone
|
||||
from zerver.models import UserProfile
|
||||
|
||||
@@ -109,6 +109,7 @@ def email_on_new_login(sender: Any, user: UserProfile, request: Any, **kwargs: A
|
||||
"from_name": FromAddress.security_email_from_name(user_profile=user),
|
||||
"from_address": FromAddress.NOREPLY,
|
||||
"context": context,
|
||||
"date": local_time.strftime(EMAIL_DATE_FORMAT),
|
||||
}
|
||||
queue_json_publish_rollback_unsafe("email_senders", email_dict)
|
||||
|
||||
|
@@ -65,7 +65,7 @@ from zerver.lib.remote_server import (
|
||||
)
|
||||
from zerver.lib.request import RequestNotes
|
||||
from zerver.lib.response import json_success
|
||||
from zerver.lib.send_email import FromAddress
|
||||
from zerver.lib.send_email import EMAIL_DATE_FORMAT, FromAddress
|
||||
from zerver.lib.timestamp import timestamp_to_datetime
|
||||
from zerver.lib.typed_endpoint import (
|
||||
ApnsAppId,
|
||||
@@ -1192,6 +1192,7 @@ def update_remote_realm_data_for_server(
|
||||
"template_prefix": "zerver/emails/internal_billing_notice",
|
||||
"to_emails": [BILLING_SUPPORT_EMAIL],
|
||||
"from_address": FromAddress.tokenized_no_reply_address(),
|
||||
"date": timezone_now().strftime(EMAIL_DATE_FORMAT),
|
||||
}
|
||||
for context in new_locally_deleted_remote_realms_on_paid_plan_contexts:
|
||||
email_dict["context"] = context
|
||||
|
@@ -49,6 +49,7 @@ class EmailLogBackEnd(EmailBackend):
|
||||
"reply_to": email.reply_to,
|
||||
"recipients": email.to,
|
||||
"body": email.body,
|
||||
"date": email.extra_headers.get("Date", "?"),
|
||||
"html_message": html_message,
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user