Skip to content

Commit

Permalink
* Forked the project
Browse files Browse the repository at this point in the history
* 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
Dominic White committed Oct 25, 2011
1 parent 4dd0e4c commit 496847a
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 26 deletions.
44 changes: 35 additions & 9 deletions 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
Expand Down
57 changes: 40 additions & 17 deletions mutt-ical.py
Expand Up @@ -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:
Expand All @@ -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'
Expand Down Expand Up @@ -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):
Expand All @@ -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
Expand All @@ -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']
Expand Down

0 comments on commit 496847a

Please sign in to comment.