Compare commits

..

2 Commits

2 changed files with 86 additions and 15 deletions

View File

@ -16,10 +16,11 @@ Update Mailman 3 lists via a json API of the form:
} }
""" """
import argparse import json
import os import os
import email.utils import email.utils
import configargparse
import mailmanclient import mailmanclient
import requests import requests
@ -120,12 +121,56 @@ def sync_moderators(
list.remove_moderator(member.address) 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( def main(
api: str, api: str,
api_auth: str, api_auth: str,
mailman_client: mailmanclient.Client, mailman_client: mailmanclient.Client,
dry_run: bool, dry_run: bool,
mail_host: str, 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}) r = requests.get(api, headers={"Authorization": api_auth})
if not r.ok: if not r.ok:
@ -155,10 +200,21 @@ def main(
sync_moderators(list, dry_run, props["moderators"]) sync_moderators(list, dry_run, props["moderators"])
sync_members(list, dry_run, props["members"]) 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(): def parse_arguments():
argp = argparse.ArgumentParser(description=__doc__) argp = configargparse.ArgumentParser(description=__doc__)
argp.add("-c", "--config", is_config_file=True, help="Config file path")
argp.add_argument("--api", required=True, help="API endpoint to retrieve JSON from") argp.add_argument("--api", required=True, help="API endpoint to retrieve JSON from")
argp.add_argument(
"--api-auth", required=True, help="API Authorization header token"
)
argp.add_argument("--mail-host", help="Base domain for all lists", required=True) argp.add_argument("--mail-host", help="Base domain for all lists", required=True)
argp.add_argument( argp.add_argument(
"--mailman-url", "--mailman-url",
@ -168,6 +224,27 @@ def parse_arguments():
argp.add_argument( argp.add_argument(
"--mailman-user", help="Username for Mailman3 REST API", default="restadmin" "--mailman-user", help="Username for Mailman3 REST API", default="restadmin"
) )
argp.add_argument(
"--mailman-pass", help="Password for Mailman3 REST API", required=True
)
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( argp.add_argument(
"-n", "-n",
"--dry-run", "--dry-run",
@ -180,21 +257,20 @@ def parse_arguments():
if __name__ == "__main__": if __name__ == "__main__":
args = parse_arguments() args = parse_arguments()
for env_var in ("API_AUTH", "MAILMAN_PASS"):
if env_var not in os.environ:
print(f"Missing {env_var} environment variable")
exit(-1)
mailman_client = mailmanclient.Client( mailman_client = mailmanclient.Client(
args.mailman_url + "/3.1", args.mailman_url + "/3.1",
args.mailman_user, args.mailman_user,
os.environ["MAILMAN_PASS"], args.mailman_pass,
) )
main( main(
args.api, args.api,
os.environ["API_AUTH"], args.api_auth,
mailman_client, mailman_client,
args.dry_run, args.dry_run,
args.mail_host, args.mail_host,
args.cf_auth_token,
args.cf_account_id,
args.cf_script_name,
args.cf_binding_name,
) )

View File

@ -6,9 +6,4 @@ User=mailman
Group=mailman Group=mailman
Type=oneshot Type=oneshot
TimeoutStartSec=600 TimeoutStartSec=600
EnvironmentFile=/opt/mailman-sync/env ExecStart=/usr/bin/python3 /opt/mailman-sync/mailman_sync.py --config /opt/mailman-sync/config.yml
ExecStart=/usr/bin/python3 /opt/mailman-sync/mailman_sync.py \
--api https://paperwork.claremontmakerspace.org/api/v1/paperwork/department/mailing_lists.json \
--mail-host claremontmakerspace.org \
--mailman-url http://localhost:8001 \
--mailman-user restadmin