summaryrefslogtreecommitdiff
path: root/addons/mass_mailing/models/mail_thread.py
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/mass_mailing/models/mail_thread.py
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/mass_mailing/models/mail_thread.py')
-rw-r--r--addons/mass_mailing/models/mail_thread.py87
1 files changed, 87 insertions, 0 deletions
diff --git a/addons/mass_mailing/models/mail_thread.py b/addons/mass_mailing/models/mail_thread.py
new file mode 100644
index 00000000..1f442675
--- /dev/null
+++ b/addons/mass_mailing/models/mail_thread.py
@@ -0,0 +1,87 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+import datetime
+
+from odoo import api, models, fields, tools
+
+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_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 routes:
+ # even if 'reply_to' in ref (cfr mail/mail_thread) that indicates a new thread redirection
+ # (aka bypass alias configuration in gateway) consider it as a reply for statistics purpose
+ thread_references = message_dict['references'] or message_dict['in_reply_to']
+ msg_references = tools.mail_header_msgid_re.findall(thread_references)
+ if msg_references:
+ self.env['mailing.trace'].set_opened(mail_message_ids=msg_references)
+ self.env['mailing.trace'].set_replied(mail_message_ids=msg_references)
+ 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)
+
+ @api.model
+ def _routing_handle_bounce(self, email_message, message_dict):
+ """ 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)._routing_handle_bounce(email_message, message_dict)
+
+ 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 and (not bounced_partner or any(p.message_bounce >= BLACKLIST_MAX_BOUNCED_LIMIT for p in bounced_partner)):
+ if max(stats) > min(stats) + datetime.timedelta(weeks=1):
+ blacklist_rec = self.env['mail.blacklist'].sudo()._add(bounced_email)
+ blacklist_rec._message_log(
+ body='This email has been automatically blacklisted because of too much bounced.')
+
+ @api.model
+ def message_new(self, msg_dict, custom_values=None):
+ """ Overrides mail_thread message_new that is called by the mailgateway
+ through message_process.
+ This override updates the document according to the email.
+ """
+ defaults = {}
+
+ if issubclass(type(self), self.pool['utm.mixin']):
+ thread_references = msg_dict.get('references', '') or msg_dict.get('in_reply_to', '')
+ msg_references = tools.mail_header_msgid_re.findall(thread_references)
+ if msg_references:
+ traces = self.env['mailing.trace'].search([('message_id', 'in', msg_references)], limit=1)
+ if traces:
+ defaults['campaign_id'] = traces.campaign_id.id
+ defaults['source_id'] = traces.mass_mailing_id.source_id.id
+ defaults['medium_id'] = traces.mass_mailing_id.medium_id.id
+
+ if custom_values:
+ defaults.update(custom_values)
+
+ return super(MailThread, self).message_new(msg_dict, custom_values=defaults)