diff --git a/mailman_sync.py b/mailman_sync.py index a172e69..c99d9d8 100755 --- a/mailman_sync.py +++ b/mailman_sync.py @@ -5,6 +5,7 @@ Update Mailman 2 lists via a json API of the form {"LIST": ["ADDRESS", ...]} """ import argparse +from dataclasses import dataclass import os from pathlib import Path import secrets @@ -18,28 +19,33 @@ PASSWORD_CHARS = string.ascii_letters + string.digits + string.punctuation PASSWORD_LEN = 18 -def newlist(mailman_bin: Path, listname: str, urlhost: str, admin: str): - password = "".join(secrets.choice(PASSWORD_CHARS) for i in range(PASSWORD_LEN)) +@dataclass +class ListManager: + mailman_bin: Path + list_name: str + dry_run: bool - output = subprocess.run( - [ - mailman_bin / "newlist", - "--quiet", - f"--urlhost={urlhost}", - listname, - admin, - password, - ], - encoding="ascii", - capture_output=True, - check=True, - ) - for line in output.stdout.splitlines(): - print(f"[Creating {listname}] {line}") + def newlist(self, urlhost: str, admin: str): + password = "".join(secrets.choice(PASSWORD_CHARS) for i in range(PASSWORD_LEN)) + output = subprocess.run( + [ + self.mailman_bin / "newlist", + "--quiet", + f"--urlhost={urlhost}", + self.list_name, + admin, + password, + ], + encoding="ascii", + capture_output=True, + check=True, + ) + for line in output.stdout.splitlines(): + print(f"[Creating {self.list_name}] {line}") -def config_list(mailman_bin: Path, mailing_list: str, dry_run: bool): - config_changes = """ + def config_list(self): + config_changes = """ # TODO: set real_name, moderator, subject prefix, and reply-to address from_is_list = 1 anonymous_list = 1 @@ -60,54 +66,51 @@ generic_nonmember_action = 3 # discard non-member emails forward_auto_discards = 0 # don't notify admin about discards """ - with tempfile.NamedTemporaryFile("w", suffix=".py") as config_file: - config_file.write(config_changes) - config_file.flush() + with tempfile.NamedTemporaryFile("w", suffix=".py") as config_file: + config_file.write(config_changes) + config_file.flush() + command = [ + self.mailman_bin / "config_list", + "--inputfile", + config_file.name, + self.list_name, + ] + if self.dry_run: + command.append("--checkonly") + + output = subprocess.run( + command, + encoding="ascii", + capture_output=True, + check=True, + ) + for line in output.stdout.splitlines(): + print(f"[Configuring {self.list_name}] {line}") + + def sync_members(self, members: list[str]): command = [ - mailman_bin / "config_list", - "--inputfile", - config_file.name, - mailing_list, + self.mailman_bin / "sync_members", + "--welcome-msg=no", + "--goodbye-msg=no", + "--notifyadmin=no", + "--file", + "-", + self.list_name, ] - if dry_run: - command.append("--checkonly") + if self.dry_run: + command.append("--no-change") + members_data = "\n".join(members) output = subprocess.run( command, + input=members_data, encoding="ascii", capture_output=True, check=True, ) for line in output.stdout.splitlines(): - print(f"[Configuring {mailing_list}] {line}") - - -def sync_members( - mailman_bin: Path, mailing_list: str, members: list[str], dry_run: bool -): - command = [ - mailman_bin / "sync_members", - "--welcome-msg=no", - "--goodbye-msg=no", - "--notifyadmin=no", - "--file", - "-", - mailing_list, - ] - if dry_run: - command.append("--no-change") - - members_data = "\n".join(members) - output = subprocess.run( - command, - input=members_data, - encoding="ascii", - capture_output=True, - check=True, - ) - for line in output.stdout.splitlines(): - print(f"[Syncing {mailing_list}] {line}") + print(f"[Syncing {self.list_name}] {line}") def main( @@ -133,16 +136,17 @@ def main( certification_lists = r.json() for name, members in certification_lists.items(): list_name = name + list_suffix + list_manager = ListManager(mailman_bin, list_name, dry_run) if list_name not in existing_lists: if dry_run: print(f"Skipping non-existing list {list_name} in dry run mode") continue else: - newlist(mailman_bin, list_name, urlhost, admin) + list_manager.newlist(urlhost, admin) print(f"Configuring/syncing {list_name}...") - config_list(mailman_bin, list_name, dry_run) - sync_members(mailman_bin, list_name, members, dry_run) + list_manager.config_list() + list_manager.sync_members(members) def parse_arguments():