Sunday, July 1, 2007

Automating Outlook with Ruby: Sending Email

Just as we have done with Excel and Word, we can use the win32ole library to automate various tasks in Microsoft Outlook. Let's take a look at how to create an new email message.

Of course, we'll want to require the win32ole library:


require 'win32ole'

Create an instance of the Outlook Application object:

outlook = WIN32OLE.new('Outlook.Application')

Outlook is a single-instance application, so if it is already running, calling the WIN32OLE.new method will behave the same as the WIN32OLE.connect method, returning the running instance.

To create a new item in Outlook, call the Application object's CreateItem method and pass it a numeric value that represents the type of item to create. Valid arguments include:

olMailItem = 0
olAppointmentItem = 1
olContactItem = 2
olTaskItem = 3
olJournalItem = 4
olNoteItem = 5
olPostItem = 6

So, to create a new email message (ie, MailItem), we call the CreateItem method with an argument of 0 (zero):

message = outlook.CreateItem(0)

This returns the newly-created mail item object. We'll proceed by setting values for this MailItem object's properties and calling some of its methods.

To define the subject line of the message, set its Subject value with a string:

message.Subject = 'Double-Header Today'

To define the message body, set the Body value to a string:

message.Body = 'This is the message body.'

Alternatively, for an HTML message body, set the HtmlBody value to an HTML-encoded string.

Define the recipient by setting the To property...

message.To = 'ted.williams@redsox.com'

...or call the Recipients.Add method one or more times:

message.Recipients.Add 'ted.williams@redsox.com'
message.Recipients.Add 'joe.dimaggio@yankees.com'

Got attachments to add? Call the Attachments.Add method one or more times, passing it the path and name of the attachment:

message.Attachments.Add('c:\my_folder\my_file.txt', 1)

The second argument, 1, indicates that this is an attached file, rather than a link to the original file.

You can save the unsent message in your Outbox by calling the Save method...

message.Save

This would then allow you to open and review the message before manually clicking the Send button.

Of course, you can send the message with the Send method:

message.Send

Note that when using the Send method, Outlook may display a security alert that forces the user -- after a forced pause of about five seconds -- to click 'Yes' to allow the message to be sent. I am unaware of a method for avoiding this alert dialog.

UPDATE: I have discovered, but not yet used, a free Outlook add-in from MAPILab that allows the end user to set Outlook security settings. And the related ($99) Security Manager claims to allow you to bypass the Outlook security alerts, with a single line of code. I can't recommend either solution, not having tried them, but mention them here for your information.

That about wraps our show for today. Would you like to see more about Automating Outlook with Ruby? As always, feel free to post a comment here or email me with questions, comments, or suggestions.

Thanks for stopping by!


Digg my article

17 comments:

Revence said...

I wonder how one can mine out the chosen default mail client ... I wouldn't want to start Outlook for someone who uses, say, Thunderbird.

Is that somewhere in the Registry?

Nice, nice job, here. Keep it up.

Christian said...

When executing the Send-method I get a message box with a warning that an application tries to send email automaticly and if I want to allow this.
Maybe this should also be handled?

Adam said...

Yeah I encountered the same thing. I tried to get around this using something like:

wsh = WIN32OLE.new('Wscript.shell')
wsh.AppActivate('Microsoft Office Outlook')
sleep(1)
wsh.SendKeys('{TAB}{TAB} {TAB}{TAB}{ENTER}')
sleep(7) # to wait for the timer
wsh.SendKeys('{TAB}{TAB}{ENTER}')

But there has got to be an easier way....

David Mullet said...

@revence:

See my new post here.

I hope that helps.

@christian & adam:

I should have included mention of this in my post, sorry. I don't know that it is possible to disable the security alert. I shall investigate further...

walter said...

The line above has message.etc assigning to attachment, but it's not used in the example.

attachment = message.Attachments.Add('c:\my_folder\my_file.txt', 1)

Probably you had in mind to assign the folder to attachment then add it to .Add method.

any other reason to use it here?

cheers
walter

David Mullet said...

@walter:

Actually, there was no reason for me to assign to a variable. The line should have read:

message.Attachments.Add('c:\my_folder\my_file.txt', 1)

I have corrected this in the post. Thanks for calling that to my attention!

David

Anonymous said...

Hi.

Thanks for the article. Do you know if this script will work with Outlook Express(?)

-Katie

David Mullet said...

@Katie-

Unfortunately, Outlook Express does not expose its objects for COM/OLE automation, so this script could not be adapted for Outlook Express.

David

rethas said...

Thank you for this utility. saved my day. Just to build on this further, is there a way to run multiple instances of the same ruby script. I would like to use this for some testing purposes where I am sending say 10 mails every second.

any thoughts?

Anonymous said...

For Katie:

if your default email program is Outlook Express, you can get around the lack of COM support by calling a mailto: tag somewhere. Thus running Outlook Express.

As I recall, in python you can use the webbrowser module to call a mailto tag, then form the tag with all your data with string concatenation.

Not sure how to do the same in Ruby
however this approach should work for your case.

cheers
walter

Anonymous said...

For Katie:

Here's a followup example python script. Please anyone post back with a Ruby equivalent, I'd appreciate it ;)

#script uses mailto tag to make
#email for Outlook Express users
#returns in body are replaced with
#linefeeds to remove OE error

import webbrowser

username = raw_input("Type the user's username: ")
email = raw_input("Type the user's email address: ")
subject = "Payment is Due"


body = r"""Dear Valued Customer,

We have noticed that you owe us money, please pay us now $"%s".

Regards,

Customer Service Department
""" %(amount_owed)

# replace return with line feed
body = body.replace("\n","%0A")

#make email
uri = r"mailto:%(email)s?subject=%(subject)s&body=%(body)s" %{'email' : email, 'subject':subject, 'body':body}

#send email to outlook express
webbrowser.open(uri)

j.erik said...

Hi,

really really usefull article for me. Do you know perhaps how i can access the outlook addressbook?

cheers -- jerik

David Mullet said...

@j.erik:

If you mean Outlook Contacts, see my new post about it here.

David

Anonymous said...

Hi,
thank for your useful outlook blogs.
I'm looking to find the hook for monitoring and filtering outlook mails. Basically, i'd like to watch and filter incoming emails (realtime) so i can apply rules on it (think procmail in *nix).

thank you and kind regards - botp

j.erik said...

@david:

No, not the contacts, I meand the address book. (Shortcut on Outlook is : STRG + SHIFT + b)

I want to make an easier and better search. The one in outlook, takes me to long to query for persons...

cheers -- jerik

David Mullet said...

@jerik:

I have just posted a new article here, discussing the Outlook Address Books.

David

cmcevoy said...

I have previously used a library called Redemption for bypassing the oh-so-irritating 'click confirm to send' dialog. I don't know how it would interface with Ruby, but I will have a go to see if it works.
http://www.dimastr.com/redemption/