Automating random giveaways, v1.0
To 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.