Google Chat History Downloader

Update 2011-11-09:
Gmail now officially supports downloading chat history via IMAP. Thank’s to Steve for pointing it out. It can be enabled in the “Labels” section of Gmail settings.

Update 2011-08-30:

Based on the comments, this doesn’t work anymore. I’d recommend checking out this thread for solutions:

I personally have been using a javascript-based solution for exporting recent chat data, which still doesn’t solve the TOS / getting blocked problem. If there is enough interest, I’ll post my code.

A couple weeks ago, I decided to migrate from one Google Account to another. I was able to transfer all of my emails from one to the other without too much difficulty. However, I looked around for a while and have not found any way to export all of my Google Talk Chat history. I don’t think there is any way to access saved chats from either IMAP or POP. I did notice though, that through the Gmail web interface, you can view saved chats as a raw message. There happens to be an old python library for interacting with the Gmail web interface called libgmail. I found however that it does not scale very well to large amounts of messages, so I had to write my own method to only process results one page at a time. Also, I found that I was easily blocked using this method over a long time, so I added 13 second delays after every request so as not to get my account suspended. It took me a day and a half to actually export all of the messages. I’m not sure if this is over kill or not, but I am tired of getting my account blocked.

Anyway, This program goes through and saves each chat history message as an .eml file. One they are in that format, it is not super hard to get them into a different Gmail account, but I’ll save that for another post.

import os
import time
import libgmail #

def thread_search(ga, searchType, **kwargs):
    index = 0
    while (index == 0) or index < threadListSummary[libgmail.TS_TOTAL]:
            threadsInfo = []
            items = ga._parseSearchResult(searchType, index, **kwargs)
                threads = items[libgmail.D_THREAD]
            except KeyError:
                for th in threads:
                    if not type(th[0]) is libgmail.types.ListType:
                        th = [th]
                threadListSummary = items[libgmail.D_THREADLIST_SUMMARY][0]
                threadsPerPage = threadListSummary[libgmail.TS_NUM]
                index += threadsPerPage
            yield libgmail.GmailSearchResult(ga, (searchType, kwargs), threadsInfo)

ga = libgmail.GmailAccount("", "password")

for page in thread_search(ga, "query", q="is:chat"):
    print "New Page"
    for thread in page:
        if[0] ==[10]:
            # Common case: Chats that only span one message
            filename = "chats/%s_%s.eml" % (,
            #only download the message if we don't have it already
            if os.path.exists(filename):
                print "already have %s" % filename
            print "Downloading raw message: %s" % filename,
            message = ga.getRawMessage('utf-8').lstrip()
            print "done."
            file(filename, 'wb').write(message)
        # Less common case: A thread that has multiple messages
        print "Looking up messages in thread %s" %
        for message in thread:
            filename = "chats/%s_%s.eml" % (,
            #only download the message if we don't have it already
            if os.path.exists(filename):
                print "already have %s" % filename
            print "Downloading raw message: %s" % filename,
            file(filename, 'wb').write(message.source.lstrip())
            print "done."


This one generates kml files.

#based on

import urllib
import xml.dom.minidom

# Sign up for one here:

def geocode(address):
    # This function queries the Google Maps API geocoder with an
    # address. It gets back a csv file, which it then parses and
    # returns a string with the longitude and latitude of the address.

    mapsKey = MAPS_KEY
    mapsUrl = ''
    url = ''.join([mapsUrl,urllib.quote(address),'&output=csv&key=',mapsKey])

    print "Looking up %s..." % address,
    coordinates = urllib.urlopen(url).read().split(',')
    print coordinates
    coorText = '%s,%s' % (coordinates[3],coordinates[2])
    if coorText == '0,0':
        print "trying again in 2 seconds."
        import time
        print "Looking up %s..." % address,
        coordinates = urllib.urlopen(url).read().split(',')
        print coordinates
        coorText = '%s,%s' % (coordinates[3],coordinates[2])        
    return coorText

class KML():
    def __init__(self, name=None, description=None):
        self.kmlDoc = xml.dom.minidom.Document()

        kmlElement = self.kmlDoc.createElementNS('','kml')

        kmlElement = self.kmlDoc.appendChild(kmlElement)

        documentElement = self.kmlDoc.createElement('Document')
        self.documentElement = kmlElement.appendChild(documentElement)

        if name:
            nameElement = self.kmlDoc.createElement('name')

        if description:
            descriptionElement = self.kmlDoc.createElement('description')

    def add_placemark(self, name, description, address):
        placemarkElement = self.kmlDoc.createElement('Placemark')

        nameElement = self.kmlDoc.createElement('name')

        descriptionElement = self.kmlDoc.createElement('description')

        pointElement = self.kmlDoc.createElement('Point')

        coordinates = geocode(address)
        coorElement = self.kmlDoc.createElement('coordinates')


    def __str__(self):
        return self.kmlDoc.toprettyxml(' ')

def email(address):
    return '<a href="mailto:%s">%s</a>' % (address, address)

if __name__ == '__main__':
    k = KML()
    k.add_placemark('Google Headquarters', "1600 Amphitheatre Pkwy<br/>Mountain View, CA 94043<br/>", "1600 Amphitheatre Pkwy Mountain View, CA 94043")

    f = file('google.kml', 'w')

Python Flickr Uploader

After buying a Flickr Pro account at the beginning of the summer and uploading all of my photos, I wanted an automatic way of uploading my photos from my camera. I already had a Python script that renamed all of my photos to the date and time they were taken, so all I had to do was get a module that would upload them to Flickr. It turns out I had a bit of trouble doing that, and I had to modify someone else’s code to make it work, which was not very fun.

Anyway long story short, I recently rewrote most of the code from scratch for fun over break. What makes my Flickr Uploader Python library different from all of the other ones out there is that mine uses JSON instead of XML to talk to Flickr. I am most proud of the authentication code, but for some reason Flickr does not support JSON for file uploads, so I had to resort to XML. I also found out that Python does not have built in support for HTTP file uploads, so I had to copy code from somewhere else for that.

Download the code: (16.8 KB)