diff --git a/app/lib/admin/actions/email_send.dart b/app/lib/admin/actions/email_send.dart index acd3b20a99..97739432df 100644 --- a/app/lib/admin/actions/email_send.dart +++ b/app/lib/admin/actions/email_send.dart @@ -38,6 +38,8 @@ The list of resolved emails will be deduplicated. 'from': 'The email address to impersonate (`support@pub.dev` by default).', 'subject': 'The subject of the email message.', 'body': 'The text content of the email body.', + 'reply-to': + '(optional) A comma separated list of email addresses to be used in the Reply-To header.', 'in-reply-to': '(optional) The local message id of the email that this is a reply to ' '(e.g. moderation case id). The email sender will the `In-Reply-To` and `References` ' @@ -65,6 +67,7 @@ The list of resolved emails will be deduplicated. ); final cc = options['cc']; final inReplyTo = options['in-reply-to']; + final replyTo = options['reply-to']; final emailList = await _resolveEmails(to!); final ccEmailList = cc == null ? null : await _resolveEmails(cc); @@ -79,6 +82,7 @@ The list of resolved emails will be deduplicated. ccRecipients: ccEmailList?.map((v) => EmailAddress(v)).toList() ?? const [], inReplyToLocalMessageId: inReplyTo, + replyTos: replyTo?.split(',') ?? const [], )); return { 'emails': emailList, diff --git a/app/lib/service/email/email_sender.dart b/app/lib/service/email/email_sender.dart index 59d8c7ad6e..5c397e5345 100644 --- a/app/lib/service/email/email_sender.dart +++ b/app/lib/service/email/email_sender.dart @@ -176,6 +176,7 @@ Message _toMessage(EmailMessage input) { 'Message-ID': '<${input.localMessageId}@pub.dev>', if (inReplyToMessageId != null) 'In-Reply-To': inReplyToMessageId, if (inReplyToMessageId != null) 'References': inReplyToMessageId, + if (input.replyTos.isNotEmpty) 'Reply-To': input.replyTos.join(','), }; return Message() ..headers = headers diff --git a/app/lib/service/email/email_templates.dart b/app/lib/service/email/email_templates.dart index 7037964ac9..49cb04184e 100644 --- a/app/lib/service/email/email_templates.dart +++ b/app/lib/service/email/email_templates.dart @@ -120,6 +120,7 @@ class EmailMessage { final List ccRecipients; final String subject; final String bodyText; + final List replyTos; EmailMessage( this.from, @@ -129,6 +130,7 @@ class EmailMessage { this.inReplyToLocalMessageId, this.localMessageId, this.ccRecipients = const [], + this.replyTos = const [], }) : bodyText = reflowBodyText(bodyText); /// Throws [ArgumentError] if the [localMessageId] or the @@ -157,6 +159,7 @@ class EmailMessage { 'ccRecipients': ccRecipients.map((e) => e.email).toList(), 'subject': subject, 'bodyText': bodyText, + if (replyTos.isNotEmpty) 'replyTos': replyTos, }; } diff --git a/app/test/admin/email_send_test.dart b/app/test/admin/email_send_test.dart index d1ad77b262..eece314b4b 100644 --- a/app/test/admin/email_send_test.dart +++ b/app/test/admin/email_send_test.dart @@ -43,6 +43,7 @@ void main() { 'to': 'package:oxygen', 'subject': 'Test email', 'body': 'Test body', + 'reply-to': 'other1@pub.dev,other2@pub.dev', }), ); expect(rs1.output, { @@ -57,7 +58,8 @@ void main() { 'recipients': ['admin@pub.dev'], 'ccRecipients': [], 'subject': 'Test email', - 'bodyText': 'Test body' + 'bodyText': 'Test body', + 'replyTos': ['other1@pub.dev', 'other2@pub.dev'], }); }); });