Permalink
Browse files

Initial commit of already working solution

  • Loading branch information...
0 parents commit bf9ea88f40bb12a6678b48b8f9f39977ca9b87ae Martin Sander committed Sep 28, 2009
Showing with 183 additions and 0 deletions.
  1. +1 −0 .gitignore
  2. +21 −0 LICENSE
  3. +17 −0 README
  4. +7 −0 ical_reply_sendmail_wrapper.sh
  5. +137 −0 mutt-ical.py
@@ -0,0 +1 @@
+.*.swp
@@ -0,0 +1,21 @@
+MIT/X Consortium License
+
+© 2009 TNG Martin Sander <mail at 0x89 dot eu>
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,17 @@
+mutt-ical.py is meant as a simple way to reply to ical invitations from mutt.
+
+To use it, copy it and ical_reply_sendmail_wrapper.sh to some directory in your $PATH.
+
+In order to be able to use it from mutt, modify your ~/.mailcap or /etc/mailcap
+files to call this script, add lines like:
+
+ text/calendar; mutt-ical.py -i -e "your@email.address" %s
+ application/ics; mutt-ical.py -i -e "your@email.address" %s
+
+(Don't forget to add your email address)
+
+To reply, just open the ical file from mutt.
+
+Inspired by:
+ http://weirdzone.ru/~veider/accept.py
+ http://vpim.rubyforge.org/files/samples/README_mutt.html
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+# get $sendmail from mutt
+SENDMAIL=`mutt -D|awk -F= '/^sendmail=/{print $2}' | sed -s 's/"//g'`
+
+# fix header for ical reply and pipe to sendmail
+sed '/^Content-Type: text\/calendar;/s/$/; METHOD="REPLY"/' | $SENDMAIL "$@"
@@ -0,0 +1,137 @@
+#!/usr/bin/env python
+# -*- coding: utf8 -*-
+
+"""
+This script is meant as a simple way to reply to ical invitations from mutt.
+See README for instructions and LICENSE for licensing information.
+"""
+
+__author__="Martin Sander"
+__license__="MIT"
+
+import vobject
+import tempfile, time
+import os, sys
+from datetime import datetime
+from subprocess import Popen, PIPE
+from getopt import gnu_getopt as getopt
+
+usage="""
+usage:
+%s [OPTIONS] -e your@email.address filename.ics
+OPTIONS:
+ -i interactive
+ -a accept
+ -d decline
+ -t tentatively accept
+ (accept is default, last one wins)
+""" % sys.argv[0]
+
+def del_if_present(dic, key):
+ if dic.has_key(key):
+ del dic[key]
+
+def set_accept_state(attendees, state):
+ for attendee in attendees:
+ attendee.params['PARTSTAT'][0] = state
+ for i in ["RSVP","ROLE","X-NUM-GUESTS","CUTYPE"]:
+ del_if_present(attendee.params,i)
+ return attendees
+
+def get_accept_decline():
+ while True:
+ sys.stdout.write("Accept Invitation? [Y/n/t]")
+ ans = sys.stdin.readline()
+ if ans.lower() == 'y\n' or ans == '\n':
+ return 'ACCEPTED'
+ elif ans.lower() == 'n\n':
+ return 'DECLINED'
+ elif ans.lower() =='t\n':
+ return 'TENTATIVE'
+
+def get_answer(invitation):
+ # create
+ ans = vobject.newFromBehavior('vcalendar')
+ ans.add('method')
+ ans.method.value = "REPLY"
+ ans.add('vevent')
+
+ # just copy from invitation
+ for i in ["uid", "summary", "dtstart", "dtend", "organizer"]:
+ if invitation.vevent.contents.has_key(i):
+ ans.vevent.add( invitation.vevent.contents[i][0] )
+
+ # new timestamp
+ ans.vevent.add('dtstamp')
+ ans.vevent.dtstamp.value = datetime.utcnow().replace(
+ tzinfo = invitation.vevent.dtstamp.value.tzinfo)
+ return ans
+
+def write_to_tempfile(ical):
+ tempdir = tempfile.mkdtemp()
+ icsfile = tempdir+"/event-reply.ics"
+ with open(icsfile,"w") as f:
+ f.write(ical.serialize())
+ return icsfile, tempdir
+
+def get_mutt_command(ical, email_address, accept_decline, icsfile):
+ accept_decline = accept_decline.capitalize()
+ sender = ical.vevent.contents['organizer'][0].value.split(':')[1].encode()
+ summary = ical.vevent.contents['summary'][0].value.encode()
+ command = ["mutt", sender, "-a", icsfile,
+ "-e", 'set sendmail=\'ical_reply_sendmail_wrapper.sh\'',
+ "-s", "'%s: %s'" % (accept_decline, summary)]
+ return command
+
+def execute(command, mailtext):
+ process = Popen(command, stdin=PIPE)
+ process.stdin.write(mailtext)
+ process.stdin.close()
+
+ result = None
+ while result is None:
+ result = process.poll()
+ time.sleep(.1)
+ if result != 0:
+ print "unable to send reply, subprocess exited with\
+ exit code %d\nPress return to continue" % result
+ sys.stdin.readline()
+
+if __name__=="__main__":
+ email_address = None
+ accept_decline = 'ACCEPTED'
+ opts, args=getopt(sys.argv[1:],"e:aidt")
+ for opt,arg in opts:
+ if opt == '-e':
+ email_address = arg
+ if opt == '-i':
+ accept_decline = get_accept_decline()
+ if opt == '-a':
+ accept_decline = 'ACCEPTED'
+ if opt == '-d':
+ accept_decline = 'DECLINED'
+ if opt == '-t':
+ accept_decline = 'TENTATIVE'
+
+ if len(args) < 1 or not email_address:
+ sys.stderr.write(usage)
+ sys.exit(1)
+
+ invitation_file = args[0]
+ with open(invitation_file) as f:
+ invitation = vobject.readOne(f)
+
+ ans = get_answer(invitation)
+
+ attendees = invitation.vevent.contents['attendee']
+ set_accept_state(attendees,accept_decline)
+ ans.vevent.contents['attendee'] = [i for i in attendees if i.value.endswith(email_address)]
+
+ icsfile, tempdir = write_to_tempfile(ans)
+
+ mutt_command = get_mutt_command(ans, email_address, accept_decline, icsfile)
+ mailtext = "'%s has %s'" % (email_address, accept_decline.lower())
+ execute(mutt_command, mailtext)
+
+ os.remove(icsfile)
+ os.rmdir(tempdir)

0 comments on commit bf9ea88

Please sign in to comment.