windup robotTo pick the winners for the Yoink and Listary giveaways, I wrote an AppleScript to make the process completely random and as painless as possible for me. I still have plans for making a similar WordPress plugin, but that’s still in the planning stages. For now, this is working quite well and I thought I’d share it. I doubt many people have the exact same setup and requirements, but the basic structure might be helpful for other projects.

It’s designed to work with promo code giveaways. So far, that’s what my giveaways have been. If I end up running a giveaway with a different registration method, I’ll extend the script and post an update for anyone interested.

This method currently has two requirements/limitations based on my own needs and setup. First, it only works with Mail.app for collecting entries and sending notifications. Second, the regular expressions it uses to find the entrants name, email and IP address are based on the standard WordPress comment notification emails. The basic idea here could easily be applied to other AppleScript-able email applications and modified to parse any format, but I’ll leave that up to intrepid readers for now.

How it works

First, I do a search for the subject line in Mail. All of the comment notifications that WordPress sends me for the post have the same subject line, so that gathers them all very quickly. All that matters is that I can select them all before running the script.

Next, I run the AppleScript. It begins by asking me for the giveaway app’s name and requests my list of promo codes. Then the script pulls the names and the message content from each selected email, passing the information to a Ruby script which scans the content for the actual email address (necessary because the actual sender on the email is always my WordPress address) and entrant’s name/handle and IP. The list of results are then passed to a second Ruby script along with the promo codes. This script checks for duplicates in the emails and IPs, picks enough random numbers (range 0 - number of entrants) to match each code provided (recursive function to avoid duplicate numbers). The Ruby script then opens a new message in Mail.app for each winner, and populates it with recipient, subject, congratulatory message (including code) and signature. It also adds the mailto: link with all of this info to an HTML file on the Desktop for reference.

It needs some improvements, especially in the mail creation routine. It currently uses the mailto AppleScript command, but really should create the outgoing message step by step, which would ultimately allow for complete automation at some point. For the time being I still want to be able to confirm the winners and message content anyway; I’ll automate more when I have full trust in it.

The AppleScript

(* 
PickWinners by Brett Terpstra, 2011, freely distributed
Picks random winners from selected WordPress comment notification emails in Mail.app
Checks for duplicate emails and IP addresses
On run, requests Application title and a newline-separated list of promo codes
Picks winners from list for each promo code and opens ready-to-send notification emails in Mail.app
Saves list of winners in HTML file with mailto links (including message and promo code)
*)

set pickWinnerScript to (path to me) & "Contents:Resources:Scripts:pickwinners.rb" as string
set extractEmailScript to (path to me) & "Contents:Resources:Scripts:extractemail.rb" as string
property winners : ""
set _res to display dialog "Application name?" default answer ""
set appname to text returned of _res
set _res to display dialog "Enter promo codes" default answer "Paste codes, one per line"
set codes to text returned of _res

tell application "Mail"
	set _sel to the selection
	set entries to {}
	
	repeat with _msg in _sel
		set nameAndEmail to do shell script "\"" & (POSIX path of extractEmailScript) & "\" \"" & content of _msg & "\""
		set end of entries to nameAndEmail
	end repeat
	
	set {astid, AppleScript's text item delimiters} to {AppleScript's text item delimiters, "
"}
	set emails to entries as string
	set AppleScript's text item delimiters to astid
	
	set winners to do shell script "\"" & (POSIX path of pickWinnerScript) & "\"  \"" & appname & "\" \"" & codes & "\" \"" & emails & "\""
end tell

if winners is "error" then
	display dialog "There was an error generating winners"
else
	set _btn to display dialog "Winners written to " & winners buttons {"OK", "Open in Safari"}
	if button returned of _btn is "Open in Safari" then
		tell application "Safari" to open (POSIX file winners) as alias
	end if
end if

extractemail.rb

#!/usr/bin/env ruby

input = ARGV.join(" ")
emailmatch = input.match(/E-mail : (.*)/)
namematch = input.match(/Author : (.*?) \(IP: (.*?)\)/)

print "#{namematch[1].strip} <#{emailmatch[1].strip}> (#{namematch[2].strip})"

pickwinners.rb

#!/usr/bin/env ruby
require 'ftools'

appname = ARGV[0]
ARGV.shift
codes = ARGV[0].split
count = codes.length
ARGV.shift
input = ARGV[0].split("\n").uniq

norepeat_email = []
norepeat_IP = []
entries = []

input.each {|entry|
  name,email,ip = entry.scan(/^(.*?) *<([^ ]+)> \((.*?)\)/)[0]
  unless (norepeat_email.include?(email) || norepeat_IP.include?(ip))
    norepeat_email.push(email)
    norepeat_IP.push(ip)
    entries << { 'name' => name, 'email' => email }
  end
}

def pick_number(max,numbers)
  num = rand(max)
  if numbers.include? num
    pick_number(max,numbers)
  else
    return num
  end
end

randoms = []
count.times do
  randoms.push(pick_number(entries.length,randoms))
end

winners = []
randoms.each_with_index {|winner,i|
  entry = entries[winner]
  name = entry['name']
  email = entry['email']
  mailto = %Q{mailto:#{email}?subject=Congratulations #{name}, you won!&body=Here is your promo code for #{appname}: #{codes[i]}%0A%0AThanks for reading!%0A%0A-Brett}
  link = %Q{<li><a href="#{mailto}">#{name} <#{email}></a></li>}
  winners.push(link)
  %x{echo 'tell application "Mail" to mailto "#{mailto.gsub(/'/,"\'")}"'|osascript}
}

if winners.length == count
  output = "<ul>"
  output += winners.join("\n")
  output += "</ul>"

  outfile = File.new(File.expand_path("~/Desktop/#{appname}Winners.html"),'w+')
  outfile.puts output
  outfile.close
  puts File.expand_path("~/Desktop/#{appname}Winners.html")
else
  puts "error"
end

It’s not the most elegant solution, but it does the job of ensuring randomness and handling the nitty gritty parts of running a contest.