Tag Archives: python

IMAP Import

I used this to upload all of my old chats saved as .eml files directly into my new Gmail account. It uses IMAP’s append to do this. I had tried Thunderbird and the ImportExportTools plugin, but ran into trouble.

This puts all of the messages into a label called “oldchats”. This label needs to exist before this program is run. It also deletes each message after it is uploaded. I made a backup copy of my messages.

import email
import os
import time
import imaplib

imap = imaplib.IMAP4_SSL('imap.gmail.com', 993)
imap.login('email@gmail.com', 'password')

os.chdir("chats")
for filename in os.listdir("."):
    print filename,
    raw_eml = file(filename).read()
    msg = email.message_from_string(raw_eml)
    date = email.utils.parsedate(msg['Date'])
    print date,
    imap.append('oldchats', None, date, raw_eml)
    print "done"
    os.remove(filename)

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: http://www.google.com/support/forum/p/gmail/thread?tid=7a7d2d6da5be047f

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 # http://libgmail.sourceforge.net/

def thread_search(ga, searchType, **kwargs):
    index = 0
    while (index == 0) or index < threadListSummary[libgmail.TS_TOTAL]:
            threadsInfo = []
            items = ga._parseSearchResult(searchType, index, **kwargs)
            try:
                threads = items[libgmail.D_THREAD]
            except KeyError:
                break
            else:
                for th in threads:
                    if not type(th[0]) is libgmail.types.ListType:
                        th = [th]
                    threadsInfo.append(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("username@gmail.com", "password")
ga.login()

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

This one checks to make sure my Gmail Contacts’ names are spelt how the contact spells them.

It looks at emails sent from the contact, and compares the sender name with the name on file for that contact.

import libgmail # http://libgmail.sourceforge.net/

ga = libgmail.GmailAccount("email@gmail.com", "password")
ga.login()
all_contacts = ga.getContacts().getAllContacts()

def test_thread(thread, contact):
    for message in thread:
        if message.sender.lower() != contact.email.lower():
            continue
        if '@' in message.author_fullname:
            continue
        if message.author_fullname == contact.name:
            return True
        else:
            print "\t", message.author_fullname, "->", contact.name
            return True

for contact in all_contacts:
    results = ga.getMessagesByQuery("from: %s -is:chat" % contact.email)
    if not results:
        print "%s -------No Email" % contact.email
        continue
    for thread in results:
        result = test_thread(thread, contact)
        if result:
            break
    else:
        print "%s -------No Good Email" % contact.email

XMPP Jabber Photo Module

I was looking around for a XMPP vCard Photo Module in May of 2007, and could not find one, so I wrote my own. It uses the xmpppy python module for communicating with the jabber server. Once the connection is made with the server, call register_handler(session). It will then download avatars of people on the roster list. To get the filename of a person’s avatar, call get_photo(photo_hash).

import base64
import os
import sha
import xmpp

PHOTO_DIR = "./photos/"

PHOTO_TYPES = {
    'image/png': '.png',
    'image/jpeg': '.jpg',
    'image/gif': '.gif',
    'image/bmp': '.bmp',
    }

def append_directory(filename):
    return os.path.join(PHOTO_DIR, filename)

def register_handler(session):
    session.RegisterHandler('presence', photo_update_handler)

def photo_update_handler(session, stanza):
    JID = stanza['from'].getStripped()
    vupdate = stanza.getTag('x', namespace='vcard-temp:x:update')
    if not vupdate:
        return
    photo = vupdate.getTag('photo')
    if not photo:
        return
    photo = photo.getData()
    if not photo:
        return
    #request the photo only if we don't have it already
    if not get_photo(photo):
        request_vcard(session, JID)

def get_photo(photo_hash):
    for ext in PHOTO_TYPES.values():
        filepath = append_directory(photo_hash + ext)
        if os.path.exists(filepath):
            return filepath

def request_vcard(session, JID):
    n = xmpp.Node('vCard', attrs={'xmlns': xmpp.NS_VCARD})
    iq = xmpp.Protocol('iq', JID, 'get', payload=[n])
    return session.SendAndCallForResponse(iq, recieve_vcard)

def recieve_vcard(session, stanza):
    photo = stanza.getTag('vCard').getTag('PHOTO')
    if not photo:
        return
    photo_type = photo.getTag('TYPE').getData()
    photo_bin = photo.getTag('BINVAL').getData()
    photo_bin = base64.b64decode(photo_bin)
    ext = PHOTO_TYPES[photo_type]
    photo_hash = sha.new()
    photo_hash.update(photo_bin)
    photo_hash = photo_hash.hexdigest()
    filename = append_directory(photo_hash + ext)
    file(filename, 'wb').write(photo_bin)

UPDATE 7/20/09
Here is roughly how I would solve the use case in the comment. I realize that my code doesn’t solve that case very nicely. If it made this more object oriented I could solve that problem. Until then, here is some rough code that I think does what it needs to. Save the above code as photo.py and be sure to create a “photos” directory

import xmpp
import photo

jid_photo_map = {}

def receive_presence(session, stanza):
    jid = stanza['from'].getStripped()
    vupdate = stanza.getTag('x', namespace='vcard-temp:x:update')
    if not vupdate:
        return
    photo = vupdate.getTag('photo')
    if not photo:
        return
    photo = photo.getData()
    if not photo:
        return
    jid_photo_map[jid] = photo

def login(username, password):
    jabber = xmpp.Client('gmail.com')
    jabber.connect(server=('talk.google.com', 5223))
    jabber.auth(username, password, 'test_client')
    jabber.sendInitPresence()
    jabber.RegisterHandler('presence', receive_presence)
    photo.register_handler(jabber)
    return jabber

def display_filenames(j):
    roster = j.getRoster()
    for jid in roster.getItems():
        if jid in jid_photo_map:
            photo_filename = photo.get_photo(jid_photo_map[jid])
        else:
            photo_filename = ""
        print jid, photo_filename

if __name__ == '__main__':
    j = login('username', 'password')
    for x in range(30):
        j.Process(1) # process for 1 second
    display_filenames(j)

Message Queue

I wrote some code for a group project that I am kind of proud of. It’s not very clean code, but it accomplishes something cool. It’s a way for a website to sent messages to a browser in real time, without the browser needing to constantly be checking to see if the website has a message that is ready to be sent.

We did most of our project in php. Here is our php code:

<?php include_once("json.php"); function get_url($url) { $output = array(); exec("curl " . $url, &$output); return $output[0]; } function msgq_new() { $id = get_url("http://127.0.0.1:8888/new/"); return $id; } function send_message($data) { return get_url("http://127.0.0.1:8888/post/" . urlencode(array2json($data))); } if (isset($_GET['action']) && $_GET['action'] == "wait_for_message") { header("Content-type: text/plain"); echo get_url("http://127.0.0.1:8888/wait/" . $_GET['id']); }

Here is the Javascript part. We were using the YUI library, but you could easily do this without it.:

function wait_for_message() {
  var id = document.body.id;
  YAHOO.util.Connect.asyncRequest('GET', '/wait_for_message.php?id=' + id, {success: function(response) {
    wait_for_message();
    if (!response.responseText || response.responseText == "\n") return; // Server sent a nop
    var data = YAHOO.lang.JSON.parse(response.responseText);
    window[data.handler](data);
  }});
}
YAHOO.util.Event.addListener(window,'load', wait_for_message)

Here is the Python part:

from time import time
from random import uniform
from Queue import Queue,Empty
from socket import error
from threading import Thread
from urllib import unquote_plus
from wsgiref.simple_server import make_server


clients={} # A Queue and a http server thread for each client

def send_to_all(msg):
    print "sending %r to all" % msg
    for k, x in clients.items()[:]:
        if (time() - x.last_get > 300):
            # Client has not asked for any messages for 5 minutes
            # Delete them.
	    x.active = False
	    del clients[k]
	    continue
        print "sending to %s" % k
        x.q.put(msg)
    print "finished sending messages."

def wait_for_message(q):
    try:
        #Wait for a new message.
        return q.get(True, uniform(55, 59))
    except Empty:
        return "" # no message within a minute, send keep-alive

def handle(environ, start_response):
    start_response('200 OK', [('Content-type', 'text/plain')])
    path = environ['PATH_INFO']

    if path.startswith("/wait/"):
	id = unquote_plus(path[len("/wait/"):])
	if id not in clients:
	    clients[id] = Server()
	clients[id].last_get = time()
        print "%s is waiting for a message..." % id
        return wait_for_message(clients[id].q)

    if path.startswith("/new"):
        from hashlib import md5
        id = md5(str(time())).hexdigest()
        clients[id] = Server()
        return id

    if path.startswith("/post/"):
	msg = unquote_plus(path[len("/post/"):])
        send_to_all(msg)

    return ""

class Server(Thread):
    "A Queue and a http server thread for each client"
    def __init__(self):
        Thread.__init__(self)
	self.q = Queue()
        self.setDaemon(1)
	self.active = True
	self.last_get = time()
        self.start()
    def run(self):
        while self.active:
            self.httpd.handle_request()

def start():
    httpd = make_server('0.0.0.0', 8888, handle)
    Server.httpd = httpd
    httpd.serve_forever()

start()

Timesheet program I wrote for Chris this summer.

from datetime import datetime
import os

def load_people():
    lines = [x.strip() for x in file('people.txt') if x.strip()]
    data = {}
    for line in lines:
        print line
        parts = line.split(' ')
        data[parts[0]] = ' '.join(parts[1:])
    return data

people = load_people()

cur_year = datetime.today().year

def get_name():
    while 1:
        try:
            num = raw_input("Type your number and press enter: ")
            return people[num]
        except KeyboardInterrupt:
            raise
        except:
            print "Invalid number"

def newday():
    return datetime.now().strftime("%m/%d %a")

def dateofline(line):
    return datetime(cur_year, int(line[0:2]), int(line[3:5])).date().day

def input_time():
    while 1:
        try:
            time = raw_input("Enter the time that you finished working (ie 4:32): ")
            hour, minute = (int(x) for x in x.split(':'))
            if hour < 7:
                hour += 12
            time = datetime.now()
            time.hour, time.minute, = hour, minute
            return time
        except:
            print "Invalid time."

def time():
    return datetime.now().strftime("%H:%M")

def punchin():
    print "Punched In"
    return 'tI ' + time()

def punchout():
    print "Punched Out"
    return 'tO ' + time()

def parsetime(string):
    return int(string[2:4]), int(string[5:7])

def record_time(name):
    filename = "%s.txt" % name
    if not os.path.exists(filename):
        f = file(filename, 'w')
        f.write('%sn' % name)
        f.write(newday())
        return f.write(punchin())
    f = file(filename, 'a')
    line = [x.strip() for x in file(filename) if x.strip()][-1]
    if datetime.today().day != dateofline(line):
        times = line.split('t')[1:]
        if len(times) % 2:
            print "You did not punch out last time."
            time = input_time()
            new_time = 'O ' + time.strftime("%H:%M")
            f.write('t' + new_time)
            times.append(new_time)
        times = [parsetime(x) for x in times]
        punched_in = -1
        total_hours = 0
        total_minutes = 0
        for x in times:
            total_hours += punched_in * x[0]
            total_minutes += punched_in * x[1]
            punched_in *= -1
        total_time = total_hours + total_minutes/60.
        f.write('tTotal: %0.2f' % total_time)
        f.write('n')
        f.write(newday())
        return f.write(punchin())
        raise NotImplemented
    times = line.split('t')[1:]
    if times[-1][0] == 'I':
        return f.write(punchout())
    if times[-1][0] == 'O':
        return f.write(punchin())
    raise Exception("Invalid data: %s" % times)    

while 1:
    name = get_name()
    print name
    record_time(name)

Counties in the Mississippi River Valley

Last Friday I took the code from an earlier go at this and made it filter census data by county to only have data in the target area. You can also view my results of the program.

#!/usr/bin/env python
import sys
import urllib

def dms(d,m,s):
    return d+m/60.+s/3600.

def parsedms(f):
    """
    Imports a coordinates from tsv file and returns it as a list
    """
    a=[]
    for line in f:
        t=line.rsplit("t")
        lon=dms(int(t[0]),int(t[1]),int(t[2]))
        lat=dms(int(t[3]),int(t[4]),int(t[5]))
        a.append((-lon,lat))
    return a

print >> sys.stderr, "loading river data"
river_file = urllib.urlopen("http://collincode.files.wordpress.com/2008/02/mississippi-river.txt")
print >> sys.stderr, "parsing river data"
river = parsedms(river_file)

def distance(Lon1,Lat1,Lon2,Lat2):
    """
    Calculates the distance between two coordinates
    Based on calcDistance in http://nmviewogc.cr.usgs.gov/javascript/aimsMap.js
    """
    import math as Math
    Lon1 = Lon1 * Math.pi / 180;
    Lon2 = Lon2 * Math.pi / 180;
    Lat1 = Lat1 * Math.pi / 180;
    Lat2 = Lat2 * Math.pi / 180;
    LonDist = Lon1-Lon2;
    LatDist = Lat1-Lat2;
    A = Math.pow(Math.sin(LatDist / 2),2) + Math.cos(Lat1) * Math.cos(Lat2) * Math.pow(Math.sin(LonDist /2),2);
    C = 2 * Math.asin(Math.sqrt(A));
    D = (3963.1676 - 13 * Math.sin((Lat1 + Lat2) / 2)) * C;
    return D

def dist2river(lon,lat,river):
    'returns the distance and closest point on the river to the point'
    smallest = distance(river[0][0],river[0][1],lon,lat) #check against initial point
    for point in river:
        d = distance(point[0],point[1],lon,lat)
        if d <= smallest: #see if this point is closer
            thepoint=point
            smallest=d
    return smallest, thepoint


COLUMNS = (
    (1, 2, 'state', str),
    (8, 71, 'name', lambda x: str(x).strip()),
    (72, 80, 'pop', int),
    (81, 89, 'houses', long),
    (118, 129, 'land', float),
    (130, 141, 'water', float),
    (142, 151, 'lat', float),
    (152, 162, 'lon', float),
)

def parse_line(line):
    return dict([(name, klass(line[s-1:e])) for s, e, name, klass in COLUMNS])

def parse_data(county_file):
    for line in county_file:
        if line:
            yield parse_line(line)

print >> sys.stderr, "loading county data"
county_file = urllib.urlopen("http://www.census.gov/tiger/tms/gazetteer/county2k.txt")
print >> sys.stderr, "parsing county data"
data = []
for county in parse_data(county_file):
    if county['name'].endswith(' County') or  county['name'].endswith(' Parish'):
        county['name'] = county['name'][:-len(' County')]
        data.append(county)

print >> sys.stderr, "calculating distances to river"
distances = {}
for county in data:
    key = "%s, %s" % (county['name'], county['state'])
    key = key.lower()
    dist = dist2river(county['lon'], county['lat'], river)[0]
    if dist < 305:
        distances[key] = dist


print >> sys.stderr, "processing your data"
def runfile(i, o):
    for line in i:
        line = line.strip("n")
        columns = line.split('t')
        county = columns[0]
        key = county.strip().lower()
        if key in distances:
            print >> o, "%.3ft%s" % (distances[key], line)

import os
for x in os.listdir('.'):
    if x.startswith('b'):
        print "running %s" % x
        runfile(file(x), file('o' + x, 'w'))

This one tells me the mac address (bssid) of the access point I am connected to with wireless zero configuration using wmi.

import win32com.client

def mhex(mac):
    return "".join('%02x' % x for x in mac)

wmi2 = win32com.client.GetObject("winmgmts:!root/wmi")
adapters = wmi2.execquery("select * from MSNdis_80211_BaseServiceSetIdentifier")
for ad in adapters:
    print ad.InstanceName
    print ad.Active and 'Active' or 'Inactive'
    print mhex(ad.Ndis80211MacAddress)