[IMP] mail, mass_mailing : reset bounce on mail reception
PURPOSE
Add some improvements in mail gateway: remove private discussion, improve
bounce management, allow resetting bounce counters, improve automatic set or
reset of blacklists and ease mass mailing inheritance.
SPECIFICATIONS
The number of message bounce is incremented each time the email bounce
on a specific email address. To have a correct information, we should
reset to zero this counter if we receive a mail from this address.
Indeed, if we receive an email from an email address, this email is
active and the message bounce number (if > 0) is not relevant anymore.
Only models inheriting form blacklist are impacted by this improvement
in order to limit a bit side effect.
Also, add a check in autoblacklist rule. No need to check the stats
if message_bounce is < 5.
This commit introduces
* ``_routing_reset_bounce`` routing method managing the bounce reset;
* ``_message_reset_bounce`` model method that resets bounce counter
in blacklist enabled models;
LINKS
Related to task ID : 1893155
Linked to PR #33340
This commit is contained in:
committed by
Thibault Delavallée
parent
f4524f03c3
commit
92fe83ecf8
@@ -156,3 +156,9 @@ class MailBlackListMixin(models.AbstractModel):
|
||||
super(MailBlackListMixin, self)._message_receive_bounce(email, partner)
|
||||
for record in self:
|
||||
record.message_bounce = record.message_bounce + 1
|
||||
|
||||
def _message_reset_bounce(self, email):
|
||||
""" Override of mail.thread generic method. Purpose is to reset the
|
||||
bounce counter of the record. """
|
||||
super(MailBlackListMixin, self)._message_reset_bounce(email)
|
||||
self.write({'message_bounce': 0})
|
||||
|
||||
@@ -815,6 +815,22 @@ class MailThread(models.AbstractModel):
|
||||
|
||||
return (model, thread_id, route[2], route[3], route[4])
|
||||
|
||||
@api.model
|
||||
def _routing_reset_bounce(self, email_message, message_dict):
|
||||
"""Called by ``message_process`` when a new mail is received from an email address.
|
||||
If the email is related to a partner, we consider that the number of message_bounce
|
||||
is not relevant anymore as the email is valid - as we received an email from this
|
||||
address. The model is here hardcoded because we cannot know with which model the
|
||||
incomming mail match. We consider that if a mail arrives, we have to clear bounce for
|
||||
each model having bounce count.
|
||||
|
||||
:param email_from: email address that sent the incoming email."""
|
||||
valid_email = message_dict['email_from']
|
||||
if valid_email:
|
||||
bl_models = self.env['ir.model'].sudo().search(['&', ('is_mail_blacklist', '=', True), ('model', '!=', 'mail.thread.blacklist')])
|
||||
for model in [bl_model for bl_model in bl_models if bl_model.model in self.env]: # transient test mode
|
||||
self.env[model.model].sudo().search([('message_bounce', '>', 0), ('email_normalized', '=', valid_email)])._message_reset_bounce(valid_email)
|
||||
|
||||
@api.model
|
||||
def message_route(self, message, message_dict, model=None, thread_id=None, custom_values=None):
|
||||
""" Attempt to figure out the correct target model, thread_id,
|
||||
@@ -883,6 +899,7 @@ class MailThread(models.AbstractModel):
|
||||
# See http://datatracker.ietf.org/doc/rfc3462/?include_text=1
|
||||
# As all MTA does not respect this RFC (googlemail is one of them),
|
||||
# we also need to verify if the message come from "mailer-daemon"
|
||||
# If not a bounce: reset bounce information
|
||||
if bounce_alias and bounce_alias in email_to_localpart:
|
||||
bounce_re = re.compile("%s\+(\d+)-?([\w.]+)?-?(\d+)?" % re.escape(bounce_alias), re.UNICODE)
|
||||
bounce_match = bounce_re.search(email_to)
|
||||
@@ -892,6 +909,7 @@ class MailThread(models.AbstractModel):
|
||||
if message.get_content_type() == 'multipart/report' or email_from_localpart == 'mailer-daemon':
|
||||
self._routing_handle_bounce(message, message_dict)
|
||||
return []
|
||||
self._routing_reset_bounce(message, message_dict)
|
||||
|
||||
# 1. Handle reply
|
||||
# if destination = alias with different model -> consider it is a forward and not a reply
|
||||
@@ -1149,6 +1167,16 @@ class MailThread(models.AbstractModel):
|
||||
"""
|
||||
pass
|
||||
|
||||
def _message_reset_bounce(self, email):
|
||||
"""Called by ``message_process`` when an email is considered as not being
|
||||
a bounce. The default behavior is to do nothing. This method is meant to
|
||||
be overridden in various modules to add some specific behavior like
|
||||
blacklist management.
|
||||
|
||||
:param string email: email for which to reset bounce information
|
||||
"""
|
||||
pass
|
||||
|
||||
def _message_parse_extract_payload_postprocess(self, message, payload_dict):
|
||||
""" Perform some cleaning / postprocess in the body and attachments
|
||||
extracted from the email. Note that this processing is specific to the
|
||||
|
||||
@@ -50,13 +50,14 @@ class MailThread(models.AbstractModel):
|
||||
|
||||
bounced_email = message_dict['bounced_email']
|
||||
bounced_msg_id = message_dict['bounced_msg_id']
|
||||
bounced_partner = message_dict['bounced_partner']
|
||||
|
||||
if bounced_msg_id:
|
||||
self.env['mailing.trace'].set_bounced(mail_message_ids=[bounced_msg_id])
|
||||
if bounced_email:
|
||||
three_months_ago = fields.Datetime.to_string(datetime.datetime.now() - datetime.timedelta(weeks=13))
|
||||
stats = self.env['mailing.trace'].search(['&', ('bounced', '>', three_months_ago), ('email', '=ilike', bounced_email)]).mapped('bounced')
|
||||
if len(stats) >= BLACKLIST_MAX_BOUNCED_LIMIT:
|
||||
if len(stats) >= BLACKLIST_MAX_BOUNCED_LIMIT and (not bounced_partner or bounced_partner.message_bounce >= BLACKLIST_MAX_BOUNCED_LIMIT):
|
||||
if max(stats) > min(stats) + datetime.timedelta(weeks=1):
|
||||
blacklist_rec = self.env['mail.blacklist'].sudo()._add(bounced_email)
|
||||
blacklist_rec._message_log(
|
||||
|
||||
Reference in New Issue
Block a user