Created
September 4, 2012 16:43
-
-
Save mcsf/3623303 to your computer and use it in GitHub Desktop.
Simple e-mail notifications for Gmail. Requires Desktop Notifications and wget/curl.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# Limitations: | |
# - only the last sender will be shown for a given conversation with | |
# multiple new messages | |
# TODO | |
# - chmod for config file | |
# - config for every default value | |
# DEFAULTS ############################################################# | |
PERIOD = 30 | |
FETCHER = 'Wget' # 'Wget' or 'Curl' | |
ALERT_CMD = 'notify-send \'Incoming e-mail\' -- \'%s\'' | |
######################################################################## | |
import ConfigParser | |
import os.path | |
import subprocess | |
import time | |
import xml.etree.ElementTree as et | |
FEED_URL = 'https://mail.google.com/mail/feed/atom' | |
NAMESPACE = '{http://purl.org/atom/ns#}' | |
SECTION = 'General' | |
CONFIG_FILE = '~/.gmail-notifier' | |
USER_MSG = 'Please edit the configuration file at %s.' % CONFIG_FILE | |
# EXCEPTIONS ########################################################### | |
class FormatError(Exception): | |
def __str__(self): | |
return 'Command format must be specified.' | |
class FetchError(Exception): | |
def __init__(self, err): | |
self.err = err | |
def __str__(self): | |
return 'Couldn\'t fetch Gmail feed.\n' + str(self.err) | |
# FETCHERS ############################################################# | |
class FeedFetcher: | |
cmd_format = None | |
def __init__(self, user, passwd, url=None): | |
if not self.cmd_format: | |
raise FormatError | |
self.user = user | |
self.passwd = passwd | |
self.url = url or FEED_URL | |
self.cmd = self.cmd_format % self.__dict__ | |
def fetch(self): | |
try: | |
return subprocess.check_output(self.cmd, shell=True) | |
except subprocess.CalledProcessError, e: | |
raise FetchError(e) | |
class Wget(FeedFetcher): | |
cmd_format = ( 'wget ' | |
+ '--user %(user)s ' | |
+ '--password %(passwd)s ' | |
+ '-O- ' | |
+ FEED_URL ) | |
class Curl(FeedFetcher): | |
cmd_format = ( 'curl ' | |
+ '--user %(user)s:%(passwd)s ' | |
+ FEED_URL ) | |
# HELPERS ############################################################## | |
def wrap(s): return NAMESPACE + s | |
def show(entry): | |
author = entry.find(wrap('author')).find(wrap('name')).text | |
subject = entry.find(wrap('title')).text | |
return '- %s: %s' % (author, subject) | |
# PARSE CONFIG FILE #################################################### | |
def get_credentials(): | |
config = ConfigParser.RawConfigParser() | |
filepath = os.path.expanduser(CONFIG_FILE) | |
config.read([filepath]) | |
if config.has_option(SECTION, 'username') \ | |
and config.has_option(SECTION, 'password'): | |
u = config.get(SECTION, 'username') | |
p = config.get(SECTION, 'password') | |
if '' in [u, p]: | |
return False | |
return (u, p) | |
else: | |
if not config.has_section(SECTION): | |
config.add_section(SECTION) | |
config.set(SECTION, 'username', '') | |
config.set(SECTION, 'password', '') | |
with open(filepath, 'w') as cf: | |
config.write(cf) | |
return False | |
# MAIN ################################################################# | |
def main(): | |
try: | |
user, passwd = get_credentials() | |
except TypeError: | |
print USER_MSG | |
return 1 | |
inbox = {} | |
fetcher = eval(FETCHER)(user, passwd) | |
while True: | |
print '---' | |
feed = fetcher.fetch() | |
try: | |
root = et.fromstring(feed) | |
except et.ParseError, e: | |
print e | |
time.sleep(PERIOD) | |
continue | |
oldbox = inbox | |
inbox = {} | |
new = [] | |
for e in root.iter(wrap('entry')): | |
tag = e.find(wrap('id')).text | |
inbox[tag] = e | |
if not oldbox.get(tag): | |
new.insert(0, e) | |
if new: | |
body = '\n'.join(map(show, new)) | |
subprocess.call(ALERT_CMD % body, shell=True) | |
time.sleep(PERIOD) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment