Using GAE Cron to get SMS Alerts from Gmail

» 06 February 2011 » In Guides, Internet, Programming, Python »

Who doesn’t love the free email service Gmail? I am currently using Google Apps Mail for this domain, and I got it installed in my computer, so I just have to switch windows to check messages. Most of the emails I am receiving is crucial, especially from Scriptlance. And when I am offline (either I am sleeping or I am not home), I don’t know if an email arrives, or if I should be online to respond to that email. So the solution for this? SMS alert.

I’m pretty sure there are lots of services that offer email alerts to your phone, but chances are, they are not free, and we are not sure if the login information is kept or used in something we do not like.

For this to work, there are some requirements:

  • SMS Gateway
  • Google App Engine Account
  • Google App Engine SDK (and Python 2.5 of course)
  • Your Gmail or Google Apps Mail credentials

The SMS gateway is not free in most cases, but you can use if a friend has one, just make sure it works with your carrier.

The first thing you need to do is create a GAE Application. Name it whatever you like, and on the app.yaml, add these lines:

- url: /check/mail
  script: check.py
  login: admin

Of course you can changes the url and script, but I just like to use that in this guide. Next is to create cron.yaml, and put the following lines:

cron:
- description: Check Mail
  url: /check/mail
  schedule: every 1 minutes

You can change the schedule on how frequent you like your mail to be checked. For me, I chose to check every one minute (There’s a grammatical error on the yaml file, but ignore it, it’s correct according to the syntax). That means, every minute, the script will check for unread messages and sends me a SMS message if there is one.

Next is to create a script called check.py:

#!/usr/bin/env python
'''
	Copyright (c) Ruel Pagayon <http://ruel.me>
'''

from xml.dom.minidom import parseString
from google.appengine.ext import db
import urllib2
import urllib
import base64
import sys

'''
	Change the values below.
'''
gFeed = 'https://mail.google.com/mail/feed/atom'
#gFeed = 'https://mail.google.com/a/domian.tld/feed/atom'	#For Google Apps for Mail users.

gEmail = 'user@domain.tld'
gPass = 'pass'

class Uid(db.Model):
	cont = db.StringProperty()

def sendSMS(message):
	'''
		Insert appropriate code for your SMS gateway here.
		Obviously the one below doesn't work.
		It's there to give you an insight on how it will work.
	'''
	urllib2.urlopen('http://somegateway.com:1578/?user=smsuser&password=smspass&PhoneNumber=18005669874&Text=%s' % urllib.quote(message))
	
def getNewMail(feed):
	'''
		Check for new unread mails. Use basic auth.
	'''
	request = urllib2.Request(feed)
	base64string = base64.encodestring('&s:%s' % (gEmail, gPass)).replace('\n', '')
	request.add_header("Authorization", "Basic %s" % base64string)
	'''
		Parse XML using DOM
	'''
	xdata = urllib2.urlopen(request).read()
	dom = parseString(xdata)
	count = dom.getElementsByTagName('fullcount')[0].firstChild.data
	
	'''
		Retrieve last message ID from datastore
	'''
	ouid = Uid.all()
	
	if ouid.count() != 0:
		lastId = ouid[0].cont
		
	if int(count) > 0:
		'''
			Check if there are feed entries.
			Get the ID of the latest message.
		'''
		eid = dom.getElementsByTagName('entry')[0].getElementsByTagName('id')[0].firstChild.data
		'''
			Generate report.
		'''
		message = "You got " + count + " new email(s)\n\n"
		for entry in dom.getElementsByTagName('entry'):
			'''
				Loop through each email entry
			'''
			message += "Sender: %s (%s)\n" % (entry.getElementsByTagName('name')[0].firstChild.data, entry.getElementsByTagName('email')[0].firstChild.data)
			message += "Subject: %s\n\n" % entry.getElementsByTagName('title')[0].firstChild.data
			
		if eid != lastId:
			'''
				If the last ID from the data store, 
				is different from the latest message ID,
				update the data store and
				send the SMS
			'''
			if ouid.count() != 0:
				nuid = Uid.get(ouid[0].key())
			else:
				nuid = Uid()
			nuid.cont = eid
			nuid.put()
			sendSMS(message)
		'''
			You got 1 new email(s)
			
			Sender: Someone (someone@domain.tld)
			Subject: Example Message
		'''

def main():
	getNewMail(gFeed)

if __name__ == '__main__':
	main()

Change the values that suites your needs. And the docstrings on the script pretty much explains everything, so I don’t have to repeat it here.

Now, we all know that a free GAE app has quota, and on the script, we will be using the Data Store API and the UrlFetch API, let’s do some math. The quota will reset every 24 hours. So, if you decided to check the mail every 1 minute, we got 1440 calls. And it’s not even a quarter of the limit.

Now obviously we can use the script on our webservers, the only thing that we need to change is the data store calls (GAE doesn’t allow writing to disk, so Datastore is used). But the problem here is the credentials are in plain text, and there’s a huge risk for this information to be seen by other parties. So why not just use GAE? Where only you and Google can have access. :)

Tags: , , , , , ,

Trackback URL

  • Pingback: Tweets that mention Using GAE Cron to get SMS Alerts from Gmail -- Topsy.com

  • Anonymous

    Or, you could just have your phone connect to google as an exchange server and have google push email to your phone. the server is m.google.com http://www.google.com/support/mobile/bin/answer.py?hl=en&answer=138636

    • http://ruel.me Ruel

      Sadly, not all people have smartphones. I myself don’t have one. And my carrier charge me whenever I access the internet.

      • Anonymous

        Then setup gmail to forward the incoming mail to your phone as an SMS basically, your number in the form of an email address sent to your phone company’s sms gateway http://en.wikipedia.org/wiki/List_of_SMS_gateways

        like 4445551234@smsgateway.com

        The message would be truncated of course, but you’d know it came in.

        • http://ruel.me Ruel

          Sadly, not all carriers provide this kind of service. Like in my country, not even a single carrier can provide that. :)

  • http://www.epro.co.uk Adwords advice – James

    Google Calendar supports SMS notification. Couldn’t you do something like create a meeting for 1hr1mins ahead with an alert “an hour before” containing the email notification? I’ve never used the Calendar API so have no idea if it’s possible to set this kind of notification but the Google calendar SMS are free here in the UK so perhaps that’s a neat _free_ solution?

    • http://ruel.me Ruel

      That’s possible. :)

  • Saiyine

    Being the mail from google, shouldn’t you use the authentication API?

    • http://ruel.me Ruel

      Using the authentication API is the same with what I used. I mean the password will be supplied in the script and in plain text. Plus, it will be more complicated, rather than just parsing an atom feed. :)

  • Rabih

    import atom
    import gdata.calendar
    import gdata.calendar.service
    import time
    from google.appengine.ext import webapp

    TIME_FORMAT = ‘%Y-%m-%dT%H:%M:%S.000Z’
    CALENDAR_URL = ‘http://www.google.com/calendar/feeds/default/private/full’

    class GoogleSms:
    def __init__(self, username, password):
    self.username = username
    self.password = password
    service = gdata.calendar.service.CalendarService()
    service.email = username
    service.password = password
    service.source = ‘GoogleSms’
    service.ProgrammaticLogin()
    self.calendar_service = service
    def send(self, message):
    # Set time to one hour from now
    event_time = time.strftime(TIME_FORMAT, time.gmtime(time.time()+3600))
    event = gdata.calendar.CalendarEventEntry()
    event.title = atom.Title(text=message)
    event.content = atom.Content(text=message)
    # Send a reminder 60 minutes before the event.
    # Since the event is 60 minutes from now, we will receive the message
    # in a few seconds.
    reminder = gdata.calendar.Reminder(minutes=60)
    reminder.method = ‘sms’
    when = gdata.calendar.When(event_time)
    when.reminder.append(reminder)
    event.when.append(when)
    try:
    # Add the entry to calendar
    cal_event = self.calendar_service.InsertEvent(event, CALENDAR_URL)
    except gdata.service.RequestError, request_exception:
    raise

    • Rabih

      Sorry for the indentation, I just pasted the code and hit ‘Post’ without looking ;)

  • http://www.isadmr.com isa Demir

    Hello, firstly thanks for this great post. But I’ve a problem.
    Can you give me some idea for fixing this:
    Thanks

    0.1.0.1 – - [04/Jun/2011:11:18:15 -0700] “GET /check/mail HTTP/1.1″ 500 0 – “AppEngine-Google; (+http://code.google.com/appengine)” ms=242 cpu_ms=23 api_cpu_ms=0 cpm_usd=0.000675 queue_name=__cron task_name=e832628d83612899d0028ebfed3a7bee loading_request=1E 2011-06-04 11:18:15.791
    : not all arguments converted during string formattingTraceback (most recent call last): File “/base/data/home/apps/gmail-sms-notify/1.350901774893126187/check.py”, line 97, in main() File “/base/data/home/apps/gmail-sms-notify/1.350901774893126187/check.py”, line 94, in main getNewMail(gFeed) File “/base/data/home/apps/gmail-sms-notify/1.350901774893126187/check.py”, line 38, in getNewMail base64string = base64.encodestring(‘&s:%s’ % (gEmail, gPass)).replace(‘n’, ”)

  • http://twitter.com/OlegT_ Олег

     Here is a way of obtaining SMS alert from gmail.com using Google Calendar and GAE on Python
    http://gmail2sms.blogspot.com/2012/03/how-to-get-sms-notification-of-new-mail.html