Permalink
Browse files

* Forked the project

* Added functionality to display the contents of the calendar entry in plain text before deciding what to do.
* Fixed a bug where Outlook generated invites were not being parsed correctly
* Removed the wrapper script as a default in favour of using mutt's SMTP configuration
  • Loading branch information...
1 parent 4dd0e4c commit 496847a33ac96167570cf78438e603fcf06b40b3 Dominic White committed Oct 25, 2011
Showing with 75 additions and 26 deletions.
  1. +35 −9 README
  2. +40 −17 mutt-ical.py
View
44 README
@@ -1,20 +1,46 @@
-mutt-ical.py is meant as a simple way to reply to ical invitations from mutt.
+mutt-ical.py is meant as a simple way to reply to display and reply to ical invitations from mutt.
-To use it, copy it and ical_reply_sendmail_wrapper.sh to some directory in your $PATH.
+It was originally authored by Martin Sander, and later modified by Dominic White.
-In order to be able to use it from mutt, modify your ~/.mailcap or /etc/mailcap
-files to call this script, add lines like:
+Installing
+----------
- text/calendar; mutt-ical.py -i -e "your@email.address" %s
- application/ics; mutt-ical.py -i -e "your@email.address" %s
+1) Copy it into somewhere in your PATH, (or you can specify the PATH in your .mailcap)
+2) Edit your mailcap (~/.mailcap or /etc/mailcap) to have the following line:
+
+ text/calendar; <path>mutt-ical.py -i -e "user@domain.tld" %s
+ application/ics; <path>mutt-ical.py -i -e "user@domain.tld" %s
(Don't forget to add your email address)
-To reply, just open the ical file from mutt.
+OSX Users
+---------
+
+3) For added fun on OSX, you can extend it to the following, to get iCal to open it nicely too (iCal cares not for mime types it seems):
+
+ text/calendar; mv %s %s.ics && open %s.ics && <path>mutt-ical.py -i -e "user@domain.tld" %s.ics && rm %s.ics
+
+4) You can force iCal to stop trying to send mail on your behalf by replacing the file /Applications/iCal.app/Contents/Resources/Scripts/Mail.scpt with your own ActionScript. I went with the following: error number -128 Which tells it that the user cancelled the action.
+
+ i) Open AppleScript Editor, paste the code from above into a new script, then save it.
+ ii) Move the old script /Applications/iCal.app/Contents/Resources/Scripts/Mail.scpt just in case you want to re-enable the functionality.
+ iii) Copy your new script into place.
+
+Usage
+-----
+
+To reply, just open the ical file from mutt by:
+ i) Viewing the attachements (usually 'v')
+ ii) Invoking the mailcap entry (usually 'm')
+ iii) Choosing your reply
+
+Martin also included the sendmail wrapper, but since mutt's SMTP support has now improved, rather configure your SMTP settings through mutt's config. The wrapper is kept in case someone finds it useful. However, the get_mutt_command function will need the relevant code uncommented to use it.
+
+Requirements
+------------
-Requirements:
mutt, python, bash, vobject:
-http://vobject.skyhouseconsulting.com/
+http://vobject.skyhouseconsulting.com/ or 'easy_install vobject'
Inspired by:
http://weirdzone.ru/~veider/accept.py
View
@@ -11,14 +11,14 @@
__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
+from pprint import pprint
usage="""
usage:
@@ -37,14 +37,14 @@ def del_if_present(dic, key):
def set_accept_state(attendees, state):
for attendee in attendees:
- attendee.params['PARTSTAT'][0] = state
+ attendee.params['PARTSTAT'] = [unicode(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]")
+ sys.stdout.write("\nAccept Invitation? [Y/n/t]")
ans = sys.stdin.readline()
if ans.lower() == 'y\n' or ans == '\n':
return 'ACCEPTED'
@@ -83,8 +83,9 @@ def get_mutt_command(ical, email_address, accept_decline, icsfile):
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]
+ #Uncomment the below line, and move it above the -s line to enable the wrapper
+ #"-e", 'set sendmail=\'ical_reply_sendmail_wrapper.sh\'',
return command
def execute(command, mailtext):
@@ -101,10 +102,45 @@ def execute(command, mailtext):
exit code %d\nPress return to continue" % result
sys.stdin.readline()
+def openics(invitation_file):
+ 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)
+ return invitation
+
+def display(ical):
+ summary = ical.vevent.contents['summary'][0].value.encode()
+ sender = ical.vevent.contents['organizer'][0].value.split(':')[1].encode()
+ try:
+ description = ical.vevent.contents['description'][0].value
+ except KeyError:
+ description = "NO DESCRIPTION"
+ attendees = ical.vevent.contents['attendee']
+ sys.stdout.write("From:\t" + sender + "\n")
+ sys.stdout.write("Title:\t" + summary + "\n")
+ sys.stdout.write("To:\t")
+ for attendee in attendees:
+ sys.stdout.write(attendee.params['CN'][0] + " <" + attendee.value.split(':')[1] + ">, ")
+ sys.stdout.write("\n\n")
+ sys.stdout.write(description + "\n")
+
if __name__=="__main__":
email_address = None
accept_decline = 'ACCEPTED'
opts, args=getopt(sys.argv[1:],"e:aidt")
+
+ if len(args) < 1:
+ sys.stderr.write(usage)
+ sys.exit(1)
+
+ invitation = openics(args[0])
+ #print(invitation)
+ display(invitation)
+
for opt,arg in opts:
if opt == '-e':
email_address = arg
@@ -117,19 +153,6 @@ def execute(command, mailtext):
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']

0 comments on commit 496847a

Please sign in to comment.