#!/usr/bin/env python """ ---------------------------------------------------------------------------- "THE BEER-WARE LICENSE" (Revision 42): wrote this file. As long as you retain this notice you can do whatever you want with this stuff. If we meet some day, and you think this stuff is worth it, you can buy me a beer in return. Tycho Andersen (Shamelessly stolen from: http://people.freebsd.org/~phk/) ---------------------------------------------------------------------------- Usage: edit the CONFIG_FILE as below. If you decide to use the keyring piece, you'll need an entry for the "gmail" service which matches your username. The keyring backend used is the one recommended by python-keyring (on ubuntu you can install python-keyring-gnome or python-keyring-kwallet depending on which you use). The simplest way to set this would be to do something like the following: tycho@mittens:~$ python Python 2.6.5 (r265:79063, Apr 16 2010, 13:57:41) [GCC 4.4.3] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import keyring >>> keyring.set_password("gmail", "tycho@tycho.ws", "secret") In your ~/.conkyrc, you'll want something like the following: Inbox: ${execi 30 ~/bin/gmail_unread.py} csmail: ${execi 30 ~/bin/gmail_unread.py csmail} This displays the number of unread messages in the Inbox and under the label csmail. Bugs: email me. Enjoy! """ import sys import os import urllib2 import base64 from subprocess import call from xml.dom.minidom import parse from ConfigParser import ConfigParser from pynotify import Notification from keyring import get_password CONF_FILE = os.path.expanduser("~/.conkygmail") def _get_config(): """ Read in the config file and set defaults if necessary. Your config file should go in the location specified by the CONF_FILE variable above, and look like this: [settings] username = tycho@tycho.ws # foo@gmail.com #password = secret # note: I do not recommend this! If you leave this # commented, this script will ask for the keyring # specified by keyring, which does not require your # password to be in plaintext. libnotify = True # do you want libnotify when you have new messages? Note: a section called "num_unread_messages" will also appear. This is for internal use by the script, and enables it to ``remember'' how many unread e-mails there were under a particular label, so it doesn't notify unless necessary. """ conf = ConfigParser() conf.read([CONF_FILE]) if not conf.has_section("settings"): conf.add_section("settings") if not conf.has_option("settings", "username"): conf.set("settings", "username", "tycho@tycho.ws") if not conf.has_option("settings", "libnotify"): conf.set("settings", "libnotify", "True") if not conf.has_section("num_unread_messages"): conf.add_section("num_unread_messages") return conf def _flatten_entry(entry): """ Flatten the xml node. """ def get_text(nodelist): rc = [] for node in nodelist: if node.nodeType == node.TEXT_NODE: rc.append(node.data) return ''.join(rc) entdict = {} entdict["title"] = get_text(entry.getElementsByTagName("title")[0].childNodes) entdict["summary"] = get_text(entry.getElementsByTagName("summary")[0].childNodes) entdict["modified"] = get_text(entry.getElementsByTagName("modified")[0].childNodes) entdict["issued"] = get_text(entry.getElementsByTagName("issued")[0].childNodes) entdict["id"] = get_text(entry.getElementsByTagName("id")[0].childNodes) author = entry.getElementsByTagName("author")[0] entdict["name"] = get_text(author.getElementsByTagName("name")[0].childNodes) entdict["email"] = get_text(author.getElementsByTagName("email")[0].childNodes) return entdict def get_unread(label = ""): """ Returns a list of (flattened) dictionaries representing the unread messages of a particular label (by default the inbox). The dictionaries may have the following elements: * title - subject of the e-mail * summary - first few sentences of the e-mail * modified - see below * issued - these two are /usually/ the same, and are the time the e-mail was received * id - the msgid of the e-mail * name - the name of the sender * email - the address of the sender """ config = _get_config() username = config.get("settings", "username") password = "secret" if not config.has_option("settings", "password"): password = get_password("gmail", username) else: password = config.get("settings", "password") url = "https://mail.google.com/mail/feed/atom/" if not username.endswith("@gmail.com"): url = ("https://mail.google.com/a/%s/feed/atom/" % (username[username.find("@")+1:])) url += label req = urllib2.Request(url) req.add_header("Authorization", "Basic %s" % (base64.encodestring("%s:%s" % (username, password))[:-1])) dom = parse(urllib2.urlopen(req)) return [ _flatten_entry(ent) for ent in dom.getElementsByTagName("entry") ] if __name__ == "__main__": if len(sys.argv) > 2: print "ERROR: You can only get messages from one label at a time!" sys.exit() label = "" if len(sys.argv) > 1: label = sys.argv[1] messages = get_unread(label) # Now that we're done fetching, we can refer to label by its real name. label = label if label else "inbox" config = _get_config() if not config.has_option("num_unread_messages", label): config.set("num_unread_messages", label, "0") # Show the notification if the user wants it and there are new unread # messages. if config.getboolean("settings", "libnotify") and \ len(messages) > config.getint("num_unread_messages", label): summary = "You have %d new message(s) in %s." % (len(messages), label) authors = [ message["name"] for message in messages ] details = "From: " + ", ".join(authors) + "." Notification(summary, details).show() # update the db with the number of read messages config.set("num_unread_messages", label, len(messages)) config.write(open(CONF_FILE, "w+")) # output for conky print len(messages)