cowboy me, 2.0: jose nazario beauty and the street

sandy and ICS

Sandy - your free personal email assistant

"sandy" is a free personal inbox assistant. the goal of sandy is to bridge human readable emails to reminders. it's built on the stikkit backend.

some cool stuff, i have been using it for a bit. i recently saw that sandy should be able to take an ICS request and handle it, but it's always failed for me. so i solved the problem with a workaround, a python script that gets fed by procmail and feeds sandy.


# icsparse 2008 jose nazario

import email, getopt, os, smtplib, sys, time

# edit this for your sandy setup # if you don't have one, see SANDYADDR = '' FROM = ''

def notify_sandy(meeting): s = smtplib.SMTP('localhost') msg = """Subject: reminder To: %s From: %s

%s """ % (SANDYADDR, FROM, meeting) s.sendmail(FROM, (SANDYADDR, FROM,), msg) del(s)

class Event(object): def __init__(self, request): self.start=False self.end=False self.about=False self.location=False self.tags = ['@office', ] self.parse(request)

def __repr__(self): if self.location: location = 'location: %s' % self.location else: location = '' return 'remind me that %s on %s until %s %s %s' % \ (self.about, time.strftime('%D at %R', self.start), time.strftime('%D at %R', self.end), location, ' '.join(self.tags))

def valueOf(self, line): return line.split(':', 1)[-1]

def parse(self, request): # remove \r for windows ... lines = [ x.strip() for x in request.split('\n') ] if 'BEGIN:VEVENT' not in lines: return inevent = False for line in lines: if line.startswith('SUMMARY:'): line = line.strip().replace('\\N', '') about = line.split(':', 1) if len(about) != 2: self.about = '' else: self.about = about[1] if line == 'BEGIN:VEVENT': inevent = True continue if line == 'END:VEVENT': inevent = False continue if not inevent: continue if line.startswith('DTSTART'): self.start = time.strptime(self.valueOf(line), '%Y%m%dT%H%M%S') if line.startswith('DTEND'): self.end = time.strptime(self.valueOf(line), '%Y%m%dT%H%M%S') if line.startswith('LOCATION'): self.location = self.valueOf(line) if line.startswith('RRULE:FREQ='): for field in line.replace('RRULE:', '').split(';'): if field.startswith('FREQ='): f = field.split('=')[1].lower() self.tags.append('@%s' % f)

def usage(usagestr): print usagestr sys.exit(1)

def main(): usagestr = """icsparse parses ICS events into Sandy reminder

reads an RFC822 mbox message via stdin and adds them to your Sandy reminder"""

try: opts, args = getopt.getopt(sys.argv[1:], 'h') except: usage(usagestr)

for o, a in opts: if o == '-h': usage(usagestr)

try: msgfile = args[1] fp = open(msgfile) except IndexError: fp = sys.stdin msg = email.message_from_file(fp) fp.close()

for part in msg.walk(): if part.get_content_maintype() == 'multipart': continue if 'Content-Type' not in part.keys(): continue found = False # look for common meeting markers: # - meeting.ics as an attachment name # - Content-Type:text/calendar for calpart in ('name="meeting.ics"', 'text/calendar',): if calpart in part['Content-Type']: found = True if not found: continue payload = part.get_payload(decode=True) event = Event(payload) if not event.about: pass else: # print event notify_sandy(str(event))

if __name__ == '__main__': main()

and this is how i feed it via procmail on my inbox:
* BEGIN:VCALENDAR|Content-Class: *urn:content-classes:calendarmessage|Content-Type: *text/calendar|name="meeting.ics"

:0 c |/home/jose/

voila, my problem is solved.



next Tuesday, Apr 15, 2008 @ 09:36am | previous Monday, Feb 18, 2008 @ 06:10pm | archives

Last modified: Thursday, Mar 06, 2008 @ 12:34pm
Weblog Commenting and Trackback by

Your Ad Here

copyright © 2002-2005 jose nazario, all rights reserved.