PURPOSE
Mass mailing is currently a bit messy. As SMS will be added as a way to notify
people in mass through SMS let us take this opportunity to somehow clean this
application: organization, light code cleaning, model renaming.
SPECIFICATIONS
Rename mail.mail.statistics to mailing.trace and mail.statistics.report
to mail.trace.report. Rationale :
* mail.mail.statistics is linked to mail.mail model. Soon this model will
hold data related to SMS sending. It makes sense to be broader in the
naming;
* mailing.trace is more inlined with marketing.trace model that is the
marketing automation model using it in marketing automation (enterprise
application);
* mailing.trace is shorter to write;
* mail.statistics.report model should sense to be updated at the same
time;
MIGRATION
mail.mail.statistics model -> mailing.trace
mail_mail_statistics table -> mailing_trace
mail.statistics.report model -> mail.trace.report
fields updated (w column change)
* link.tracker.click: mail_stat_id -> mailing_trace_id
fields updated (no column change)
* mail.mail: statistics_ids -> mailing_trace_ids
* mail.mass_mailing: statistics_ids -> mailing_trace_ids
LINKS
Task ID 2037906
Preparing task ID 1997464 (SMS addition in mass mailing)
PR #34938
75 lines
3.9 KiB
Python
75 lines
3.9 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
import logging
|
|
import re
|
|
import datetime
|
|
|
|
from odoo import api, models, tools, fields
|
|
from odoo.tools import decode_smtp_header, decode_message_header
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
BLACKLIST_MAX_BOUNCED_LIMIT = 5
|
|
|
|
|
|
class MailThread(models.AbstractModel):
|
|
""" Update MailThread to add the support of bounce management in mass mailing traces. """
|
|
_inherit = 'mail.thread'
|
|
|
|
@api.model
|
|
def message_route(self, message, message_dict, model=None, thread_id=None, custom_values=None):
|
|
""" Override to udpate mass mailing traces based on bounce emails """
|
|
bounce_alias = self.env['ir.config_parameter'].sudo().get_param("mail.bounce.alias")
|
|
email_to = decode_message_header(message, 'To')
|
|
email_to_localpart = (tools.email_split(email_to) or [''])[0].split('@', 1)[0].lower()
|
|
|
|
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)
|
|
if bounce_match:
|
|
bounced_mail_id = bounce_match.group(1)
|
|
self.env['mailing.trace'].set_bounced(mail_mail_ids=[bounced_mail_id])
|
|
|
|
return super(MailThread, self).message_route(message, message_dict, model, thread_id, custom_values)
|
|
|
|
@api.model
|
|
def message_route_process(self, message, message_dict, routes):
|
|
""" Override to update the parent mailing traces. The parent is found
|
|
by using the References header of the incoming message and looking for
|
|
matching message_id in mailing.trace. """
|
|
if message.get('References') and routes:
|
|
message_ids = [x.strip() for x in decode_smtp_header(message['References']).split()]
|
|
self.env['mailing.trace'].set_opened(mail_message_ids=message_ids)
|
|
self.env['mailing.trace'].set_replied(mail_message_ids=message_ids)
|
|
return super(MailThread, self).message_route_process(message, message_dict, routes)
|
|
|
|
def message_post_with_template(self, template_id, **kwargs):
|
|
# avoid having message send through `message_post*` methods being implicitly considered as
|
|
# mass-mailing
|
|
no_massmail = self.with_context(
|
|
default_mass_mailing_name=False,
|
|
default_mass_mailing_id=False,
|
|
)
|
|
return super(MailThread, no_massmail).message_post_with_template(template_id, **kwargs)
|
|
|
|
def _message_receive_bounce(self, email, partner, mail_id=None):
|
|
"""In addition, an auto blacklist rule check if the email can be blacklisted
|
|
to avoid sending mails indefinitely to this email address.
|
|
This rule checks if the email bounced too much. If this is the case,
|
|
the email address is added to the blacklist in order to avoid continuing
|
|
to send mass_mail to that email address. If it bounced too much times
|
|
in the last month and the bounced are at least separated by one week,
|
|
to avoid blacklist someone because of a temporary mail server error,
|
|
then the email is considered as invalid and is blacklisted."""
|
|
super(MailThread, self)._message_receive_bounce(email, partner, mail_id=None)
|
|
|
|
three_months_ago = fields.Datetime.to_string(datetime.datetime.now() - datetime.timedelta(weeks=13))
|
|
traces = self.env['mailing.trace'] \
|
|
.search(['&', ('bounced', '>', three_months_ago), ('email', '=ilike', email)]) \
|
|
.mapped('bounced')
|
|
if len(traces) >= BLACKLIST_MAX_BOUNCED_LIMIT:
|
|
if max(traces) > min(traces) + datetime.timedelta(weeks=1):
|
|
blacklist_rec = self.env['mail.blacklist'].sudo()._add(email)
|
|
blacklist_rec._message_log(
|
|
body='This email has been automatically blacklisted because of too much bounced.')
|