chore: Add a condition to handle bounced email (#11873)

Add bounced emails to the conversation thread.
Fix Gmail bounce detection by checking the X-Failed-Recipients header.

Currently, bounced emails are rejected as auto-replies, which causes
support agents to miss important delivery failure context. This PR
ensures bounced messages are correctly added to the thread, preserving
visibility for the support team.
This commit is contained in:
Pranav
2025-08-01 02:13:46 -07:00
committed by GitHub
parent c98c255ed0
commit 5ab913f7b5
4 changed files with 139 additions and 6 deletions

View File

@@ -4,16 +4,17 @@ module IncomingEmailValidityHelper
def incoming_email_from_valid_email?
return false unless valid_external_email_for_active_account?
# Return if email doesn't have a valid sender
# This can happen in cases like bounce emails for invalid contact email address
return false unless Devise.email_regexp.match?(@processed_mail.original_sender)
# Process bounced emails, as regular emails
return true if @processed_mail.bounced?
# we skip processing auto reply emails like delivery status notifications
# out of office replies, etc.
return false if auto_reply_email?
# return if email doesn't have a valid sender
# This can happen in cases like bounce emails for invalid contact email address
# TODO: Handle the bounce separately and mark the contact as invalid in case of reply bounces
# The returned value could be "\"\"" for some email clients
return false unless Devise.email_regexp.match?(@processed_mail.original_sender)
true
end

View File

@@ -157,6 +157,10 @@ class MailPresenter < SimpleDelegator
auto_submitted? || x_auto_reply?
end
def bounced?
@mail.bounced? || @mail['X-Failed-Recipients'].try(:value).present?
end
def notification_email_from_chatwoot?
# notification emails are send via mailer sender email address. so it should match
original_sender == Mail::Address.new(ENV.fetch('MAILER_SENDER_EMAIL', 'Chatwoot <accounts@chatwoot.com>')).address

120
spec/fixtures/files/bounced_gmail.eml vendored Normal file
View File

@@ -0,0 +1,120 @@
Delivered-To: robert.smith@gmail.com
Return-Path: <>
Subject: Delivery Status Notification (Failure)
From: Mail Delivery Subsystem <mailer-daemon@googlemail.com>
To: robert.smith@gmail.com
Content-Type: multipart/report; boundary="00000000000093475906390e1e9b"; report-type=delivery-status
Auto-Submitted: auto-replied
Message-ID: <686707c9.050a0220.302e7d.0cb2.GMR@mx.google.com>
Date: Thu, 03 Jul 2025 15:44:25 -0700 (PDT)
X-Failed-Recipients: alex.jones@fictionalcorp.com
--00000000000093475906390e1e9b
Content-Type: multipart/related; boundary="000000000000936d8406390e1ec7"
--000000000000936d8406390e1ec7
Content-Type: multipart/alternative; boundary="000000000000936d9006390e1ec8"
--000000000000936d9006390e1ec8
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
** Address not found **
Your message wasn't delivered to alex.jones@fictionalcorp.com because the address co=
uldn't be found or is unable to receive email.
Learn more here: https://support.google.com/mail/?p=3DNoSuchUser
The response was:
550 5.1.1 The email account that you tried to reach does not exist. Please =
try double-checking the recipient's email address for typos or unnecessary =
spaces. For more information, go to https://support.google.com/mail/?p=3DNo=
SuchUser d2e1a72fcca58-74ce2b0525csor332154b3a.0 - gsmtp
--000000000000936d9006390e1ec8
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
<html>
<head>
<style>
* {
font-family:Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif;
}
</style>
</head>
<body>
<table cellpadding=3D"0" cellspacing=3D"0" class=3D"email-wrapper" style=3D=
"padding-top:32px;background-color:#ffffff;"><tbody>
<tr><td>
<table cellpadding=3D0 cellspacing=3D0><tbody>
<tr><td style=3D"max-width:560px;padding:24px 24px 32px;background-color:#f=
afafa;border:1px solid #e0e0e0;border-radius:2px">
<img style=3D"padding:0 24px 16px 0;float:left" width=3D72 height=3D72 alt=
=3D"Error Icon" src=3D"cid:icon.png">
<table style=3D"min-width:272px;padding-top:8px"><tbody>
<tr><td><h2 style=3D"font-size:20px;color:#212121;font-weight:bold;margin:0=
">
Address not found
</h2></td></tr>
<tr><td style=3D"padding-top:20px;color:#757575;font-size:16px;font-weight:=
normal;text-align:left">
Your message wasn't delivered to <a style=3D'color:#212121;text-decoration:=
none'><b>alex.jones@fictionalcorp.com</b></a> because the address couldn't be found =
or is unable to receive email.
</td></tr>
<tr><td style=3D"padding-top:24px;color:#4285F4;font-size:14px;font-weight:=
bold;text-align:left">
<a style=3D"text-decoration:none" href=3D"https://support.google.com/mail/?=
p=3DNoSuchUser">LEARN MORE</a>
</td></tr>
</tbody></table>
</td></tr>
</tbody></table>
</td></tr>
<tr style=3D"border:none;background-color:#fff;font-size:12.8px;width:90%">
<td align=3D"left" style=3D"padding:48px 10px">
The response was:<br/>
<p style=3D"font-family:monospace">
550 5.1.1 The email account that you tried to reach does not exist. Please =
try double-checking the recipient's email address for typos or unnecessary =
spaces. For more information, go to https://support.google.com/mail/?p=3DNo=
SuchUser d2e1a72fcca58-74ce2b0525csor332154b3a.0 - gsmtp
</p>
</td>
</tr>
</tbody></table>
</body>
</html>
--000000000000936d9006390e1ec8--
--000000000000936d8406390e1ec7
Content-Type: image/png; name="icon.png"
Content-Disposition: attachment; filename="icon.png"
Content-Transfer-Encoding: base64
Content-ID: <icon.png>
--000000000000936d8406390e1ec7--
--00000000000093475906390e1e9b
Content-Type: message/delivery-status
--00000000000093475906390e1e9b
Content-Type: message/rfc822
Date: Thu, 03 Jul 2025 15:44:23 -0700
From: Robert Smith <robert.smith@gmail.com>
Reply-To: robert.smith@gmail.com
To: alex.jones@fictionalcorp.com
Message-ID: <conversation/93775311-9416-4428-964b-862001f8a8b2/messages/27855337@gmail.com>
In-Reply-To: <account/1/conversation/93775311-9416-4428-964b-862001f8a8b2@gmail.com>
Subject: Just checking in
Mime-Version: 1.0
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: 7bit
<p>Hey, just checking in. Let me know if you got my earlier message.</p>
--00000000000093475906390e1e9b--

View File

@@ -115,6 +115,14 @@ RSpec.describe Imap::ImapMailbox do
end
end
context 'when the email is bounced' do
let!(:bounced_mail) { create_inbound_email_from_fixture('bounced_gmail.eml') }
it 'processes the bounced email' do
expect { class_instance.process(bounced_mail.mail, channel) }.to change(Message, :count)
end
end
context 'when a reply for existing email conversation' do
let(:prev_conversation) { create(:conversation, account: account, inbox: channel.inbox, assignee: agent) }
let(:reply_mail) do