Instant Gmail Prowl Script

An iPhone client for Growl available on the App Store.
joshua.menke
Harmless
Posts: 7
Joined: Wed Jul 08, 2009 3:49 pm

Instant Gmail Prowl Script

Postby joshua.menke » Wed Jul 08, 2009 5:31 pm

Here's a hacked together python script that will almost instantly notify you when you get Gmail. You can use it with any IMAP account that supports IMAP IDLE.

Why is this better than just using Growl?
I can't speak for Growl on the Mac, but the Windows Gmail Growl notifier polls the atom feed. Google recommends only doing this every 10 minutes. This means you get notified within 5 minutes of new emails on average.

If you like getting notified faster, this is almost instant. It uses IDLE which means the IMAP server will notify the script instead of it having to poll. It's more like "push"

I hacked it together modifying some guy's python idle script.

It requires prowlpy.py: http://github.com/jacobb/prowlpy/tree/master

and imaplib2: http://www.janeelix.com/piers/python/imaplib2_pp.html

Oh, and prowlpy requires httplib2 which you can "python setup.py install". http://code.google.com/p/httplib2/

Feel free to improve it. I kept it running in a thread so it can be shut down. If you use the synchronous version of idle, it will block until activity on the account, which means you can't shut down the script cleanly. I don't think.

Also, you might want to change line 25 in prowlpy.py to just h = httplib2.Http() instead of using ".cache" because you'll get errors with longer descriptions. I haven't investigated why this is the case. It'll still work OK.

Here it is:

Code: Select all

import imaplib2
import time
import os
import prowlpy
import email
from threading import *

prowl_apikey = "APIKEYHERE"
IMAP_USERNAME = "USERNAME"
IMAP_PASSWORD = "PASSWORD"
 
# This is the threading object that does all the waiting on
# the event
class Idler(object):
    def __init__(self, conn):
        self.thread = Thread(target=self.idle)
        self.M = conn
        self.event = Event()
        self.last_notify = ""
 
    def start(self):
        self.thread.start()
 
    def stop(self):
        # This is a neat trick to make thread end. Took me a
        # while to figure that one out!
        self.event.set()
 
    def join(self):
        self.thread.join()
 
    def idle(self):
        # Starting an unending loop here
        while True:
            # This is part of the trick to make the loop stop
            # when the stop() command is given
            if self.event.isSet():
                return

            self.needsync = False

            # A callback method that gets called when a new
            # email arrives. Very basic, but that's good.
            def callback(args):
                if not self.event.isSet():
                    self.needsync = True
                    self.event.set()

            # Do the actual idle call. This returns immediately,
            # since it's asynchronous.
            self.M.idle(callback=callback)

            # This waits until the event is set. The event is
            # set by the callback, when the server 'answers'
            # the idle call and the callback function gets
            # called.
            self.event.wait()
            # Because the function sets the needsync variable,
            # this helps escape the loop without doing
            # anything if the stop() is called. Kinda neat
            # solution.
            if self.needsync:
                self.event.clear()
                self.dosync()
 
    # The method that gets called when a new email arrives.
    def dosync(self):
        print "Got an event!"
        retcode, messages = self.M.search(None, 'UNSEEN')
        if len(messages) >= 1 and len(messages[0].split()) >= 1:
            ret, msginfo = self.M.fetch(messages[0].split()[-1], '(BODY[HEADER.FIELDS (SUBJECT FROM)] BODY[1])')
            if ret == 'OK':
                header = msginfo[0][1].split('\r\n')
                sender = header[0]
                subject = header[1][9:]
                body = msginfo[1][1][:100]
                notify = sender+subject+body
                if notify != self.last_notify:
                    self.last_notify = notify
                    print "New email %s %s %s" % (sender,subject,body)
                    p = prowlpy.Prowl(prowl_apikey)
                    try:
                        p.post('Gmail Josh',subject,body)
                        print 'Posted to Prowl'
                    except Exception,msg:
                        print msg
                else:
                    print "Already notified"
 
idler = None
last_notify = ""
try:
    # loop forever, timing out faster than IMAP will time you out
    while True:
        # Set the following two lines to your creds and server
        M = imaplib2.IMAP4_SSL("imap.gmail.com")
        M.login(IMAP_USERNAME, IMAP_PASSWORD)

        # defaults to inbox
        M.select()

        # Start the Idler thread
        idler = Idler(M)
        idler.last_notify = last_notify
        print "Connected. Waiting on IDLE."
        idler.start()

        # sleep 20 minutes. Idle can time out in 30 minutes.
        time.sleep(20*60)
       
        # a hack to make sure you only get notified once per email
        last_notify = idler.last_notify
        idler.stop()
        idler.join()
        M.close()
        M.logout()
        idler = None
finally:
    # Clean up.
    if idler is not None:
        print "Shutting down"
        idler.stop()
        idler.join()
        M.close()
        M.logout()

joshua.menke
Harmless
Posts: 7
Joined: Wed Jul 08, 2009 3:49 pm

Re: Instant Gmail Prowl Script

Postby joshua.menke » Wed Jul 08, 2009 10:09 pm

I guess this is awfully complicated when you could just use Thunderbird:

http://cybernetnews.com/push-gmail-iphone/

But it's nice if you don't want to have Thunderbird running all the time.

User avatar
shauber
Harmless
Posts: 7
Joined: Fri Jul 10, 2009 1:08 pm
Contact:

Re: Instant Gmail Prowl Script

Postby shauber » Fri Jul 10, 2009 1:14 pm

I already run Mail.app on my media server Mac to keep an archive of my GMail, and just use the growlmail plugin for that. Works great, and I have a backup of my GMail account.

joshua.menke
Harmless
Posts: 7
Joined: Wed Jul 08, 2009 3:49 pm

Re: Instant Gmail Prowl Script

Postby joshua.menke » Fri Jul 10, 2009 3:33 pm

shauber wrote:I already run Mail.app on my media server Mac to keep an archive of my GMail, and just use the growlmail plugin for that. Works great, and I have a backup of my GMail account.


Cool. Unfortunately, I find myself Mac-less at this time. Although after owning an iPhone, I sorta want to try out Mac OS.

As for Thunderbird, I tried that approach and it was flakier than my python.

I wonder if the Thunderbird is being dropped by Gmail and not renewing the IMAP connection.

iphone4
Harmless
Posts: 8
Joined: Sun Jul 12, 2009 7:12 pm

Re: Instant Gmail Prowl Script

Postby iphone4 » Sun Jul 12, 2009 7:17 pm

Working well for me.

However it does notify my iPhone with messages like:
Gmail%20Josh%20iphone4%20%3Ciphone4%40goo
Message%0D%0A%0D%0A2009/7/12%20iphone4

Any way of cleaning up the message by avoiding the escaping of space into %20, etc ?

joshua.menke
Harmless
Posts: 7
Joined: Wed Jul 08, 2009 3:49 pm

Re: Instant Gmail Prowl Script

Postby joshua.menke » Sun Jul 12, 2009 10:43 pm

iphone4 wrote:Working well for me.

However it does notify my iPhone with messages like:
Gmail%20Josh%20iphone4%20%3Ciphone4%40goo
Message%0D%0A%0D%0A2009/7/12%20iphone4

Any way of cleaning up the message by avoiding the escaping of space into %20, etc ?


I'll check it out.

Probably something to do with the urllib stuff in prowlpy.

xiechaos
Harmless
Posts: 2
Joined: Mon Jul 13, 2009 8:27 am

Re: Instant Gmail Prowl Script

Postby xiechaos » Mon Jul 13, 2009 8:32 am

iphone4 wrote:Working well for me.

However it does notify my iPhone with messages like:
Gmail%20Josh%20iphone4%20%3Ciphone4%40goo
Message%0D%0A%0D%0A2009/7/12%20iphone4

Any way of cleaning up the message by avoiding the escaping of space into %20, etc ?


commenting out line line 35 to 38 seems solves this problem for me:
35 #application = urllib.quote(str(application))
36 #event = urllib.quote(str(event))
37 #description = urllib.quote(str(description))
38 #priority = urllib.quote(str(priority))

chrisns
Harmless
Posts: 1
Joined: Mon Jul 13, 2009 9:40 am

Re: Instant Gmail Prowl Script

Postby chrisns » Mon Jul 13, 2009 10:00 am

Awesome thanks for this!
Works a treat!
Took me a while to realize that it wasn't working because I was copy/pasting from firefox (3.5), did it in Safari and it worked fine, firefox screwed the tabs and some of the linebreaks.
Thanks again!
~c

iphone4
Harmless
Posts: 8
Joined: Sun Jul 12, 2009 7:12 pm

Re: Instant Gmail Prowl Script

Postby iphone4 » Mon Jul 13, 2009 5:00 pm

The %20 style escaping of messages is fixed with those lines commented out (were they needed for any other reason?)

I do find it fails after a couple of hours. I get an error:
sem_init: Resource temporarily unavailable
Shutting down

Here is a longer log. Any ideas?


Connected. Waiting on IDLE.
Got an event!
New email Subject: testing testing testing testing testing testing testing testing testing testing testing testing 123
abc
123
ABC
123
XYZ
123
xyz

Posted to Prowl
Got an event!
Already notified
Got an event!
Already notified
Got an event!
Already notified
Connected. Waiting on IDLE.
Got an event!
Already notified
Got an event!
Already notified
Got an event!
Already notified
sem_init: Resource temporarily unavailable
Shutting down
Traceback (most recent call last):
File "gmail.py", line 124, in <module>
M.close()
File "/cygdrive/c/Users/Dom/Desktop/Growl/python/imaplib2.py", line 562, in close
raise self.error('No mailbox selected.')
imaplib2.error: No mailbox selected.

joshua.menke
Harmless
Posts: 7
Joined: Wed Jul 08, 2009 3:49 pm

Re: Instant Gmail Prowl Script

Postby joshua.menke » Mon Jul 13, 2009 5:04 pm

Hmm. I'm just shooting from the hip here since I haven't dug into the real reasons.

But it could be that it's calling close() on something that wasn't really opened because of some other problem.

A work around could be to call M.select() before that M.close(). Or, error control the close().

I don't know if I'll be looking at this anytime soon.

The %20 escaping is since it gets used in a URL, but might not be necessary.

I haven't looked closely enough, but I wonder if it's possible to exploit some security loophole without url encoding it.

iphone4 wrote:The %20 style escaping of messages is fixed with those lines commented out (were they needed for any other reason?)

I do find it fails after a couple of hours. I get an error:
sem_init: Resource temporarily unavailable
Shutting down

Here is a longer log. Any ideas?


Connected. Waiting on IDLE.
Got an event!
New email Subject: testing testing testing testing testing testing testing testing testing testing testing testing 123
abc
123
ABC
123
XYZ
123
xyz

Posted to Prowl
Got an event!
Already notified
Got an event!
Already notified
Got an event!
Already notified
Connected. Waiting on IDLE.
Got an event!
Already notified
Got an event!
Already notified
Got an event!
Already notified
sem_init: Resource temporarily unavailable
Shutting down
Traceback (most recent call last):
File "gmail.py", line 124, in <module>
M.close()
File "/cygdrive/c/Users/Dom/Desktop/Growl/python/imaplib2.py", line 562, in close
raise self.error('No mailbox selected.')
imaplib2.error: No mailbox selected.

crimsontwo
Harmless
Posts: 1
Joined: Tue Jul 14, 2009 5:27 pm

Re: Instant Gmail Prowl Script

Postby crimsontwo » Tue Jul 14, 2009 6:06 pm

Great script, works just fine except for not properly displaying e-mail subj./from line. Also, if there are UTF-8 characters in either of the fields, they come up all scrambled (%).
Last edited by crimsontwo on Thu Jul 16, 2009 2:50 pm, edited 1 time in total.

t00fan
Harmless
Posts: 1
Joined: Thu Jul 16, 2009 2:05 pm

Re: Instant Gmail Prowl Script

Postby t00fan » Thu Jul 16, 2009 2:10 pm

Hi,

Thanks for the great script. However i keep getting "a float is required" error message on screen when it parses the new message. It will "Get an Event!" print out the email, but right after the subject line, instead of forwarding it on to Prowl, it will give me that error.

Any help?

Thanks,

joshua.menke
Harmless
Posts: 7
Joined: Wed Jul 08, 2009 3:49 pm

Re: Instant Gmail Prowl Script

Postby joshua.menke » Thu Jul 16, 2009 4:22 pm

I'll try to look into all this stuff.

Sorry I'm not super"actively" maintaining the script.

Any of you are welcome to tinker with it.

The display problems probably stem from me maybe not asking for the right fields from the message encoding. Either that or the urllib encoding in prowlpy. I haven't checked if prowlpy has been updated either.

The close errors are probably from me not dealing right with google's non-standard(?) IMAP set up.

But it does run in an infinite loop---you don't have to restart it every 30 minutes.

iphone4
Harmless
Posts: 8
Joined: Sun Jul 12, 2009 7:12 pm

Re: Instant Gmail Prowl Script

Postby iphone4 » Fri Jul 17, 2009 5:57 pm

I'm still getting the sem_init crashes. Normally within half an hour. Once it hit this immediately after launching. I'm using Python 2.5.2, with Windows Vista.
I tried adding a M.select() before the M.close(), but it had no effect. Here is another traceback with more levels of call stack in.

Got an event!
Got an event!
Got an event!
sem_init: Resource temporarily unavailable
Exception in thread Thread-8:
Traceback (most recent call last):
File "/usr/lib/python2.5/threading.py", line 486, in __bootstrap_inner
self.run()
File "/usr/lib/python2.5/threading.py", line 446, in run
self.__target(*self.__args, **self.__kwargs)
File "gmail.py", line 51, in idle
self.M.idle(callback=callback)
File "/cygdrive/c/Users/Dom/Desktop/Growl/python/imaplib2.py", line 682, in idle
return self._simple_command(name, **kw)
File "/cygdrive/c/Users/Dom/Desktop/Growl/python/imaplib2.py", line 1349, in _simple_command
rqb = self._command(name, callback=self._command_completer, *args)
File "/cygdrive/c/Users/Dom/Desktop/Growl/python/imaplib2.py", line 1103, in _command
crqb = self._request_push(tag='continuation')
File "/cygdrive/c/Users/Dom/Desktop/Growl/python/imaplib2.py", line 1337, in _request_push
rqb = Request(self, name=name, **kw)
File "/cygdrive/c/Users/Dom/Desktop/Growl/python/imaplib2.py", line 132, in __init__
self.ready = threading.Event()
File "/usr/lib/python2.5/threading.py", line 335, in Event
return _Event(*args, **kwargs)
File "/usr/lib/python2.5/threading.py", line 343, in __init__
self.__cond = Condition(Lock())
error: can't allocate lock

iphone4
Harmless
Posts: 8
Joined: Sun Jul 12, 2009 7:12 pm

Re: Instant Gmail Prowl Script

Postby iphone4 » Mon Jul 20, 2009 3:21 pm

Noticed that if there is a message with an attachment you get this sort of data sent in the message field:

--0016364c7725d0fc32046f23f26b
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encod

joshua.menke
Harmless
Posts: 7
Joined: Wed Jul 08, 2009 3:49 pm

Re: Instant Gmail Prowl Script

Postby joshua.menke » Mon Jul 20, 2009 5:41 pm

iphone4 wrote:Noticed that if there is a message with an attachment you get this sort of data sent in the message field:

--0016364c7725d0fc32046f23f26b
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encod


Yeah, I read more carefully through the IMAP RFC, but I'm wondering if it wouldn't be better to connect to Google Instead. It notifies just as fast.

I know this because if you use Pidgin (pidgin.im), the PidginSnarl plugin, and have Snarl forward to your iPhone, you get email notifies almost as fast as my script. The difference is maybe a second or 2.

I'm not sure how stable Pidgin with Snarl is though.

kjconrad
Muffin
Posts: 30
Joined: Sat Jul 18, 2009 5:41 am

Re: Instant Gmail Prowl Script

Postby kjconrad » Wed Jul 22, 2009 8:04 pm

I was also having problem with this script erroring out but have found a work around until the script can be stabilized (or gmail adds native push support).

I made a this simple shell script:

Code: Select all

#! /bin/sh
cd ~/googlenotifications/
python gmailnotification.py
./gmail.command


The shell script is named "gmail.command" which you will want to alter to point to the right directory and chmod +x. It will start the gmailnotification.py and after it fails relaunch itself. A handy infinite loop incase the other infinite loop errors out :)

Also made one for the google voice script since I was also having problems with it erroring out.

w1ngnutt
Harmless
Posts: 1
Joined: Tue Aug 25, 2009 2:17 pm

Re: Instant Gmail Prowl Script

Postby w1ngnutt » Tue Aug 25, 2009 2:23 pm

t00fan wrote:Hi,

Thanks for the great script. However i keep getting "a float is required" error message on screen when it parses the new message. It will "Get an Event!" print out the email, but right after the subject line, instead of forwarding it on to Prowl, it will give me that error.

Any help?

Thanks,


The issue exception message you are seeing of "a float is required" is due to an incompatibility problem of httplib2 with Python 2.6. See the comments at the bottom of the following page: http://code.google.com/p/httplib2/wiki/Examples

chriscannon
Harmless
Posts: 12
Joined: Sun Sep 20, 2009 6:22 pm

Re: Instant Gmail Prowl Script

Postby chriscannon » Sat Sep 26, 2009 9:28 pm

In my opinion this script is overly complex and not stable in the least. So much in fact that I've created my own script.

It's called Gprowl and you can download it here: https://sourceforge.net/projects/gprowl/

It's one file, stable, simple, well commented and documented, and only requires OpenSSL. This is under active development and new features will be regularly added.

Thanks,
Chris


Return to “Prowl”

Who is online

Users browsing this forum: No registered users