summaryrefslogtreecommitdiff
path: root/mutt-ical.py
diff options
context:
space:
mode:
authorJesse Morgan <jesse@jesterpm.net>2013-03-16 10:37:40 -0700
committerJesse Morgan <jesse@jesterpm.net>2013-03-16 10:37:40 -0700
commit0593e4279ac6a11c1b95c0cca35f20f631e1ff5c (patch)
tree06cbfd24ee8efed98881d16ace34b6a7c8c1da3e /mutt-ical.py
parentac5044074e01f518ae3c837dfad0fa3fc0719962 (diff)
Adding files from bismuth
Diffstat (limited to 'mutt-ical.py')
-rwxr-xr-xmutt-ical.py149
1 files changed, 149 insertions, 0 deletions
diff --git a/mutt-ical.py b/mutt-ical.py
new file mode 100755
index 0000000..69ff4b2
--- /dev/null
+++ b/mutt-ical.py
@@ -0,0 +1,149 @@
+#!/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.
+"""
+
+from __future__ import with_statement
+
+__author__="Martin Sander"
+__license__="MIT"
+
+
+import vobject
+import tempfile, time
+import os, sys
+import warnings
+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", "-a", icsfile,
+ "-e", 'set sendmail=\'ical_reply_sendmail_wrapper.sh\'',
+ "-s", "'%s: %s'" % (accept_decline, summary), "--", sender]
+ 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:
+ try:
+ with warnings.catch_warnings(): #vobject uses deprecated Exception stuff
+ warnings.simplefilter("ignore")
+ invitation = vobject.readOne(f, ignoreUnreadable=True)
+ except AttributeError:
+ invitation = vobject.readOne(f, ignoreUnreadable=True)
+
+ 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)]
+ if len(ans.vevent.contents) < 1:
+ sys.stderr.write("Seems like you have not been invited to this event!\n")
+ sys.exit(1)
+
+ 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)