Send Entourage mails from the command line with Ruby

I’ve been spending a lot of time creating reports lately, and I generally live in a terminal, so naturally I started to think of ways to easily send emails with these reports attached. I’m stuck using Microsoft Entourage here at work, but thankfully they have a pretty robust AppleScript library. Mixing some stuff together I pulled together this Ruby script which will allow me to do some neat stuff:

  • pipe data into the email command to create a body
  • use aliases as groups to send emails to common groups of people, etc
  • add multiple attachments to emails straight from the command line!
  • not have to move my hands from the keyboard when sending emails!

At its core, it is nothing more than a simple ruby shell script which compiles AppleScript and uses osascript to execute the AppleScript. When run it will pop open a new Entourage mail window with the To/CC/Subject filled out, as well as any attachments and moves focus to the window. If I had piped anything to STDIN that would be the body, otherwise it would blank and ready for a quick message. Then a quick CMD-ENTER and off the mail goes. Simple and elegant, yet kind of powerful :)

#!/usr/local/bin/ruby

# Mail script for sending stuff from the command line through entourage
# which makes life super easy for sending files, or piping files to a mail body, etc.
# 
#   email -t bosses -c tracker -a ~/report.csv <SUBJECT HERE>
# 
# Author:: Rob Hurring
# Date:: 2011-11-15
# Version:: 1.0a

require 'optparse'
require 'fcntl'

# aliases for users, can be a string or array here 
UserAliases = {
  'bosses'  => %w{boss1@example.com boss2@example.com boss3@example.com},
  'me'      => 'me@example.com',
  'tracker' => 'tracker-script@example.com'
}

# compiles all addresses and maps our aliases
def compile_addresses(addresses)
  addresses.inject([]) do |all, address|
    address = UserAliases[address] if UserAliases.keys.include?(address)
    all |= Array(address)
  end.join(', ')
end

# Parse our options

options = {:to => [], :cc => [], :attachments => []}
OptionParser.new do |opts|
  opts.banner = "Usage: #{$0} [options] "
  opts.on('-t', '--to=TO', 'To'){ |t| options[:to] |= t.split(',') }
  opts.on('-c', '--cc=CC', 'CC'){ |c| options[:cc] |= c.split(',') }
  opts.on('-a', '--attach=ATTACHMENT', 'Attach'){ |a| options[:attachments] << File.expand_path(a) }
  opts.on_tail("-h", "--help", "Show this message") do
    puts opts
    exit 1
  end
end.parse!

# Get our subject and body (if STDIN is being used)
# Note: we DONT prompt for STDIN if nothing is piped in - it will just
#   open a blank email with the rest filled out

subject = ARGV.join(' ') || ''
body = STDIN.read if STDIN.fcntl(Fcntl::F_GETFL, 0) == 0
body.gsub!(/"/, "\\\"") unless body.nil?

# Compile script

to = compile_addresses(options[:to])
cc = compile_addresses(options[:cc])
attachments = options[:attachments].inject([]) do |all, attachment|
  attachment.gsub! /\//,':'
  all << %|make new attachment at msg with properties {file:"#{attachment}"}|
end.join("\n")

script =<<-EOS
tell application "Microsoft Entourage"
  activate
  set msg to make new draft window with properties {subject:"#{subject}", content:"#{body}", to recipients:"#{to}", cc recipients:"#{cc}"}
  #{attachments}
end tell
EOS

# Run Script

`osascript -e '#{script}' 2>/dev/null`
# email your bosses some reports
email -t bosses -a reports.tar.gz "Reports for you"

# email your tracker with some bad news
tail -n10 error_log|email -t tracker -a screenshot.png "BUG: something ut-ohd"

View Gist