diff --git a/mailman_sync.py b/mailman_sync.py index aa0ed42..44623ed 100755 --- a/mailman_sync.py +++ b/mailman_sync.py @@ -17,6 +17,7 @@ Update Mailman 3 lists via a json API of the form: """ import argparse +import json import os import email.utils @@ -120,12 +121,56 @@ def sync_moderators( list.remove_moderator(member.address) +def update_cloudflare_lists( + dry_run: bool, + auth_token: str, + account_id: str, + script_name: str, + binding_name: str, + expected: list[str], +): + settings_api_url = f"https://api.cloudflare.com/client/v4/accounts/{account_id}/workers/scripts/{script_name}/settings" + + r = requests.get( + settings_api_url, + headers={"Authorization": auth_token}, + ) + + data = r.json() + # TODO: more error handling + if not data["success"]: + print("Failed to fetch Cloudflare email worker settings") + + bindings_dict = {b["name"]: b for b in data["result"]["bindings"]} + if set(bindings_dict.get(binding_name, {}).get("json", [])) != set(expected): + print("Cloudflare email worker list out of date, updating") + bindings_dict[binding_name] = { + "name": binding_name, + "json": expected, + "type": "json", + } + if not dry_run: + r = requests.patch( + settings_api_url, + headers={"Authorization": auth_token}, + files={ + "settings": json.dumps({"bindings": list(bindings_dict.values())}), + }, + ) + else: + print("Cloudflare email worker list already up to date") + + def main( api: str, api_auth: str, mailman_client: mailmanclient.Client, dry_run: bool, mail_host: str, + cf_auth_token: str, + cf_account_id: str, + cf_script_name: str, + cf_binding_name: str, ): r = requests.get(api, headers={"Authorization": api_auth}) if not r.ok: @@ -155,10 +200,16 @@ def main( sync_moderators(list, dry_run, props["moderators"]) sync_members(list, dry_run, props["members"]) + lists = [list.list_name.lower() for list in domain.get_lists()] + update_cloudflare_lists( + dry_run, cf_auth_token, cf_account_id, cf_script_name, cf_binding_name, lists + ) + def parse_arguments(): argp = argparse.ArgumentParser(description=__doc__) argp.add_argument("--api", required=True, help="API endpoint to retrieve JSON from") + argp.add_argument("--mail-host", help="Base domain for all lists", required=True) argp.add_argument( "--mailman-url", @@ -168,6 +219,24 @@ def parse_arguments(): argp.add_argument( "--mailman-user", help="Username for Mailman3 REST API", default="restadmin" ) + + argp.add_argument( + "--cf-auth-token", help="Auth token for CloudFlare API", required=True + ) + argp.add_argument( + "--cf-account-id", + help="Account ID of CloudFlare email worker script", + required=True, + ) + argp.add_argument( + "--cf-script-name", help="Name of CloudFlare email worker script", required=True + ) + argp.add_argument( + "--cf-binding-name", + help="Name of environment variable in email worker script", + default="MAILMAN_LISTS", + ) + argp.add_argument( "-n", "--dry-run", @@ -197,4 +266,8 @@ if __name__ == "__main__": mailman_client, args.dry_run, args.mail_host, + args.cf_auth_token, + args.cf_account_id, + args.cf_script_name, + args.cf_binding_name, )