Google Tasks interface case study

By William Entriken

2 minutes

Here is the API for Google Tasks. It only supports your default list and it’s read-only. And for free, it emails your task list to you. (This mail WILL go to your spam folder)

#!/bin/bash
cd $HOME
curl 

curl <https://www.google.com/accounts/ClientLogin> \
-d Email=<YOURUSERNAME@gmail.com> \
-d Passwd=YOURPASS \
-d source=privacylog \
-d service=goanna_mobile > token

AUTH=$(sed -n 's/Auth=/auth=/p' token)
HEADER="Authorization: GoogleLogin $AUTH"
URL="<https://mail.google.com/tasks/ig>"
curl --header "$HEADER" "$URL" > tasks

grep -o "\"\(name\|task_date\|notes\)\":\"[^\"]*\"" tasks > list

mail -s "Your tasks" <YOURUSERNAME@gmail.com> > list

rm token tasks list

If you want to have that to run daily at 7am, save that script to ~/googletasks.sh and run:

chmod 700 ~/googletasks.sh
crontab -l > ~/tmpcron
echo "0 7 * * * $HOME/googletasks.sh" >> ~/tmpcron
crontab ~/tmpcron
rm ~/tmpcron

There is no official API for Google Tasks at this time. But, below is full read-only access to all your tasks across all your lists. I’m including a simple app that sends this to you via email (This mail WILL go to your spam folder). I have this running at home to automatically print out a copy of all task lists periodically.

#!/bin/bash

cd /tmp
rm -f message; touch message

curl "https://www.google.com/accounts/ClientLogin" \
-d Email=<YOURUSERNAME@gmail.com> \
-d Passwd=YOURPASSWORD \
-d source=privacylog \
-d service=goanna_mobile > token

AUTH=$(sed -n '/Au/s/A/a/p' token)
HEADER="Authorization: GoogleLogin $AUTH"
URL="https://mail.google.com/tasks/m"
curl --header "$HEADER" "$URL" > main

for list in $(grep -o '"[0-9:]{20,25}:0"' main | tr -d '"' | sort -u)
do
    curl --header "$HEADER" "$URL?listid=$list" > list
    echo >> message
    title=$(sed -n 's|.*selected="selected">&([^<]\+).*|\1|gp' list | head -n1)
    echo "### $title ###" >> message
    sed -n 's|([^<]{1,}) .*|[  ] \1|p' list >> message

    # This one below indents sub tasks but only works on Linux
    # sed -n -e 's/.*&nbsp;.*//p' -e 's|([^<]+).*|\1|p' list | sed -n -e '/./!{N;s/\n/  /}' -e '/./p' | sed 's/^/[ ]  /' >> message
done

mail -s "Weekly review" <YOURUSERNAME@gmail.com> < message

rm token main list message

Notes for Mac: use * instead of +.

Comments

Do you I website where I can paste source code and it spits out color coded source code. Then I can view source and use that HTML in Blogger?

William Entriken

Hi, could you help me with what "/Au" does in sed?

Unknown

Original line of code: AUTH=$(sed -n '/Au/s/A/a/p' token)

Sure, since sed is in -n (non-print mode), this matches lines with /Au/ and then /p (prints) them, as opposed to printing every line with an A in it.

Of course a more straightforward approach is just s/Auth/auth/p which does the same thing. I'll put that in the post.

William Entriken

Is there any way to access https://mail.google.com/tasks/ig using oAuth or any other method that does not require asking the user to provide their password?

Dmitry Panov

Nope, we're all waiting for that. This post of course is just a workaround. Feel free to vote on that feature at https://code.google.com/p/gdata-issues/issues/detail?id=987

William Entriken

Link is now: https://code.google.com/a/google.com/p/apps-api-issues/issues/detail?id=985

Anonymous

Hi.
I have implemented this code in Java and when I run it no Android simulator I get:
P3P: CP="This is not a P3P policy! See https://www.google.com/support/accounts/bin/answer.py?hl=en&answer=151657

Can you advise please?

Anonymous

Hi Anonymous. Is this Java code available for inspection?

William Entriken

URI uri = new URI("https://www.google.com/accounts/ClientLogin?Email=user@gmail.com&Passwd=pass&source=privacylog&service=goanna_mobile");
HttpPost post = new HttpPost(uri);
HttpClient client = new DefaultHttpClient();
HttpResponse response1 = client.execute(post);

InputStreamReader isr = new InputStreamReader(response1.getEntity().getContent(),"UTF-8");

char buff[] = new char[ (int) response1.getEntity().getContentLength()];
int n;
StringBuffer sb = new StringBuffer();
while ((n = isr.read(buff, 0, (int)response1.getEntity().getContentLength() - 1)) > 0) {
sb.append(buff, 0, n);
}
String a = sb.toString();
String arr[] = a.split("
");
String auth[] = arr[2].split("=");
isr.close();

URI uri1 = new URI("https://mail.google.com/tasks/ig");
HttpGet post1 = new HttpGet(uri1);
post1.addHeader("Authorization","GoogleLogin auth=" + auth[1].trim());
HttpResponse response = client.execute(post1);

isr = new InputStreamReader(response.getEntity().getContent(),"UTF-8");
buff = new char[ (int) response.getEntity().getContentLength()];
sb = new StringBuffer();
while ((n = isr.read(buff, 0, (int)response.getEntity().getContentLength() - 1)) > 0) {
sb.append(buff, 0, n);
}
a = sb.toString(); //as String is easy ... but with
isr.close();


In response header i get the above message.

Anonymous

I believe this is an implementation-specific problem. See https://groovy.329449.n5.nabble.com/Httpbuider-issues-td396109.html

I couldn't find an Android Simulator out there, but low level problems like this might be investigated with ethereal.

It doen't affect you, but there's a new version of this post at https://privacylog.blogspot.com/2010/09/updated-google-tasks-api.html

William Entriken

So now when you wake up, the coffee is already brewed and the task list is automatically printed. Put a wireless printer in the kitchen. No, I'm serious.

William Entriken

Use these lines instead because they are cross platform (sed parses escape sequences differently on Mac and Linux):
title=$(perl -ne 'die "$1\n" for m/"selected">([^<]+).*/' list 2>&1)
perl -ne 'print "[ ] $1\n" for m/"text">([^<]+)/' list >> message

William Entriken

A quick python rewrite: https://pastebin.ca/1994952

fab31

Great job fab31! here is full text for posterity:
A quick python rewrite, can be a start to clean up & improve...

```python
#!/usr/bin/env python
YOURUSENAME = raw_input('login:')
YOURPASSWORD = raw_input('password:')

import re
from urllib import urlencode
import urllib2 as urllib

login = urllib.urlopen('https://www.google.com/accounts/ClientLogin', data=urlencode({
'Email' : YOURUSENAME+'@gmail.com' ,
'Passwd' : YOURPASSWORD ,
'source' : 'privacylog' ,
'service': 'goanna_mobile' ,
}))

token = login.read()
AUTH = (line.split('=', 1)[1] for line in token.split('\n') if line.startswith('Auth=')).next()
HEADER = {'Authorization': 'GoogleLogin auth='+AUTH}
URL = "https://mail.google.com/tasks/m"
main = urllib.urlopen(urllib.Request(URL, headers=HEADER))

data = main.read()
file('/tmp/out.html', 'w').write(data)

r = re.compile('.*"([0-9:]{20,25}:0)"')
r2 = re.compile('selected="selected">([^<]+).*')
#r3 = re.compile('(.*)')
r3 = re.compile('(?ms) *(.*?) *$')

for line in data.split('\n'):
m = r.match(line)
if m:
list_id = m.groups()[0]
site = urllib.urlopen(urllib.Request(URL+"?listid="+list_id, headers=HEADER))
content = site.read()
title = r2.findall(content)[0]
tasks = r3.findall(content)
print title.center(80)
for t in tasks:
if t:
print t
open('/tmp/tid_'+list_id, 'w').write(content)
```

William Entriken

Please discuss this topic anywhere and let me know any great comments or media coverage I should link here.