#!/usr/bin/env python # -*- coding: utf-8 -*- ############################################# # check-gmail.py # # (c) 2006 Timothy Kilbourn # # A script to check for messages on a Gmail account # Uses gmailatom.py by Juan Grande (juan.grande@gmail.com) # # 11.27.2006 created # 11.29.2006 added username option, output for no new messages # added utf-8 character handling # 12.04.2006 added summary hashes to print new messages only # the first time they appear # added graceful exiting from KeyboardInterrupts # 12.05.2006 general code cleanups # added exception handling # added message filtering # # To do: # * Clean up all of the code so it is more object-oriented # * Fix filtering so it matches [SPAM] from CITES spam filtering # * Make filtering more flexible # * Add a configuration file (but not for passwords!) # ############################################# import gmailatom import getpass import time import sre import md5 from optparse import OptionParser from datetime import datetime # Constants FirstTry = 1 UpdatePeriod = 5 DefaultTries = 1 DefaultDelay = 60 MinimumDelay = 15 DefaultTimeFormat = "%H:%M %P" DefaultMessageEncoding = "utf-8" Filter = False Prog = "check-gmail" Version = "0.6" Desc = "A commandline program for checking a Gmail account for new messages" Usage = "%prog -u USER [-t TIME] [-n NUM] [-f EXP]" HTMLEscapes = { \ "…": "...", \ "<": "<", \ ">": ">", \ "&": "&", \ "'": "'", \ """: "\"", \ } # Auxiliary functions def uprint(s,enc=DefaultMessageEncoding): print s.encode(enc) def now(format=DefaultTimeFormat): return datetime.now().strftime(format) class GmailMessage: author_name="" subject="" summary="" md5=None def __init__(self,author,subject,summary): self.author_name=author self.subject=subject self.summary=summary self.normalize_summary() self.md5=md5.new(self.summary.encode(DefaultMessageEncoding)) def normalize_summary(self): for (escape, str) in HTMLEscapes.items(): self.summary = self.summary.replace(escape, str) def hash(self): return self.md5.hexdigest() # Commandline parser parser = OptionParser(usage=Usage, version="%s %s" % (Prog, Version), description=Desc, prog=Prog) parser.add_option( "-n", "--number-of-tries", type="int", metavar="NUM", dest="tries", help="number of times to check for messages (0 for infinite)") parser.add_option( "-t", "--time", type="int", metavar="TIME", dest="delay", help="time (in seconds) to wait between tries") parser.add_option( "-u", "--user", type="string", metavar="USER", dest="username", help="Gmail account username (without the @gmail.com)") parser.add_option( "-f", "--filter", type="string", metavar="EXP", dest="filter", help="prevent messages with EXP from being reported (uses regular expression syntax)" ) # Commandline defaults parser.set_defaults(tries=DefaultTries) parser.set_defaults(delay=DefaultDelay) # Check commandline options (options, args) = parser.parse_args() if options.tries < 0: options.tries = 1 if options.delay < MinimumDelay: options.delay = MinimumDelay if options.username == None: parser.error("a username is required for the -u option") if options.filter != None: Filter = True msg_filter = sre.compile(options.filter) # Get the password password = getpass.getpass( "Enter the password for %s@gmail.com: " % options.username ) # Gmail check setup g = gmailatom.GmailAtom( options.username, password ) tryno = FirstTry prev_unread = False hashes = [] # Loop while (options.tries == 0) or (tryno <= options.tries): if tryno > FirstTry: try: time.sleep(options.delay) except KeyboardInterrupt: break try: g.refreshInfo() except Exception, e: print "An error has occurred: %s" % e print "(retrying in %d seconds...)" % options.delay continue unread = g.getUnreadMsgCount() if unread > 0: if unread > 1: unread_plural = "s" else: unread_plural = "" # iterate through the messages and look for new ones new_msgs=[] filtered_msgs = 0 for i in range(0,unread): if Filter and (sre.match(msg_filter, g.getMsgTitle(i)) or \ sre.match(msg_filter, g.getMsgSummary(i))): filtered_msgs = filtered_msgs + 1 continue msg = GmailMessage(g.getMsgAuthorName(i),\ g.getMsgTitle(i),\ g.getMsgSummary(i)) if hashes.count(msg.hash()) == 0: hashes.append(msg.hash()) new_msgs.append(msg) if len(new_msgs) > 0: prev_unread = True if len(new_msgs) > 1: new_plural = "s" else: new_plural = "" if Filter and filtered_msgs > 0: filter_str = "%d filtered, " % filtered_msgs else: filter_str = "" print "%d new unread message%s! (%s%d total unread message%s) (%s)" % (len(new_msgs), new_plural, filter_str, unread, unread_plural, now()) for msg in new_msgs: uprint("Message %d:" % (new_msgs.index(msg) + 1)) uprint(" From: %s" % msg.author_name) uprint(" Subject: %s" % msg.subject) uprint(" %s" % msg.summary) print else: if (tryno % UpdatePeriod == 0) \ or (tryno == options.tries): if Filter and filtered_msgs > 0: filter_str = "(%d filtered)" % filtered_msgs else: filter_str = "" print "%d unread message%s %s (%s)" % (unread, unread_plural, filter_str, now()) else: # Print "No unread messages" under various circumstances if (prev_unread == True) \ or (tryno == FirstTry) \ or (tryno % UpdatePeriod == 0) \ or (tryno == options.tries): print "No unread messages (%s)" % (now()) prev_unread = False tryno = tryno + 1