Avoid duplicates

This handler implements several strategies to reduce the reception of duplicate messages. It does this by removing certain recipients from the list of recipients calculated earlier.

>>> from mailman.app.lifecycle import create_list
>>> mlist = create_list('_xtest@example.com')

Create some members we’re going to use.

>>> from mailman.interfaces.usermanager import IUserManager
>>> from zope.component import getUtility
>>> user_manager = getUtility(IUserManager)

>>> address_a = user_manager.create_address('aperson@example.com')
>>> address_b = user_manager.create_address('bperson@example.com')

>>> from mailman.interfaces.member import MemberRole
>>> member_a = mlist.subscribe(address_a, MemberRole.member)
>>> member_b = mlist.subscribe(address_b, MemberRole.member)
>>> # This is the message metadata dictionary as it would be produced by
>>> # the CalcRecips handler.
>>> recips = dict(
...     recipients=['aperson@example.com', 'bperson@example.com'])

Short circuiting

The module short-circuits if there are no recipients.

>>> from mailman.testing.helpers import (specialized_message_from_string
...   as message_from_string)
>>> msg = message_from_string("""\
... From: aperson@example.com
... Subject: A message of great import
...
... Something
... """)
>>> msgdata = {}
>>> from mailman.config import config
>>> handler = config.handlers['avoid-duplicates']
>>> handler.process(mlist, msg, msgdata)
>>> msgdata
{}
>>> print(msg.as_string())
From: aperson@example.com
Subject: A message of great import

Something

Suppressing the list copy

Members can elect not to receive a list copy of any message on which they are explicitly named as a recipient. This is done by setting their receive_list_copy preference to False. However, if they aren’t mentioned in one of the recipient headers (i.e. To, CC, Resent-To, or Resent-CC), then they will get a list copy.

>>> member_a.preferences.receive_list_copy = False
>>> msg = message_from_string("""\
... From: Claire Person <cperson@example.com>
...
... Something of great import.
... """)
>>> msgdata = recips.copy()
>>> handler.process(mlist, msg, msgdata)
>>> sorted(msgdata['recipients'])
['aperson@example.com', 'bperson@example.com']
>>> print(msg.as_string())
From: Claire Person <cperson@example.com>

Something of great import.

If they’re mentioned on the CC line, they won’t get a list copy and will be removed from the CC.

>>> msg = message_from_string("""\
... From: Claire Person <cperson@example.com>
... CC: aperson@example.com
...
... Something of great import.
... """)
>>> msgdata = recips.copy()
>>> handler.process(mlist, msg, msgdata)
>>> sorted(msgdata['recipients'])
['bperson@example.com']
>>> print(msg.as_string())
From: Claire Person <cperson@example.com>

Something of great import.

But if they’re mentioned on the CC line and have receive_list_copy set to True (the default), then they still get a list copy.

>>> msg = message_from_string("""\
... From: Claire Person <cperson@example.com>
... CC: bperson@example.com
...
... Something of great import.
... """)
>>> msgdata = recips.copy()
>>> handler.process(mlist, msg, msgdata)
>>> sorted(msgdata['recipients'])
['aperson@example.com', 'bperson@example.com']
>>> print(msg.as_string())
From: Claire Person <cperson@example.com>
CC: bperson@example.com

Something of great import.

Other headers checked for recipients include the To

>>> msg = message_from_string("""\
... From: Claire Person <cperson@example.com>
... To: aperson@example.com
...
... Something of great import.
... """)
>>> msgdata = recips.copy()
>>> handler.process(mlist, msg, msgdata)
>>> sorted(msgdata['recipients'])
['bperson@example.com']
>>> print(msg.as_string())
From: Claire Person <cperson@example.com>
To: aperson@example.com

Something of great import.

Resent-To

>>> msg = message_from_string("""\
... From: Claire Person <cperson@example.com>
... Resent-To: aperson@example.com
...
... Something of great import.
... """)
>>> msgdata = recips.copy()
>>> handler.process(mlist, msg, msgdata)
>>> sorted(msgdata['recipients'])
['bperson@example.com']
>>> print(msg.as_string())
From: Claire Person <cperson@example.com>
Resent-To: aperson@example.com

Something of great import.

…and Resent-CC headers.

>>> msg = message_from_string("""\
... From: Claire Person <cperson@example.com>
... Resent-Cc: aperson@example.com
...
... Something of great import.
... """)
>>> msgdata = recips.copy()
>>> handler.process(mlist, msg, msgdata)
>>> sorted(msgdata['recipients'])
['bperson@example.com']
>>> print(msg.as_string())
From: Claire Person <cperson@example.com>
Resent-Cc: aperson@example.com

Something of great import.