Autoview .ics iCalendar (text/calendar) attachments in mutt
Wednesday, 5 October 2011 15:13![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
We interrupt this journal to bring you an important productivity-related update!
I've been vaguely trying to figure out how to display text/calendar ical meeting invite attachments in mutt, which I receive from colleagues sending requests from their fancy-pants graphical email clients. If you've looked at a vCalendar/iCalendar file you'll know it's not a very pleasant thing to read, especially since Outlook seems to like sending invites relative to Eastern Time from and to people located in the UK.
Outlook used to send a text summary along with the attachment so this hasn't been particularly high on my priority lists until lately, now that both the iPhone and the latest versions of Outlook simply send a blank message (in both text and HTML parts!) with the invite attached.
I found a collection of Ruby scripts after a few attempts to get my head around a couple of Python vCalendar-parsing libraries without much luck. The viewical.rb file included in that package worked fairly well, but had a rounding error causing HH:30 to turn into HH:29 and didn't compensate for Outlook insanity, so I've fixed that (at least, during daylight savings, we'll have to see what happens after that). The package from the page above also provides some utilities to Accept/Decline meeting invites, but I don't have any personal use for them at the moment so I haven't set them up. My copy of viewical.rb now looks like this:
#!/usr/bin/env ruby
require "rubygems" # apt-get install rubygems
require "icalendar" # gem install icalendar
require "date"
class DateTime
def myformat
(self.offset == 0 ? (DateTime.parse(self.strftime("%a %b %d %Y, %H:%M ") + self.icalendar_tzid)) : self).
new_offset(Rational(Time.now.utc_offset - 60*60, 24*60*60)).strftime("%a %b %d %Y, %H:%M")
# - 60*60 to compensate for icalendar gem/Outlook mismatch
end
end
cals = Icalendar.parse($<)
cals.each do |cal|
cal.events.each do |event|
puts "Organizer: #{event.organizer}"
puts "Event: #{event.summary}"
puts "Starts: #{event.dtstart.myformat} local time"
puts "Ends: #{event.dtend.myformat}"
puts "Location: #{event.location}"
puts "Contact: #{event.contacts}"
puts "Description:\n#{event.description}"
puts ""
end
end
Put that in your PATH somewhere, then put this in your .mailcap:
text/calendar; icalview.rb; copiousoutput
And this in your .muttrc:
auto_view text/calendar
Now whenever you view an email with a text/calendar attachment, mutt will display it in-line where the message usually appears (above the message, if there is one).
As an aside, it was frustrating to find out that although Google Calendar has an "Import Calendar" function on their frontend, there's no equivalent call in their API, so my idea to write an application to just pump the attachment directly to my Google Calendar account fell rather flat on its face.
Edit (2016-02-18): A more recent version of the Ruby icalendar package breaks the above script, and the time zone stuff is still broken in it anyway. In the meantime it looks like the offerings from the Python side of things have improved. The ics package seems to parse the files really well, including timezone information using the arrow library. I'm impressed. Here's the Python equivalent of the ruby script:
icalview.py
I've been vaguely trying to figure out how to display text/calendar ical meeting invite attachments in mutt, which I receive from colleagues sending requests from their fancy-pants graphical email clients. If you've looked at a vCalendar/iCalendar file you'll know it's not a very pleasant thing to read, especially since Outlook seems to like sending invites relative to Eastern Time from and to people located in the UK.
Outlook used to send a text summary along with the attachment so this hasn't been particularly high on my priority lists until lately, now that both the iPhone and the latest versions of Outlook simply send a blank message (in both text and HTML parts!) with the invite attached.
I found a collection of Ruby scripts after a few attempts to get my head around a couple of Python vCalendar-parsing libraries without much luck. The viewical.rb file included in that package worked fairly well, but had a rounding error causing HH:30 to turn into HH:29 and didn't compensate for Outlook insanity, so I've fixed that (at least, during daylight savings, we'll have to see what happens after that). The package from the page above also provides some utilities to Accept/Decline meeting invites, but I don't have any personal use for them at the moment so I haven't set them up. My copy of viewical.rb now looks like this:
#!/usr/bin/env ruby
require "rubygems" # apt-get install rubygems
require "icalendar" # gem install icalendar
require "date"
class DateTime
def myformat
(self.offset == 0 ? (DateTime.parse(self.strftime("%a %b %d %Y, %H:%M ") + self.icalendar_tzid)) : self).
new_offset(Rational(Time.now.utc_offset - 60*60, 24*60*60)).strftime("%a %b %d %Y, %H:%M")
# - 60*60 to compensate for icalendar gem/Outlook mismatch
end
end
cals = Icalendar.parse($<)
cals.each do |cal|
cal.events.each do |event|
puts "Organizer: #{event.organizer}"
puts "Event: #{event.summary}"
puts "Starts: #{event.dtstart.myformat} local time"
puts "Ends: #{event.dtend.myformat}"
puts "Location: #{event.location}"
puts "Contact: #{event.contacts}"
puts "Description:\n#{event.description}"
puts ""
end
end
Put that in your PATH somewhere, then put this in your .mailcap:
text/calendar; icalview.rb; copiousoutput
And this in your .muttrc:
auto_view text/calendar
Now whenever you view an email with a text/calendar attachment, mutt will display it in-line where the message usually appears (above the message, if there is one).
As an aside, it was frustrating to find out that although Google Calendar has an "Import Calendar" function on their frontend, there's no equivalent call in their API, so my idea to write an application to just pump the attachment directly to my Google Calendar account fell rather flat on its face.
Edit (2016-02-18): A more recent version of the Ruby icalendar package breaks the above script, and the time zone stuff is still broken in it anyway. In the meantime it looks like the offerings from the Python side of things have improved. The ics package seems to parse the files really well, including timezone information using the arrow library. I'm impressed. Here's the Python equivalent of the ruby script:
icalview.py
#!/usr/bin/env python import sys import ics # pip install ics import arrow # pulled in with ics, else pip install arrow cal = ics.Calendar(sys.stdin.read().decode('utf-8')) tz = arrow.now().tzinfo tzformat = 'ddd DD MMM HH:mm' for event in cal.events: extra = {} for item in event._unused: if not isinstance(item, ics.parse.ContentLine): continue try: extra[item.name.lower()] = item.value except: print item raise print "Event: ", event.name print "Status: ", extra['status'].title() print "Organiser:", extra['organizer'] print "Starts: ", event.begin.to(tz).format(tzformat), "(local time)" print "Ends: ", event.end.to(tz).format(tzformat) print "Location: ", event.location print "Description:" print event.description print