Skip to content

Instantly share code, notes, and snippets.

@adamwalz
Created April 28, 2013 19:28
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save adamwalz/5478083 to your computer and use it in GitHub Desktop.
Save adamwalz/5478083 to your computer and use it in GitHub Desktop.
Python script to export Apples Mail.app rules into gmail filters
from plistlib import readPlist
from cgi import escape
from lxml.etree import SubElement as sub
import lxml.etree
# Load in apple mail rules file
rules_file = '~/Library/Mail/V2/MailData/SyncedRules.plist'
rules = readPlist(rules_file)
# Create gmail filters xml header
APPS_NAMESPACE = 'http://schemas.google.com/apps/2006'
NSMAP = {None: 'http://www.w3.org/2005/Atom', 'apps': APPS_NAMESPACE}
feed = lxml.etree.Element('feed', nsmap=NSMAP)
title = sub(feed, 'title').text = 'Mail Filters'
author = sub(feed, 'author')
sub(author, 'name').text = 'Adam Walz'
sub(author, 'email').text = 'adam@adamwalz.net'
# Parse apple mail rules
prop = '{' + APPS_NAMESPACE + "}property"
for rule in rules:
if rule['Criteria'][0]['Header'] == 'AnyMessage':
continue
# All mail rules have the following sub elements
entry = sub(feed, 'entry')
sub(entry, 'category', attrib={'term': 'filter'})
sub(entry, 'title').text = 'Mail Filter'
sub(entry, 'content')
logic = ' OR ' if rule['AllCriteriaMustBeSatisfied'] == 'NO' else ''
# Create rule string
newcriteria = ''
for criteria in rule['Criteria']:
if newcriteria:
newcriteria = newcriteria + logic
qual = '+"' if criteria.get('Qualifier') == 'IsEqualTo' else '('
negation = ' -' if criteria.get('Qualifier') == 'DoesNotContain' \
else ''
exp = qual + criteria['Expression']
header = criteria['Header']
if header == 'From':
newcriteria = newcriteria + negation + 'from:' + exp
elif header == 'To' or header == 'AnyRecipient':
newcriteria = newcriteria + negation + 'deliveredto:' + exp
elif header == 'Subject':
newcriteria = newcriteria + negation + 'subject:' + exp
elif header == 'Body':
newcriteria = newcriteria + negation + exp
if criteria.get('Qualifier') == 'IsEqualTo':
newcriteria = newcriteria + '"'
else:
newcriteria = newcriteria + ')'
newcriteria = escape(newcriteria)
# Create label
label = []
for part in rule.get('CopyToMailbox', '').split('/'):
if part.endswith('.mbox'):
label.append(part.rstrip('.mbox').lower())
# sub elements unique to rule
sub(entry, prop, attrib={'name': 'hasTheWord', 'value': newcriteria})
sub(entry, prop, attrib={'name': 'label', 'value': '/'.join(label)})
# Print gmail filters
# print(lxml.etree.tostring(feed,
# xml_declaration=True,
# encoding="utf-8",
# pretty_print=True))
print(lxml.etree.tounicode(feed))
@543345
Copy link

543345 commented Jun 11, 2020

Hello! I'm a full-on noob in this domain, but your code answered my query exactly - i just don't know how to use it, whether it's safe to use and/or has been tested or not. I'm using a MacBook Pro 13.3", running a gmail account (+1 other) through apple mail and want my (apple)mail rules to be exported to my gmail account filters, so that my iPhone mail app has those rules already applied. I think that as I have created rules on my MacBook but am not using an iCloud email address the MacBook rules aren't working on my iPhone, but my original gmail filters are. Any help much appreciated. J

@Jeguito
Copy link

Jeguito commented Nov 2, 2021

Following… how can we lunch/use your code?
thanks in advance.
cheers

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment