papilio

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.

Метки: , , , ,

(Russian) Статистика подкастов

Sorry, this entry is only available in Russian.

Метки: , , , , , , ,

(Russian) Как скачать полностью блог

Sorry, this entry is only available in Russian.

Метки: , , , ,

(Russian) CloudBerry Explorer — работать с Amazon S3 стало проще

Sorry, this entry is only available in Russian.

Метки: , , , , ,

(Russian) Включение синхронизации с мобильными устройствами для Google Apps

Sorry, this entry is only available in Russian.

Метки: , , , , , , ,

(Russian) Объединение контактов Google

Sorry, this entry is only available in Russian.

Метки: , , , , , , ,

(Russian) Управление компьютером прямо в браузере

Sorry, this entry is only available in Russian.

Метки: , , , , , , , ,

(Russian) Flex Builder бесплатно для студентов

Sorry, this entry is only available in Russian.

Метки: , , , , , , , , , ,

(Russian) Типограф для WordPress

Sorry, this entry is only available in Russian.

Метки: , , , , , , ,

(Russian) Google Short Links

Sorry, this entry is only available in Russian.

Метки: , , , , , , , , , ,