Typically my spare time today was spent in the folllowing proportions: 2 hours allotted to emptying my inbox; 3 minutes spent actually emptying my inbox; 1 hour, 57 minutes spent writing a program to theoretically “help” me empty my inbox.
Spreading the procrastination around, here’s that code. It’s for the millions of people who, like me, use good old command line mutt and a graphical contact manager on Unix (although I think it may have more uses than that). It takes a standard email, pulls out the name and email of the person the mail is from, generates a vCard, and then attempts to open that vCard in your nearby contact manager. It uses vobject, hewn from the glowing pits of Chandler, which you can install with easy_install vobject
or apt-get install python-vobject
. Hours of entertainment.
Things you might be able to glean from this:
- my vaguely okay wrapper for command line python utilities, concealed in the Main() class.
- vobject is pretty good for handling vCard and iCal objects. I’ve used it a few times.
- if you ever want to open a file using the correct handler under GNOME/KDE, xdg-open is your friend.
- my ingenious solution for not knowing when the contact handler (in my case, Kontact) will actually pick up the vCard, which is stored in a temporary file that I delete at the end of my program — just wait five seconds, and then delete it. How hacky is that?
- that python’s subprocess module badly needs more helper utilities, IMHO. That subprocess.Popen stuff is just Python’s new way of saying “run a shell command”, and it should be clearer than that.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
#!/usr/bin/env python ## # email2vcf.py ### """email2vcf.py """ __version__ = "0.1" __author__ = "Danny O'Brien <http://www.spesh.com/danny/>" __license__ = "GPL v3" import vobject import email from email.Utils import parseaddr import tempfile, os, subprocess, time class VcardableMessage(email.Message.Message): def toVcard(self): r""" >>> from StringIO import StringIO as st >>> em = "To: me <me@spesh.com\nSubject: a test\nFrom: Testy McTest <test@spesh.com>\n\nThis is my phone numner: +1 555 134 5678\nThanks!\n\nd." >>> p = email.Parser.Parser(VcardableMessage) >>> m = p.parse(st(em)) >>> v = m.toVcard() >>> print v.n <N{} McTest Testy > >>> print v.fn <FN{}Testy McTest> >>> print v.email <EMAIL{}test@spesh.com> >>> print v.note.value From email message-id: Unknown Dated: Unknown Subject: a test """ v = vobject.vCard() (name, email) = parseaddr(self['From']) family = None givenname = None if ',' in name: (family, givenname) = name.split(',') if ' ' in name: (family, givenname) = name.rsplit(' ', 1) if not family: givenname = name v.add('fn').value = name v.add('n').value = vobject.vcard.Name(family = family, given = givenname) v.add('email').value = email mid = self.get('message-id', 'Unknown') dt = self.get('date', 'Unknown') sub = self.get('subject','Unknown') v.add('note').value = 'From email message-id: %s\nDated: %s\nSubject: %s' % (mid, dt, sub) return v def main(args): """ Convert mail to vcard, then kick up systems' vcard handler""" if len(args) == 0: st = sys.stdin if len(args) == 1: st = file(args[0],'r') p = email.Parser.Parser(VcardableMessage) m = p.parse(st) v = m.toVcard() f = tempfile.NamedTemporaryFile(prefix="email2vcf", suffix=".vcf") f.write(v.serialize()) f.flush() p= subprocess.Popen("xdg-open" + " " + f.name, shell=True) sts = os.waitpid(p.pid, 0) time.sleep(5) # give app long enough to import the data f.close() import sys, getopt class Main(): """ Encapsulates option handling. Subclass to add new options, add 'handle_x' method for an -x option, add 'handle_xlong' method for an --xlong option help (-h, --help) should be automatically created from module docstring and handler docstrings. test (-t, --test) will run all docstring and unittests it finds """ class Usage(Exception): def __init__(self, msg): self.msg = msg def __init__(self): handlers = [i[7:] for i in dir(self) if i.startswith('handle_') ] self.shortopts = ''.join([i for i in handlers if len(i) == 1]) self.longopts = [i for i in handlers if (len(i) > 1)] def handler(self,option): i = 'handle_%s' % option.lstrip('-') if hasattr(self, i): return getattr(self, i) def default_main(self, args): print sys.argv[0]," called with ", args def handle_help(self, v): """ Shows this message """ print sys.modules.get(__name__).__doc__ descriptions = {} for i in list(self.shortopts) + self.longopts: d=self.handler(i).__doc__ if d in descriptions: descriptions[d].append(i) else: descriptions[d] = [i] for d, o in descriptions.iteritems(): for i in o: if len(i) == 1: print '-%s' % i, else: print '--%s' % i, print print d sys.exit(0) handle_h=handle_help def handle_test(self, v): """ Runs test suite for file """ import doctest import unittest suite = unittest.defaultTestLoader.loadTestsFromModule(sys.modules.get(__name__)) suite.addTest(doctest.DocTestSuite()) runner = unittest.TextTestRunner() runner.run(suite) sys.exit(0) handle_t=handle_test def run(self, main= None, argv=None): """ Execute main function, having stripped out options and called the responsible handler functions within the class. Main defaults to listing the remaining arguments. """ if not callable(main): main = self.default_main if argv is None: argv = sys.argv try: try: opts, args = getopt.getopt(argv[1:], self.shortopts, self.longopts) except getopt.error, msg: raise self.Usage(msg) for o, a in opts: (self.handler(o))(a) return main(args) except self.Usage, err: print >>sys.stderr, err.msg self.handle_help(None) return 2 if __name__ == "__main__": sys.exit(Main().run(main) or 0) |
August 18th, 2008 at 5:31 am
nice lifehack.. now you have an empty mailbox and a contact manager full of junk ? :)
I found the best way to clean my inbox (if I ever feel like it) is to order it first by subject and then by sender. You’d be amazed how everything is linked together and bulk of mails stand out ready for deletion.Wurkz !
August 18th, 2008 at 8:03 pm
sadly, i don’t think i even used it on the mail that I wanted to use it on…
yes, that is a good tip! i’ve heard that from Cory too!
August 19th, 2008 at 4:39 pm
Oh, there’s a slight bug there, too
should be