intermittent commit
This commit is contained in:
@@ -81,6 +81,33 @@ class CampaignBuildResult:
|
||||
built_messages: list[BuiltMessage]
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class _EntryMessageContext:
|
||||
resolution: EntryAttachmentResolution
|
||||
senders: list[RecipientConfig]
|
||||
sender: RecipientConfig | None
|
||||
recipients: dict[str, list[RecipientConfig]]
|
||||
issues: list[MessageIssue]
|
||||
validation_status: MessageValidationStatus
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class _RenderedMessageTemplate:
|
||||
subject: str
|
||||
text_body: str | None
|
||||
html_body: str | None
|
||||
body_mode: str
|
||||
values: dict[str, Any]
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class _MimeBuildResult:
|
||||
message: EmailMessage | None
|
||||
build_status: BuildStatus
|
||||
validation_status: MessageValidationStatus
|
||||
attachment_count: int
|
||||
|
||||
|
||||
def _resolve(campaign_file: str | Path, raw_path: str) -> Path:
|
||||
campaign_path = Path(campaign_file).resolve()
|
||||
path = Path(raw_path).expanduser()
|
||||
@@ -431,6 +458,322 @@ def _write_eml(message: EmailMessage, output_dir: Path, entry: EntryConfig, entr
|
||||
return str(path), path.stat().st_size
|
||||
|
||||
|
||||
def _entry_message_context(
|
||||
*,
|
||||
config: CampaignConfig,
|
||||
campaign_file: str | Path,
|
||||
entry: EntryConfig,
|
||||
entry_index: int,
|
||||
attachment_match_index: AttachmentMatchIndex | None,
|
||||
) -> _EntryMessageContext:
|
||||
resolution = resolve_entry_attachments(
|
||||
config=config,
|
||||
campaign_file=campaign_file,
|
||||
entry=entry,
|
||||
entry_index=entry_index,
|
||||
match_index=attachment_match_index,
|
||||
)
|
||||
effective_addresses = effective_address_lists(config, entry)
|
||||
senders = effective_addresses["from"]
|
||||
sender = senders[0] if senders else None
|
||||
recipients = {key: value for key, value in effective_addresses.items() if key != "from"}
|
||||
issues = _message_issues_from_attachment_resolution(resolution)
|
||||
validation_status = _validation_status_from_attachment_status(resolution.status)
|
||||
validation_status = _apply_ignored_field_override_issues(config, entry, issues, validation_status)
|
||||
return _EntryMessageContext(
|
||||
resolution=resolution,
|
||||
senders=senders,
|
||||
sender=sender,
|
||||
recipients=recipients,
|
||||
issues=issues,
|
||||
validation_status=validation_status,
|
||||
)
|
||||
|
||||
|
||||
def _apply_ignored_field_override_issues(
|
||||
config: CampaignConfig,
|
||||
entry: EntryConfig,
|
||||
issues: list[MessageIssue],
|
||||
validation_status: MessageValidationStatus,
|
||||
) -> MessageValidationStatus:
|
||||
ignored_field_overrides = ignored_entry_field_overrides(config, entry)
|
||||
if not ignored_field_overrides:
|
||||
return validation_status
|
||||
issues.append(
|
||||
MessageIssue(
|
||||
severity="warning",
|
||||
code="field_override_not_allowed",
|
||||
message=(
|
||||
"Recipient field value(s) ignored because the campaign field does not allow overrides: "
|
||||
+ ", ".join(ignored_field_overrides)
|
||||
),
|
||||
behavior="warn",
|
||||
source="fields",
|
||||
)
|
||||
)
|
||||
if validation_status == MessageValidationStatus.READY:
|
||||
return MessageValidationStatus.WARNING
|
||||
return validation_status
|
||||
|
||||
|
||||
def _message_draft(
|
||||
*,
|
||||
config: CampaignConfig,
|
||||
entry: EntryConfig,
|
||||
entry_index: int,
|
||||
context: _EntryMessageContext,
|
||||
build_status: BuildStatus,
|
||||
validation_status: MessageValidationStatus,
|
||||
send_status: SendStatus = SendStatus.DRAFT,
|
||||
imap_status: ImapStatus | None = None,
|
||||
subject: str | None = None,
|
||||
attachment_count: int = 0,
|
||||
issues: list[MessageIssue] | None = None,
|
||||
eml_path: str | None = None,
|
||||
eml_size: int | None = None,
|
||||
) -> MessageDraft:
|
||||
if imap_status is None:
|
||||
imap_status = _imap_initial_status(config) if build_status == BuildStatus.BUILT else ImapStatus.SKIPPED
|
||||
return MessageDraft(
|
||||
entry_index=entry_index,
|
||||
entry_id=entry.id,
|
||||
active=entry.active,
|
||||
build_status=build_status,
|
||||
validation_status=validation_status,
|
||||
send_status=send_status,
|
||||
imap_status=imap_status,
|
||||
subject=subject,
|
||||
from_=_message_address(context.sender),
|
||||
from_all=_message_addresses(context.senders),
|
||||
to=_message_addresses(context.recipients["to"]),
|
||||
cc=_message_addresses(context.recipients["cc"]),
|
||||
bcc=_message_addresses(context.recipients["bcc"]),
|
||||
reply_to=_message_addresses(context.recipients["reply_to"]),
|
||||
bounce_to=_message_addresses(context.recipients["bounce_to"]),
|
||||
disposition_notification_to=_message_addresses(context.recipients["disposition_notification_to"]),
|
||||
attachment_count=attachment_count,
|
||||
attachments=_attachment_summaries(context.resolution),
|
||||
issues=issues if issues is not None else context.issues,
|
||||
eml_path=eml_path,
|
||||
eml_size_bytes=eml_size,
|
||||
)
|
||||
|
||||
|
||||
def _inactive_entry_message(
|
||||
*,
|
||||
config: CampaignConfig,
|
||||
entry: EntryConfig,
|
||||
entry_index: int,
|
||||
context: _EntryMessageContext,
|
||||
) -> BuiltMessage:
|
||||
draft = _message_draft(
|
||||
config=config,
|
||||
entry=entry,
|
||||
entry_index=entry_index,
|
||||
context=context,
|
||||
build_status=BuildStatus.BUILD_FAILED,
|
||||
validation_status=MessageValidationStatus.INACTIVE,
|
||||
imap_status=ImapStatus.SKIPPED,
|
||||
issues=[
|
||||
MessageIssue(
|
||||
severity="info",
|
||||
code="inactive_entry",
|
||||
message="Entry is inactive",
|
||||
behavior=config.validation_policy.inactive_entry.value,
|
||||
source="entry",
|
||||
)
|
||||
],
|
||||
)
|
||||
return BuiltMessage(draft=draft, mime=None)
|
||||
|
||||
|
||||
def _validate_required_recipients(
|
||||
config: CampaignConfig,
|
||||
recipients: dict[str, list[RecipientConfig]],
|
||||
issues: list[MessageIssue],
|
||||
validation_status: MessageValidationStatus,
|
||||
) -> MessageValidationStatus:
|
||||
if recipients["to"]:
|
||||
return validation_status
|
||||
behavior = config.validation_policy.missing_email.value
|
||||
issues.append(
|
||||
_issue_from_behavior(
|
||||
code="missing_email",
|
||||
message="No effective To recipient is configured",
|
||||
behavior=behavior,
|
||||
source="recipients",
|
||||
)
|
||||
)
|
||||
if behavior == MissingAddressBehavior.BLOCK.value:
|
||||
return MessageValidationStatus.BLOCKED
|
||||
return MessageValidationStatus.EXCLUDED
|
||||
|
||||
|
||||
def _render_message_template(
|
||||
config: CampaignConfig,
|
||||
campaign_file: str | Path,
|
||||
entry: EntryConfig,
|
||||
) -> _RenderedMessageTemplate:
|
||||
subject_template, text_template, html_template = _load_template_parts(config, campaign_file)
|
||||
body_mode = _template_body_mode(config)
|
||||
values = build_template_values(config, entry)
|
||||
keep_missing_placeholders = not config.validation_policy.ignore_empty_fields
|
||||
subject = _render_template(subject_template, values, keep_missing=keep_missing_placeholders)
|
||||
text_body = None
|
||||
html_body = None
|
||||
if text_template is not None:
|
||||
text_body = _render_template(text_template or "", values, keep_missing=keep_missing_placeholders)
|
||||
if html_template is not None:
|
||||
html_body = _render_template(html_template or "", values, keep_missing=keep_missing_placeholders)
|
||||
return _RenderedMessageTemplate(
|
||||
subject=subject,
|
||||
text_body=text_body,
|
||||
html_body=html_body,
|
||||
body_mode=body_mode,
|
||||
values=values,
|
||||
)
|
||||
|
||||
|
||||
def _unresolved_template_fields(rendered: _RenderedMessageTemplate) -> list[str]:
|
||||
unresolved_fields = _find_unresolved_placeholders(rendered.subject)
|
||||
if rendered.body_mode in {TemplateBodyMode.TEXT.value, TemplateBodyMode.BOTH.value}:
|
||||
unresolved_fields |= _find_unresolved_placeholders(rendered.text_body)
|
||||
if rendered.body_mode in {TemplateBodyMode.HTML.value, TemplateBodyMode.BOTH.value}:
|
||||
unresolved_fields |= _find_unresolved_placeholders(rendered.html_body)
|
||||
return sorted(unresolved_fields)
|
||||
|
||||
|
||||
def _validate_rendered_template(
|
||||
config: CampaignConfig,
|
||||
rendered: _RenderedMessageTemplate,
|
||||
issues: list[MessageIssue],
|
||||
validation_status: MessageValidationStatus,
|
||||
) -> MessageValidationStatus:
|
||||
unresolved = _unresolved_template_fields(rendered)
|
||||
if not unresolved:
|
||||
return validation_status
|
||||
behavior = config.validation_policy.template_error.value
|
||||
issues.append(
|
||||
_issue_from_behavior(
|
||||
code="template_error",
|
||||
message="Unresolved template placeholder(s): " + ", ".join(unresolved),
|
||||
behavior=behavior,
|
||||
source="template",
|
||||
)
|
||||
)
|
||||
if behavior == MissingAddressBehavior.BLOCK.value:
|
||||
return MessageValidationStatus.BLOCKED
|
||||
return MessageValidationStatus.EXCLUDED
|
||||
|
||||
|
||||
def _populate_message_headers(
|
||||
message: EmailMessage,
|
||||
*,
|
||||
senders: list[RecipientConfig],
|
||||
recipients: dict[str, list[RecipientConfig]],
|
||||
subject: str,
|
||||
) -> None:
|
||||
message["Date"] = formatdate(localtime=True)
|
||||
message["Message-ID"] = make_msgid()
|
||||
if senders:
|
||||
message["From"] = _format_recipient_header(senders)
|
||||
if len(senders) > 1:
|
||||
# RFC 5322 requires a singular Sender when From contains more
|
||||
# than one mailbox. The first effective From address remains
|
||||
# the SMTP envelope sender and compatibility primary value.
|
||||
message["Sender"] = _format_recipient(senders[0])
|
||||
if recipients["to"]:
|
||||
message["To"] = _format_recipient_header(recipients["to"])
|
||||
if recipients["cc"]:
|
||||
message["Cc"] = _format_recipient_header(recipients["cc"])
|
||||
# Bcc deliberately remains envelope-only and is tracked in MessageDraft.
|
||||
if recipients["reply_to"]:
|
||||
message["Reply-To"] = _format_recipient_header(recipients["reply_to"])
|
||||
if recipients["disposition_notification_to"]:
|
||||
message["Disposition-Notification-To"] = _format_recipient_header(
|
||||
recipients["disposition_notification_to"]
|
||||
)
|
||||
# bounce_to is tracked but not emitted as Return-Path. That should be the SMTP envelope sender.
|
||||
message["Subject"] = subject
|
||||
|
||||
|
||||
def _populate_message_body(message: EmailMessage, rendered: _RenderedMessageTemplate) -> None:
|
||||
effective_html_body = rendered.html_body if rendered.html_body and rendered.html_body.strip() else None
|
||||
if rendered.body_mode == TemplateBodyMode.HTML.value:
|
||||
message.set_content(effective_html_body or "", subtype="html")
|
||||
elif rendered.body_mode == TemplateBodyMode.TEXT.value:
|
||||
message.set_content(rendered.text_body or "")
|
||||
elif effective_html_body is not None:
|
||||
message.set_content(rendered.text_body or "")
|
||||
message.add_alternative(effective_html_body, subtype="html")
|
||||
else:
|
||||
message.set_content(rendered.text_body or "")
|
||||
|
||||
|
||||
def _build_mime_message(
|
||||
*,
|
||||
config: CampaignConfig,
|
||||
entry: EntryConfig,
|
||||
entry_index: int,
|
||||
output_dir: Path | None,
|
||||
work_dir: Path | None,
|
||||
context: _EntryMessageContext,
|
||||
rendered: _RenderedMessageTemplate,
|
||||
) -> _MimeBuildResult:
|
||||
message = EmailMessage()
|
||||
try:
|
||||
_populate_message_headers(
|
||||
message,
|
||||
senders=context.senders,
|
||||
recipients=context.recipients,
|
||||
subject=rendered.subject,
|
||||
)
|
||||
_populate_message_body(message, rendered)
|
||||
if work_dir is None:
|
||||
work_dir = output_dir or Path(tempfile.mkdtemp(prefix="govoplan-build-"))
|
||||
attachment_count = _attach_files(
|
||||
message=message,
|
||||
config=config,
|
||||
entry=entry,
|
||||
entry_index=entry_index,
|
||||
resolution=context.resolution,
|
||||
values=rendered.values,
|
||||
work_dir=work_dir,
|
||||
)
|
||||
return _MimeBuildResult(
|
||||
message=message,
|
||||
build_status=BuildStatus.BUILT,
|
||||
validation_status=context.validation_status,
|
||||
attachment_count=attachment_count,
|
||||
)
|
||||
except ZipBuildError as exc:
|
||||
context.issues.append(
|
||||
MessageIssue(
|
||||
severity="error",
|
||||
code=exc.code,
|
||||
message=str(exc),
|
||||
behavior="block",
|
||||
source="attachments",
|
||||
)
|
||||
)
|
||||
except Exception as exc:
|
||||
context.issues.append(
|
||||
MessageIssue(
|
||||
severity="error",
|
||||
code="build_failed",
|
||||
message=str(exc),
|
||||
behavior="block",
|
||||
source="builder",
|
||||
)
|
||||
)
|
||||
return _MimeBuildResult(
|
||||
message=None,
|
||||
build_status=BuildStatus.BUILD_FAILED,
|
||||
validation_status=MessageValidationStatus.BLOCKED,
|
||||
attachment_count=0,
|
||||
)
|
||||
|
||||
|
||||
def build_entry_message(
|
||||
*,
|
||||
config: CampaignConfig,
|
||||
@@ -442,169 +785,57 @@ def build_entry_message(
|
||||
work_dir: Path | None = None,
|
||||
attachment_match_index: AttachmentMatchIndex | None = None,
|
||||
) -> BuiltMessage:
|
||||
resolution = resolve_entry_attachments(config=config, campaign_file=campaign_file, entry=entry, entry_index=entry_index, match_index=attachment_match_index)
|
||||
effective_addresses = effective_address_lists(config, entry)
|
||||
senders = effective_addresses["from"]
|
||||
sender = senders[0] if senders else None
|
||||
recipients = {key: value for key, value in effective_addresses.items() if key != "from"}
|
||||
issues = _message_issues_from_attachment_resolution(resolution)
|
||||
validation_status = _validation_status_from_attachment_status(resolution.status)
|
||||
|
||||
ignored_field_overrides = ignored_entry_field_overrides(config, entry)
|
||||
if ignored_field_overrides:
|
||||
issues.append(
|
||||
MessageIssue(
|
||||
severity="warning",
|
||||
code="field_override_not_allowed",
|
||||
message="Recipient field value(s) ignored because the campaign field does not allow overrides: " + ", ".join(ignored_field_overrides),
|
||||
behavior="warn",
|
||||
source="fields",
|
||||
)
|
||||
)
|
||||
if validation_status == MessageValidationStatus.READY:
|
||||
validation_status = MessageValidationStatus.WARNING
|
||||
|
||||
context = _entry_message_context(
|
||||
config=config,
|
||||
campaign_file=campaign_file,
|
||||
entry=entry,
|
||||
entry_index=entry_index,
|
||||
attachment_match_index=attachment_match_index,
|
||||
)
|
||||
if not entry.active:
|
||||
draft = MessageDraft(
|
||||
entry_index=entry_index,
|
||||
entry_id=entry.id,
|
||||
active=False,
|
||||
build_status=BuildStatus.BUILD_FAILED,
|
||||
validation_status=MessageValidationStatus.INACTIVE,
|
||||
send_status=SendStatus.DRAFT,
|
||||
imap_status=ImapStatus.SKIPPED,
|
||||
from_=_message_address(sender),
|
||||
from_all=_message_addresses(senders),
|
||||
to=_message_addresses(recipients["to"]),
|
||||
cc=_message_addresses(recipients["cc"]),
|
||||
bcc=_message_addresses(recipients["bcc"]),
|
||||
reply_to=_message_addresses(recipients["reply_to"]),
|
||||
bounce_to=_message_addresses(recipients["bounce_to"]),
|
||||
disposition_notification_to=_message_addresses(recipients["disposition_notification_to"]),
|
||||
attachments=_attachment_summaries(resolution),
|
||||
issues=[MessageIssue(severity="info", code="inactive_entry", message="Entry is inactive", behavior=config.validation_policy.inactive_entry.value, source="entry")],
|
||||
)
|
||||
return BuiltMessage(draft=draft, mime=None)
|
||||
return _inactive_entry_message(config=config, entry=entry, entry_index=entry_index, context=context)
|
||||
|
||||
if not recipients["to"]:
|
||||
behavior = config.validation_policy.missing_email.value
|
||||
issues.append(_issue_from_behavior(code="missing_email", message="No effective To recipient is configured", behavior=behavior, source="recipients"))
|
||||
validation_status = MessageValidationStatus.BLOCKED if behavior == MissingAddressBehavior.BLOCK.value else MessageValidationStatus.EXCLUDED
|
||||
|
||||
subject_template, text_template, html_template = _load_template_parts(config, campaign_file)
|
||||
body_mode = _template_body_mode(config)
|
||||
values = build_template_values(config, entry)
|
||||
keep_missing_placeholders = not config.validation_policy.ignore_empty_fields
|
||||
subject = _render_template(subject_template, values, keep_missing=keep_missing_placeholders)
|
||||
text_body = _render_template(text_template or "", values, keep_missing=keep_missing_placeholders) if text_template is not None else None
|
||||
html_body = _render_template(html_template or "", values, keep_missing=keep_missing_placeholders) if html_template is not None else None
|
||||
|
||||
unresolved_fields = _find_unresolved_placeholders(subject)
|
||||
if body_mode in {TemplateBodyMode.TEXT.value, TemplateBodyMode.BOTH.value}:
|
||||
unresolved_fields |= _find_unresolved_placeholders(text_body)
|
||||
if body_mode in {TemplateBodyMode.HTML.value, TemplateBodyMode.BOTH.value}:
|
||||
unresolved_fields |= _find_unresolved_placeholders(html_body)
|
||||
unresolved = sorted(unresolved_fields)
|
||||
if unresolved:
|
||||
behavior = config.validation_policy.template_error.value
|
||||
issues.append(
|
||||
_issue_from_behavior(
|
||||
code="template_error",
|
||||
message="Unresolved template placeholder(s): " + ", ".join(unresolved),
|
||||
behavior=behavior,
|
||||
source="template",
|
||||
)
|
||||
)
|
||||
validation_status = MessageValidationStatus.BLOCKED if behavior == MissingAddressBehavior.BLOCK.value else MessageValidationStatus.EXCLUDED
|
||||
|
||||
message = EmailMessage()
|
||||
try:
|
||||
message["Date"] = formatdate(localtime=True)
|
||||
message["Message-ID"] = make_msgid()
|
||||
if senders:
|
||||
message["From"] = _format_recipient_header(senders)
|
||||
if len(senders) > 1:
|
||||
# RFC 5322 requires a singular Sender when From contains more
|
||||
# than one mailbox. The first effective From address remains
|
||||
# the SMTP envelope sender and compatibility primary value.
|
||||
message["Sender"] = _format_recipient(senders[0])
|
||||
if recipients["to"]:
|
||||
message["To"] = _format_recipient_header(recipients["to"])
|
||||
if recipients["cc"]:
|
||||
message["Cc"] = _format_recipient_header(recipients["cc"])
|
||||
# Bcc deliberately remains envelope-only and is tracked in MessageDraft.
|
||||
if recipients["reply_to"]:
|
||||
message["Reply-To"] = _format_recipient_header(recipients["reply_to"])
|
||||
if recipients["disposition_notification_to"]:
|
||||
message["Disposition-Notification-To"] = _format_recipient_header(recipients["disposition_notification_to"])
|
||||
# bounce_to is tracked but not emitted as Return-Path. That should be the SMTP envelope sender.
|
||||
message["Subject"] = subject
|
||||
|
||||
effective_html_body = html_body if html_body and html_body.strip() else None
|
||||
if body_mode == TemplateBodyMode.HTML.value:
|
||||
message.set_content(effective_html_body or "", subtype="html")
|
||||
elif body_mode == TemplateBodyMode.TEXT.value:
|
||||
message.set_content(text_body or "")
|
||||
elif effective_html_body is not None:
|
||||
message.set_content(text_body or "")
|
||||
message.add_alternative(effective_html_body, subtype="html")
|
||||
else:
|
||||
message.set_content(text_body or "")
|
||||
|
||||
if work_dir is None:
|
||||
work_dir = output_dir or Path(tempfile.mkdtemp(prefix="multimailer-build-"))
|
||||
attachment_count = _attach_files(
|
||||
message=message,
|
||||
config=config,
|
||||
entry=entry,
|
||||
entry_index=entry_index,
|
||||
resolution=resolution,
|
||||
values=values,
|
||||
work_dir=work_dir,
|
||||
)
|
||||
build_status = BuildStatus.BUILT
|
||||
except ZipBuildError as exc:
|
||||
issues.append(MessageIssue(severity="error", code=exc.code, message=str(exc), behavior="block", source="attachments"))
|
||||
validation_status = MessageValidationStatus.BLOCKED
|
||||
build_status = BuildStatus.BUILD_FAILED
|
||||
attachment_count = 0
|
||||
message = None # type: ignore[assignment]
|
||||
except Exception as exc:
|
||||
issues.append(MessageIssue(severity="error", code="build_failed", message=str(exc), behavior="block", source="builder"))
|
||||
validation_status = MessageValidationStatus.BLOCKED
|
||||
build_status = BuildStatus.BUILD_FAILED
|
||||
attachment_count = 0
|
||||
message = None # type: ignore[assignment]
|
||||
context.validation_status = _validate_required_recipients(
|
||||
config,
|
||||
context.recipients,
|
||||
context.issues,
|
||||
context.validation_status,
|
||||
)
|
||||
rendered = _render_message_template(config, campaign_file, entry)
|
||||
context.validation_status = _validate_rendered_template(
|
||||
config,
|
||||
rendered,
|
||||
context.issues,
|
||||
context.validation_status,
|
||||
)
|
||||
mime_result = _build_mime_message(
|
||||
config=config,
|
||||
entry=entry,
|
||||
entry_index=entry_index,
|
||||
output_dir=output_dir,
|
||||
work_dir=work_dir,
|
||||
context=context,
|
||||
rendered=rendered,
|
||||
)
|
||||
|
||||
eml_path: str | None = None
|
||||
eml_size: int | None = None
|
||||
if write_eml and output_dir is not None and message is not None:
|
||||
eml_path, eml_size = _write_eml(message, output_dir, entry, entry_index)
|
||||
if write_eml and output_dir is not None and mime_result.message is not None:
|
||||
eml_path, eml_size = _write_eml(mime_result.message, output_dir, entry, entry_index)
|
||||
|
||||
draft = MessageDraft(
|
||||
draft = _message_draft(
|
||||
config=config,
|
||||
entry=entry,
|
||||
entry_index=entry_index,
|
||||
entry_id=entry.id,
|
||||
active=entry.active,
|
||||
build_status=build_status,
|
||||
validation_status=validation_status,
|
||||
send_status=SendStatus.DRAFT,
|
||||
imap_status=_imap_initial_status(config) if build_status == BuildStatus.BUILT else ImapStatus.SKIPPED,
|
||||
subject=subject,
|
||||
from_=_message_address(sender),
|
||||
from_all=_message_addresses(senders),
|
||||
to=_message_addresses(recipients["to"]),
|
||||
cc=_message_addresses(recipients["cc"]),
|
||||
bcc=_message_addresses(recipients["bcc"]),
|
||||
reply_to=_message_addresses(recipients["reply_to"]),
|
||||
bounce_to=_message_addresses(recipients["bounce_to"]),
|
||||
disposition_notification_to=_message_addresses(recipients["disposition_notification_to"]),
|
||||
attachment_count=attachment_count,
|
||||
attachments=_attachment_summaries(resolution),
|
||||
issues=issues,
|
||||
context=context,
|
||||
build_status=mime_result.build_status,
|
||||
validation_status=mime_result.validation_status,
|
||||
subject=rendered.subject,
|
||||
attachment_count=mime_result.attachment_count,
|
||||
eml_path=eml_path,
|
||||
eml_size_bytes=eml_size,
|
||||
eml_size=eml_size,
|
||||
)
|
||||
return BuiltMessage(draft=draft, mime=message)
|
||||
return BuiltMessage(draft=draft, mime=mime_result.message)
|
||||
|
||||
|
||||
|
||||
@@ -680,7 +911,7 @@ def build_campaign_messages(
|
||||
|
||||
started = time.perf_counter()
|
||||
attachment_match_index = AttachmentMatchIndex()
|
||||
with tempfile.TemporaryDirectory(prefix="multimailer-build-") as tmp:
|
||||
with tempfile.TemporaryDirectory(prefix="govoplan-build-") as tmp:
|
||||
work_dir = output_path or Path(tmp)
|
||||
built_messages = [
|
||||
build_entry_message(
|
||||
|
||||
Reference in New Issue
Block a user