Skip to content

Instantly share code, notes, and snippets.

@huyz
Created November 23, 2022 08:10
Show Gist options
  • Save huyz/6128fa639f0d028c1cbd414936b3786d to your computer and use it in GitHub Desktop.
Save huyz/6128fa639f0d028c1cbd414936b3786d to your computer and use it in GitHub Desktop.
Sort Thunderbird/Postbox identities
#!/usr/bin/env python
# For a given Thunderbird/Postbox account, this sorts the identities by email address.
# Only supports macOS (specific to macOS pgrep) right now, but could be easily tweaked for Linux.
# Prerequisites: pgrep, difft (difftastic)
from operator import itemgetter
from os import environ
from sys import platform, stderr
from typing import Dict
import re
import shutil
import subprocess
import tempfile
#### Config
DEBUG = False
PREFS_FILENAME = (
environ["HOME"]
+ "/Library/Application Support/Thunderbird/Profiles/XXXXXX.default/prefs.js"
)
# Look at prefs.js and figure out the ID of the account whose identities you want to sort
ACCOUNT_ID = 4
# If defined, this ID will be prioritized during the sort
PRIMARY_EMAIL_ADDRESS = ""
PGREP_COMMAND = shutil.which("pgrep") or "/usr/bin/pgrep"
DIFFT_COMMAND = shutil.which("difft") or "/opt/homebrew/bin/difft"
#### Init
if platform != "darwin":
stderr.write(f"error: Only supports macOS at this time.\n")
exit(1)
IDS_LINE_PREFIX = f'user_pref("mail.account.account{ACCOUNT_ID}.identities"'
#### Read
# Read in a line from a file
with open(PREFS_FILENAME) as f:
ids_line = [l for l in f.readlines() if l.startswith(IDS_LINE_PREFIX)][0]
if not ids_line:
stderr.write(f"Can't find matching line for ID {ACCOUNT_ID} in {PREFS_FILENAME}\n")
exit(1)
if DEBUG:
print(ids_line)
ids = set(re.sub(r'.*identities",\s*"([^"]+)"\);\s*$', r"\1", ids_line).split(","))
if DEBUG:
print(ids)
with open(PREFS_FILENAME) as f:
emails: Dict[str, str] = dict()
for line in f.readlines():
m = re.search(
r'^user_pref\("mail\.identity\.(id\d+)\.useremail",\s*"([^"]+)"\);\s*', line
)
if m and m.group(1) in ids:
emails[m.group(1)] = m.group(2)
#### Output
sorted_ids_line = ",".join(
map(
itemgetter(0),
sorted(
emails.items(), key=lambda x: "" if x[1] == PRIMARY_EMAIL_ADDRESS else x[1]
),
)
)
output = f'{IDS_LINE_PREFIX}, "{sorted_ids_line}");'
print(output)
#### Write
# Check if Postbox/ThunderBird is running
if "PostboxApp" in PREFS_FILENAME:
process_name = "Postbox.app"
elif "Thunderbird" in PREFS_FILENAME:
process_name = "Thunderbird.app"
else:
stderr.write(
f"error: Can't determine app name from {PREFS_FILENAME}; can't update file.\n"
)
exit(1)
# TODO: support linux pgrep as well.
process = subprocess.Popen(
[PGREP_COMMAND, "-f", process_name], stdout=subprocess.PIPE, stderr=subprocess.PIPE
)
my_pid, err = process.communicate()
if err:
stderr.write(f"Error {err} while pgrep'ing {process_name}; can't update file.\n")
exit(1)
if my_pid:
stderr.write(f"\nWarning: {process_name} is running; can't update file.\n")
exit(1)
print(f"\nWARNING: {PREFS_FILENAME} will be updated thusly:\n")
with tempfile.NamedTemporaryFile("wt", delete=False) as tmp:
with open(PREFS_FILENAME) as f:
for line in f.readlines():
if line.startswith(IDS_LINE_PREFIX):
tmp.write(f"{output}\n")
else:
tmp.write(line)
tmp.flush()
# TODO: fall back to regular diff?
subprocess.run([DIFFT_COMMAND, PREFS_FILENAME, tmp.name])
answer = input("Perform update? [y/N] ")
if answer.lower() in ["y", "yes"]:
shutil.copyfile(tmp.name, PREFS_FILENAME)
print("File updated.")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment