Managing members

The mailman members command allows a site administrator to display, add, and delete members from a mailing list.

>>> command = cli('mailman.commands.cli_members.members')

Listing members

You can list all the members of a mailing list by calling the command with no options. To start with, there are no members of the mailing list.

>>> ant = create_list('[email protected]')
>>> command('mailman members ant.example.com')
ant.example.com has no members

Once the mailing list add some members, they will be displayed.

>>> from mailman.testing.helpers import subscribe
>>> subscribe(ant, 'Anne', email='[email protected]')
<Member: Anne Person <[email protected]> on [email protected]
         as MemberRole.member>
>>> subscribe(ant, 'Bart', email='[email protected]')
<Member: Bart Person <[email protected]> on [email protected]
         as MemberRole.member>

>>> command('mailman members ant.example.com')
Anne Person <[email protected]>
Bart Person <[email protected]>

Members are displayed in alphabetical order based on their address.

>>> subscribe(ant, 'Anne', email='[email protected]')
<Member: Anne Person <[email protected]> on [email protected]
         as MemberRole.member>

>>> command('mailman members ant.example.com')
Anne Person <[email protected]>
Anne Person <[email protected]>
Bart Person <[email protected]>

You can also output this list to a file.

>>> from tempfile import NamedTemporaryFile
>>> filename = cleanups.enter_context(NamedTemporaryFile()).name

>>> command('mailman members -o ' + filename + ' ant.example.com')
>>> with open(filename, 'r', encoding='utf-8') as fp:
...     print(fp.read())
Anne Person <[email protected]>
Anne Person <[email protected]>
Bart Person <[email protected]>

The output file can also be standard out.

>>> command('mailman members -o - ant.example.com')
Anne Person <[email protected]>
Anne Person <[email protected]>
Bart Person <[email protected]>

Filtering on delivery mode

You can limit output to just the regular non-digest members…

>>> member = ant.members.get_member('[email protected]')
>>> from mailman.interfaces.member import DeliveryMode
>>> member.preferences.delivery_mode = DeliveryMode.plaintext_digests

>>> command('mailman members --regular ant.example.com')
Anne Person <[email protected]>
Bart Person <[email protected]>

…or just the digest members. Furthermore, you can either display all digest members…

>>> member = ant.members.get_member('[email protected]')
>>> member.preferences.delivery_mode = DeliveryMode.mime_digests

>>> command('mailman members --digest any ant.example.com')
Anne Person <[email protected]>
Anne Person <[email protected]>

…just plain text digest members…

>>> command('mailman members --digest plaintext ant.example.com')
Anne Person <[email protected]>

…or just MIME digest members.

>>> command('mailman members --digest mime ant.example.com')
Anne Person <[email protected]>

Filtering on delivery status

You can also filter the display on the member’s delivery status. By default, all members are displayed, but you can filter out only those whose delivery status is enabled…

>>> from mailman.interfaces.member import DeliveryStatus

>>> member = ant.members.get_member('[email protected]')
>>> member.preferences.delivery_status = DeliveryStatus.by_moderator
>>> member = ant.members.get_member('[email protected]')
>>> member.preferences.delivery_status = DeliveryStatus.by_user

>>> member = subscribe(ant, 'Cris', email='[email protected]')
>>> member.preferences.delivery_status = DeliveryStatus.unknown
>>> member = subscribe(ant, 'Dave', email='[email protected]')
>>> member.preferences.delivery_status = DeliveryStatus.enabled
>>> member = subscribe(ant, 'Elle', email='[email protected]')
>>> member.preferences.delivery_status = DeliveryStatus.by_bounces

>>> command('mailman members --nomail enabled ant.example.com')
Anne Person <[email protected]>
Dave Person <[email protected]>

…or disabled by the user…

>>> command('mailman members --nomail byuser ant.example.com')
Bart Person <[email protected]>

…or disabled by the list administrator (or moderator)…

>>> command('mailman members --nomail byadmin ant.example.com')
Anne Person <[email protected]>

…or by the bounce processor…

>>> command('mailman members --nomail bybounces ant.example.com')
Elle Person <[email protected]>

…or for unknown (legacy) reasons.

>>> command('mailman members --nomail unknown ant.example.com')
Cris Person <[email protected]>

You can also display all members who have delivery disabled for any reason.

>>> command('mailman members --nomail any ant.example.com')
Anne Person <[email protected]>
Bart Person <[email protected]>
Cris Person <[email protected]>
Elle Person <[email protected]>

Adding members

You can add members to a mailing list from the command line. To do so, you need a file containing email addresses and full names that can be parsed by email.utils.parseaddr().

>>> bee = create_list('[email protected]')
>>> with open(filename, 'w', encoding='utf-8') as fp:
...     print("""\
... [email protected]
... Bart Person <[email protected]>
... [email protected] (Cate Person)
... """, file=fp)

>>> command('mailman members --add ' + filename + ' bee.example.com')

>>> from operator import attrgetter
>>> dump_list(bee.members.addresses, key=attrgetter('email'))
[email protected]
Bart Person <[email protected]>
Cate Person <[email protected]>

You can also specify - as the filename, in which case the addresses are taken from standard input.

>>> stdin = """\
... [email protected]
... Elly Person <[email protected]>
... [email protected] (Fred Person)
... """
>>> command('mailman members --add - bee.example.com', input=stdin)

>>> dump_list(bee.members.addresses, key=attrgetter('email'))
[email protected]
Bart Person <[email protected]>
Cate Person <[email protected]>
[email protected]
Elly Person <[email protected]>
Fred Person <[email protected]>

Blank lines and lines that begin with ‘#’ are ignored.

>>> with open(filename, 'w', encoding='utf-8') as fp:
...     print("""\
... [email protected]
... # [email protected]
...
... [email protected]
... """, file=fp)

>>> command('mailman members --add ' + filename + ' bee.example.com')

>>> dump_list(bee.members.addresses, key=attrgetter('email'))
[email protected]
Bart Person <[email protected]>
Cate Person <[email protected]>
[email protected]
Elly Person <[email protected]>
Fred Person <[email protected]>
[email protected]
[email protected]

Addresses which are already subscribed are ignored, although a warning is printed.

>>> with open(filename, 'w', encoding='utf-8') as fp:
...     print("""\
... [email protected]
... [email protected]
... [email protected]
... """, file=fp)

>>> command('mailman members --add ' + filename + ' bee.example.com')
Already subscribed (skipping): [email protected]
Already subscribed (skipping): [email protected]

>>> dump_list(bee.members.addresses, key=attrgetter('email'))
[email protected]
Bart Person <[email protected]>
Cate Person <[email protected]>
[email protected]
Elly Person <[email protected]>
Fred Person <[email protected]>
[email protected]
[email protected]
[email protected]

Deleting members

You can delete members from a mailing list from the command line. To do so, you need a file containing email addresses and full names that can be parsed by email.utils.parseaddr(). All mail addresses in the file will be deleted from the mailing list.

Assuming you have populated a mailing list with the code examples from above, use these code snippets to delete subscriptions from the list again.

>>> with open(filename, 'w', encoding='utf-8') as fp:
...     print("""\
... [email protected]
... [email protected] (Cate Person)
... """, file=fp)

>>> command('mailman members --delete ' + filename + ' bee.example.com')

>>> from operator import attrgetter
>>> dump_list(bee.members.addresses, key=attrgetter('email'))
Bart Person <[email protected]>
[email protected]
Elly Person <[email protected]>
Fred Person <[email protected]>
[email protected]
[email protected]ample.com
[email protected]

You can also specify - as the filename, in which case the addresses are taken from standard input.

>>> stdin = """\
... [email protected]
... Elly Person <[email protected]>
... """
>>> command('mailman members --delete - bee.example.com', input=stdin)

>>> dump_list(bee.members.addresses, key=attrgetter('email'))
Bart Person <[email protected]>
Fred Person <[email protected]>
[email protected]
[email protected]
[email protected]

Blank lines and lines that begin with ‘#’ are ignored.

>>> with open(filename, 'w', encoding='utf-8') as fp:
...     print("""\
... # [email protected]
...
... [email protected]
... """, file=fp)

>>> command('mailman members --delete ' + filename + ' bee.example.com')

>>> dump_list(bee.members.addresses, key=attrgetter('email'))
Fred Person <[email protected]>
[email protected]
[email protected]
[email protected]

Addresses which are not subscribed are ignored, although a warning is printed.

>>> with open(filename, 'w', encoding='utf-8') as fp:
...     print("""\
... [email protected]
... [email protected]
... """, file=fp)

>>> command('mailman members --delete ' + filename + ' bee.example.com')
Member not subscribed (skipping): [email protected]

>>> dump_list(bee.members.addresses, key=attrgetter('email'))
Fred Person <[email protected]>
[email protected]
[email protected]

Synchronizing members

You can synchronize all member addresses of a mailing list with the member addresses found in a file from the command line. To do so, you need a file containing email addresses and full names that can be parsed by email.utils.parseaddr(). All mail addresses not contained in the file will be deleted from the mailing list. Every address found in the specified file will be added to the specified mailing list.

Assuming you have populated a mailing list with the code examples from above, use these code snippets to synchronize mail addresses with subscriptions of the mailing list. Note that only changes of the mailing list will be written to output.

>>> with open(filename, 'w', encoding='utf-8') as fp:
...     print("""\
... [email protected]
... [email protected] (Cate Person)
... Fred Person <[email protected]>
... """, file=fp)

>>> command('mailman members --sync ' + filename + ' bee.example.com')
[ADD] [email protected]
[ADD] Cate Person <[email protected]>
[DEL] [email protected]
[DEL] [email protected]

>>> from operator import attrgetter
>>> dump_list(bee.members.addresses, key=attrgetter('email'))
[email protected]
Cate Person <[email protected]>
Fred Person <[email protected]>

You can also specify - as the filename, in which case the addresses are taken from standard input.

>>> stdin = """\
... [email protected]
... Elly Person <[email protected]>
... """
>>> command('mailman members --sync - bee.example.com', input=stdin)
[ADD] [email protected]
[ADD] Elly Person <[email protected]>
[DEL] [email protected]
[DEL] Cate Person <[email protected]>
[DEL] Fred Person <[email protected]>

>>> dump_list(bee.members.addresses, key=attrgetter('email'))
[email protected]
Elly Person <[email protected]>

Blank lines and lines that begin with ‘#’ are ignored.

>>> with open(filename, 'w', encoding='utf-8') as fp:
...     print("""\
... #[email protected]
... [email protected]
...
... [email protected]
... """, file=fp)

>>> command('mailman members --sync ' + filename + ' bee.example.com')
[ADD] [email protected]
[DEL] [email protected]

>>> dump_list(bee.members.addresses, key=attrgetter('email'))
Bart Person <[email protected]>
Elly Person <[email protected]>

If there is nothing to do, it will output just that.

>>> with open(filename, 'w', encoding='utf-8') as fp:
...     print("""\
... [email protected]
... [email protected]
... """, file=fp)

>>> command('mailman members --sync ' + filename + ' bee.example.com')
Nothing to do

>>> dump_list(bee.members.addresses, key=attrgetter('email'))
Bart Person <[email protected]>
Elly Person <[email protected]>

Displaying members

With no arguments, the command displays all members of the list.

>>> command('mailman members bee.example.com')
Bart Person <[email protected]>
Elly Person <[email protected]>