Command-line utility for Google Tasks
It is my first post in English here. I've recently decided to translate a part of my posts into English because I want to share my experience.
I'm used to work with PIM. I like Google Calendar as well as Google Tasks which has been recently released. I use these services because of great synchronization options: I can sync all my mobile devices and laptops.
So, there is a problem: display Google Tasks directly in my console (e.g. it is necessary to display tasks on my desktop where Google Calendar is already being displayed). But how can I do it without Google Tasks API? There is no API — we can emulate an internet browser.
Firstly, it is needful to choose right page version of Google Tasks among a great number of different pages (for mobile devices, iGoogle, Gmail etc). I've chosen a mobile version because it is simple and an specific task list can be retrieved:
- https://mail.google.com/tasks/m for users of Google Accounts
- https://mail.google.com/tasks/a/domain.com/m for Google Apps users
Initially I've wanted to use bash scripting. But it was very challenging and I've decided to write a python script (I suppose every Mac and Linux has python).
At last I wrote the script which will be commented below (the script is also available for download:google_tasks.py):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 | #!/usr/bin/env python """ This is a python script for retrieving Google Tasks Usage: -e or --email [email] Your Google Account or Google Apps email -p or --password [password] Your password -b or --bullet [bullet] Bullet for tasks list. Default is '*' Example: google_tasks.py -e bob@gmail.com -p yaroslavl -b -- Thank you Scott Hillman for the implementation of Google Authentication http://everydayscripting.blogspot.com/2009/10/python-fixes-to-google-login-script.html Evgeny Pavlov, http://evgeny.tel """ import urllib import urllib2 import htmllib import getpass import re import sys import getopt def unescape(text): """Removes HTML or XML character references and entities from a text string From Fredrik Lundh http://effbot.org/zone/re-sub.htm#unescape-html Little bit modified """ def fixup(m): text = m.group(0) if text[:2] == "&#": # Character reference try: if text[:3] == "&#x": return unichr(int(text[3:-1], 16)) else: return unichr(int(text[2:-1])) except ValueError: print "Error with encoding HTML entities" pass else: # Named entity text = unichr(htmlentitydefs.name2codepoint[text[1:-1]]) return text # leave as is return re.sub("&#?\w+;", fixup, text) def main(argv): """ Get arguments: email, password and type of bullet """ bullet = '* ' email = '' password = '' try: opts, args = getopt.getopt(argv, "he:p:b:", ["help", "email=", "password=", "bullet="]) except getopt.GetoptError: usage() sys.exit(2) for opt, arg in opts: if opt in ("-h", "--help"): usage() sys.exit() elif opt in ("-e", "--email"): email = arg elif opt in ("-p", "--password"): password = arg elif opt in ("-b", "--bullet"): bullet = arg return (email, password, bullet) def usage(): """ Help and the list of arguments for this script """ print """This is a python script for retrieving Google Tasks Usage: -e or --email [email] Your Google Account or Google Apps email -p or --password [password] Your password -b or --bullet [bullet] Bullet for tasks list. Default is '*' Example: google_tasks.py -e bob@gmail.com -p yaroslavl -b -- Evgeny Pavlov, http://evgeny.tel""" if __name__ == "__main__": # Arguments (email, password, bullet) = main(sys.argv[1:]) # Google Account or Google Apps email_split = email.split('@') try: email_domain = email_split[1] except: print 'Incorrect email address!\n' usage() sys.exit() if email_domain in ('googlemail.com', 'gmail.com', 'google.com'): google_apps = 0 else: google_apps = 1 email = email_split[0] # Initialization opener = urllib2.build_opener(urllib2.HTTPCookieProcessor()) urllib2.install_opener(opener) # Define URLs if google_apps: login_page_url = 'https://www.google.com/a/%s/ServiceLogin' % email_domain auth_url = 'https://www.google.com/a/%s/LoginAction2' % email_domain tasks_url = 'https://mail.google.com/tasks/a/%s/m' % email_domain else: login_page_url = 'https://www.google.com/accounts/ServiceLogin' auth_url = 'https://www.google.com/accounts/ServiceLoginAuth' tasks_url = 'https://mail.google.com/tasks/m' # 1. Load login page login_page_content = opener.open(login_page_url).read() # Find GALX value galx_match_obj = re.search(r'name="GALX"\s*value="([^"]+)"', login_page_content, re.IGNORECASE) galx = galx_match_obj.group(1) if galx_match_obj.group(1) is not None else '' # Set up login credentials login_params = urllib.urlencode( { 'Email' : email, 'Passwd' : password, 'continue' : tasks_url, 'GALX': galx }) # 2. Login opener.open(auth_url, login_params) # 3. Open Tasks home page tasks_content = opener.open(tasks_url).read() # Check signing in key = re.search('create_tasks', tasks_content) if not key: print 'Check your credintals!' sys.exit() # Retrieve list ids tasks_content_split_obj = re.search(r'<select(.*?)select>', tasks_content, re.IGNORECASE) tasks_content_split = tasks_content_split_obj.group(1) listids = re.findall(r'[0-9]{20}:[0-9]:[0-9]', tasks_content_split) # 4. Fetch all lists for listid in listids: # List content list_content = opener.open(tasks_url + "?listid=%s" % listid).read() # Only tasks remain list_content_split_obj = re.search('(.*?)name="numa"', list_content, re.IGNORECASE | re.DOTALL) list_content_split = list_content_split_obj.group(1) # Get Tasks in <tr></tr> tasks_in_tr = re.findall(r' <tr(.*?)tr>', list_content_split, re.IGNORECASE | re.DOTALL) # Work with this dirty tasks for task_in_tr in tasks_in_tr: # Retrieve task task_obj = re.search(r' <td class="text">(.*?)</td> ', task_in_tr, re.IGNORECASE) task = task_obj.group(1) # Indent indent = len(re.findall(r' <td class="checkbox"', task_in_tr)) - 1 # HTML entities task = unescape(task) task = task.strip() # 5. At last output if task != '': print ' ' * indent, bullet, task |
The script dispays Google tasks.
But how does it work? It's simple: in order to get through Google Authentication, email and password are needed. Every time during signing in Google checks a special variable GALX. So,
- 31-56, function unescape (). It is necessary for converting HTML entities which exist in the mobile version of Google Tasks. Transform #&123; into normal symbols. Python script on W3C.
- 59-80, in function main () arguments are handled. Come across Dive Into Python.
- 106-118, checking account type — Google Accounts or Google Apps. It is very simple — just matching domain of email.
- 134-151, Google authentication with this GALX. Furthermore, we need to use cookies. The idea of Every Day Scripting blog. I could also realize this with wget.
- Next HTML is being parsed and analysed to find tasks.
If you want to start the script, put it in some place (/usr/bin or another), make it executable (chown +x) and run with following keys:
google_tasks.py -e bob@gmail.com -p yaroslavl
Futhermore, a decoration of bullets can be chanched with key -b: it is asterisk by default.
Коммент.(5)