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:

  1. Controlling the Outlook session using OLE
  2. Outlook Appointments with Ruby
  3. Automating Outlook with Ruby: Sending Email
  4. 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&amp;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.

Comments are closed.