diff --git a/app/config.py b/app/config.py index b3d132f6..ecffc2e3 100644 --- a/app/config.py +++ b/app/config.py @@ -333,6 +333,7 @@ AlERT_WRONG_MX_RECORD_CUSTOM_DOMAIN = "custom_domain_mx_record_issue" ALERT_DIRECTORY_DISABLED_ALIAS_CREATION = "alert_directory_disabled_alias_creation" ALERT_HOTMAIL_COMPLAINT = "alert_hotmail_complaint" +ALERT_YAHOO_COMPLAINT = "alert_yahoo_complaint" # <<<<< END ALERT EMAIL >>>> diff --git a/app/email/status.py b/app/email/status.py index 5e2b7b59..b2fbfef9 100644 --- a/app/email/status.py +++ b/app/email/status.py @@ -15,6 +15,8 @@ E208 = "250 SL E208 Hotmail complaint handled" E209 = "250 SL E209 Email Loop" +E210 = "250 SL E210 Yahoo complaint handled" + # 4** errors # E401 = "421 SL E401 Retry later" E402 = "421 SL E402 Encryption failed - Retry later" diff --git a/app/email_utils.py b/app/email_utils.py index c6a5659a..97aa3555 100644 --- a/app/email_utils.py +++ b/app/email_utils.py @@ -592,7 +592,6 @@ def get_orig_message_from_bounce(msg: Message) -> Message: def get_orig_message_from_outlook_complaint(msg: Message) -> Message: - """parse the original email from Bounce""" i = 0 for part in msg.walk(): i += 1 @@ -604,6 +603,18 @@ def get_orig_message_from_outlook_complaint(msg: Message) -> Message: return part +def get_orig_message_from_yahoo_complaint(msg: Message) -> Message: + i = 0 + for part in msg.walk(): + i += 1 + + # 1st part is the container + # 2nd part is the empty body + # 6th is original message + if i == 6: + return part + + def get_header_from_bounce(msg: Message, header: str) -> str: """using regex to get header value from bounce message get_orig_message_from_bounce is better. This should be the last option diff --git a/email_handler.py b/email_handler.py index b6aa5dde..d0f6a74c 100644 --- a/email_handler.py +++ b/email_handler.py @@ -81,6 +81,7 @@ from app.config import ( NEWRELIC_CONFIG_PATH, POSTMASTER, ALERT_HOTMAIL_COMPLAINT, + ALERT_YAHOO_COMPLAINT, ) from app.email import status from app.email.rate_limit import rate_limited @@ -118,6 +119,7 @@ from app.email_utils import ( should_ignore_bounce, get_orig_message_from_outlook_complaint, parse_full_address, + get_orig_message_from_yahoo_complaint, ) from app.extensions import db from app.log import LOG, set_message_id @@ -1314,7 +1316,7 @@ def handle_hotmail_complaint(msg: Message) -> bool: alias = Alias.get_by(email=alias_address) if not alias: - LOG.d("No alias for %s", alias_address) + LOG.w("No alias for %s", alias_address) return False user = alias.user @@ -1339,6 +1341,46 @@ def handle_hotmail_complaint(msg: Message) -> bool: return True +def handle_yahoo_complaint(msg: Message) -> bool: + """ + Handle yahoo complaint sent to postmaster + Return True if the complaint can be handled, False otherwise + """ + orig_msg = get_orig_message_from_yahoo_complaint(msg) + to_header = orig_msg["To"] + if not to_header: + LOG.e("cannot find the alias") + return False + + _, alias_address = parse_full_address(get_header_unicode(to_header)) + alias = Alias.get_by(email=alias_address) + + if not alias: + LOG.w("No alias for %s", alias_address) + return False + + user = alias.user + LOG.w("Handle yahoo complaint for %s %s %s", alias, user, alias.mailboxes) + + send_email_with_rate_control( + user, + ALERT_YAHOO_COMPLAINT, + user.email, + f"Yahoo abuse report", + render( + "transactional/yahoo-complaint.txt.jinja2", + alias=alias, + ), + render( + "transactional/yahoo-complaint.html", + alias=alias, + ), + max_nb_alert=2, + ) + + return True + + def handle_bounce_reply_phase(envelope, msg: Message, email_log: EmailLog): """ Handle reply phase bounce @@ -1758,6 +1800,17 @@ def handle(envelope: Envelope) -> str: if handle_hotmail_complaint(msg): return status.E208 + if ( + len(rcpt_tos) == 1 + and mail_from == "feedback@arf.mail.yahoo.com" + and rcpt_tos[0] == POSTMASTER + ): + LOG.w("Handle yahoo complaint") + + # if the complaint cannot be handled, forward it normally + if handle_yahoo_complaint(msg): + return status.E210 + # Handle bounce if ( len(rcpt_tos) == 1 diff --git a/templates/emails/transactional/yahoo-complaint.html b/templates/emails/transactional/yahoo-complaint.html new file mode 100644 index 00000000..62c69d6e --- /dev/null +++ b/templates/emails/transactional/yahoo-complaint.html @@ -0,0 +1,33 @@ +{% extends "base.html" %} + +{% block content %} + {% call text() %} + This is SimpleLogin team.
+ Yahoo has informed us about an email sent to {{ alias.email }} that might have been marked as spam. + {% endcall %} + + {% call text() %} + Putting a forwarded email into Spam affects SimpleLogin email delivery, has a negative effect for all users and + is a violation of our terms and condition. + {% endcall %} + + {% call text() %} + If that’s the case, please disable the alias instead if you don't want to receive the emails sent to this alias. + {% endcall %} + + {% call text() %} + If SimpleLogin isn’t useful for you, please know that you can simply delete your account on the Settings page. + {% endcall %} + + {% call text() %} + Looking to hear back from you. + {% endcall %} + + {% call text() %} + Best,
+ SimpleLogin Team. + {% endcall %} + +{% endblock %} + + diff --git a/templates/emails/transactional/yahoo-complaint.txt.jinja2 b/templates/emails/transactional/yahoo-complaint.txt.jinja2 new file mode 100644 index 00000000..ad1d6c59 --- /dev/null +++ b/templates/emails/transactional/yahoo-complaint.txt.jinja2 @@ -0,0 +1,17 @@ +Hi, + +This is SimpleLogin team. + +Yahoo has informed us about an email sent to {{ alias.email }} that might have been marked as spam. + +Putting a forwarded email into Spam affects SimpleLogin email delivery, has a negative effect for all users and + is a violation of our terms and condition. + +If that’s the case, please disable the alias instead if you don't want to receive the emails sent to this alias. + +If SimpleLogin isn’t useful for you, please know that you can simply delete your account on the Settings page. + +Looking to hear back from you. + +Best, +SimpleLogin Team. \ No newline at end of file