Editing Outlook Calendars With Ruby

I admit it. I’m a pretty lazy guy. I don’t have much patience for mind-numbingly repetitive tasks. They bring me down. Editing my Outlook calendar every day is simply a waste of time in my opinion. I figured that there must be a way to automatically create appointments through some kind of script. Then I wondered if Ruby could help.
After doing some quick research, I found out that it’s actually quite easy to connect Ruby and Outlook. This is done through Microsoft’s Object Linking and Embedding (OLE) protocol. I wasn’t able to find a definitive tutorial for Ruby and OLE, but there is enough information scattered about the internet to get a simple script up and running.
Modifying an Outlook calendar from Ruby basically boils down to knowing how to do three things: 1) Opening a context to Outlook, 2) Connecting to the “Calendar” (as opposed to the Mailbox, Contacts, etc.), and 3) Creating/Listing appointments. I learned how to do all of this from the following sources:
- Controlling the Outlook session using OLE
- Outlook Appointments with Ruby
- Automating Outlook with Ruby: Sending Email
- win32ole Ruby Standard Library Documentation
Those links provided enough information for me to write a script that connects to my calendar and prints out all of my existing appointments. It’s not much, but as a proof-of-concept it’s not bad.
And here’s the code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | # # 28 June 2010, Phillip Weisberg <fettuccini@qiroka.com> # License: Creative Commons 3.0 Attribution 3.0 Unported # Purpose: Demonstrates how to read/add items in a Microsoft Outlook calendar # # References: # Controlling the Outlook session using OLE: http://downloads.sybase.com/codexchange/powerbuilder/404/PowerBuilder_-_Controlling_the_Outlook_session_using_OLE.htm # Outlook Appointments with Ruby: http://www.artima.com/forums/flat.jsp?forum=123&thread=111926 # Automating Outlook with Ruby: Sending Email: http://rubyonwindows.blogspot.com/2007/07/automating-outlook-with-ruby-sending.html # win32ole Ruby Standard Library Documentation: http://www.ruby-doc.org/stdlib/libdoc/win32ole/rdoc/index.html # #------------------------------------------------------------------------------ # Requires #------------------------------------------------------------------------------ require 'win32ole' require 'date' #------------------------------------------------------------------------------ # Struct Definitions #------------------------------------------------------------------------------ Struct.new( "AppointmentInfo", :Subject, :Body, :Start, :End, :ReminderSet ) #------------------------------------------------------------------------------ # Instance Variables #------------------------------------------------------------------------------ @outlookApplication = WIN32OLE.new( 'Outlook.Application' ) #------------------------------------------------------------------------------ # Methods #------------------------------------------------------------------------------ # --- Creates a new appointment in Outlook --- def CreateOutlookAppointment( appointmentInfo, categories ) # Verify that the appointment ends after it starts (Note that the data of an all-day event appears to end before it starts) if ( appointmentInfo.End <= appointmentInfo.Start ) puts "[createAppointment] Warning: Appointment creation aborted due to end date before start date." return end # Create the Outlook application item appointment = @outlookApplication.CreateItem( 1 ) # 1 = Creates an appointment appointment.Subject = appointmentInfo.Subject appointment.Body = appointmentInfo.Body appointment.ReminderSet = appointmentInfo.ReminderSet appointment.Start = appointmentInfo.Start appointment.End = appointmentInfo.End appointment.Categories = categories appointment.Save puts "Created Appointment: [#{appointment.Subject}] at #{appointment.Start}" end # --- Finds a listing of all appointments on the specified date from a list of existing appointments --- def FindExistingAppointmentsForDate( existingAppointments, dateStart ) appointmentsForDate = Array.new existingAppointments.each { |currAppointment| if ( currAppointment.Start.year == dateStart.year ) and ( currAppointment.Start.month == dateStart.month ) and ( currAppointment.Start.day == dateStart.day ) appointmentsForDate.push( currAppointment ) end } return appointmentsForDate end # --- Loads all of the existing calendar appointments into the array @existingAppointments --- # --- TODO: Load recurrences as appointments (currently, they are ignored) --- def LoadExistingAppointments namespace = @outlookApplication.GetNamespace( "MAPI" ) ole_folder = namespace.GetDefaultFolder( 9 ) # 9 = Calendar numItems = ole_folder.Items.Count existingAppointments = Array.new for idx in ( 1 .. numItems ) currItem = ole_folder.Items.Item( idx ) dateStart = Time.local( currItem.Start.split("/")[0], currItem.Start.split("/")[1], currItem.Start.split("/")[2].split(" ")[0], currItem.Start.split(" ")[1].split(":")[0], currItem.Start.split(" ")[1].split(":")[1], currItem.Start.split(" ")[1].split(":")[2]) dateEnd = Time.local( currItem.End.split("/")[0], currItem.End.split("/")[1], currItem.End.split("/")[2].split(" ")[0], currItem.End.split(" ")[1].split(":")[0], currItem.End.split(" ")[1].split(":")[1], currItem.End.split(" ")[1].split(":")[2]) tempAppointment = Struct::AppointmentInfo.new( currItem.Subject, "", dateStart, dateEnd, false ); existingAppointments.push( tempAppointment ) end return existingAppointments end #------------------------------------------------------------------------------ # Main Program #------------------------------------------------------------------------------ # --- Load all existing appointments and print the contents --- puts "-----[All Existing Appointments]----------------------------------------" existingAppointments = LoadExistingAppointments() existingAppointments.each { |existingAppointment| puts "Existing Appointment: [#{existingAppointment.Subject}] at: #{existingAppointment.Start}" } puts "------------------------------------------------------------------------\n\n" # --- Find all appointments for a specific day and print the contents --- puts "----[Existing Appointments Today]---------------------------------------" dateToday = Time.now appointmentsForDate = FindExistingAppointmentsForDate( existingAppointments, dateToday ) appointmentsForDate.each { |appointmentForDate| puts "Existing Appointment: [#{appointmentForDate.Subject}] at: #{appointmentForDate.Start}" } puts "------------------------------------------------------------------------\n\n" # --- Add an hour long appointment to the Outlook calendar (starting from the current date/time) --- startDate = Time.now endDate = startDate + 3600 # endDate is one hour after the startDate newAppointment = Struct::AppointmentInfo.new( "Example Subject", "Example description", startDate, endDate, false ); CreateOutlookAppointment( newAppointment, "work, school" ) puts "DONE" |
The code demonstrates how to list all existing appointments in an Outlook calendar as well as how to add new ones programmatically. As a note, this script does not account for recurrences (appointments which repeat) or all-day events. However, it does provide a starting point for taking control of your Outlook calendar through Ruby.