I needed a script send an SMS today, and I found a very handy post at sudocode to send one via Google Voice, using PHP. I wanted to make it a little more command-line-friendly, so I rewrote it (ham-handedly) in Ruby and added some options parsing to it. It’s designed for — and only tested on — OS X, but may work fine elsewhere.
The code
To use it, copy the code below into a text file, save it as voicesms.rb (or download it directly using the link at the top of the code), and run chmod a+x voicesms.rb
from the command line to make it executable.
```ruby
#!/usr/bin/env ruby -Ku
require 'net/http'
require 'net/https'
require 'open-uri'
require 'cgi'
require 'optparse'
ACCOUNT = '' # Set to Google Voice account email for default account
PASSWORD = '' # Set to Google Voice account password for default account
NUMBERS = ['+1555444333','+1555444222'] # Set one or more numbers for default destination(s)
options = {}
optparse = OptionParser.new do|opts|
opts.banner = "Usage: voicesms.rb -n +15554443333[,+15554442222] -m \"Message to send\" [-u Username:Password]"
options[:numbers] = NUMBERS
opts.on( '-n', '--numbers NUM[,NUM]', 'Phone numbers to SMS (separate multiples with comma)' ) do|numbers|
options[:numbers] = numbers.split(',')
end
options[:message] = false
opts.on( '-m', '--message MESSAGE', 'Message to send' ) do|msg|
options[:message] = msg
end
options[:username] = ACCOUNT
options[:password] = PASSWORD
opts.on( '-u', '--user USERNAME:PASSWORD', 'Google Voice username and password' ) do|creds|
parts = creds.split(':')
options[:username] = parts[0]
options[:password] = parts[1]
end
opts.on( '-h', '--help', 'Display this screen' ) do
puts opts
exit
end
end
optparse.parse!
unless options[:message]
puts "Message required. Use -m \"MESSAGE\"."
puts "Enter `voicesms.rb -h` for help."
exit
end
def postit(uri_str, data, header = nil, limit = 3)
raise ArgumentError, 'HTTP redirect too deep' if limit == 0
url = URI.parse(uri_str)
http = Net::HTTP.new(url.host,443)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
response,content = http.post(url.path,data,header)
case response
when Net::HTTPSuccess then content
when Net::HTTPRedirection then postit(response['location'],data,header, limit - 1)
else
puts response.inspect
response.error!
end
end
def getit(uri_str, header, limit = 3)
raise ArgumentError, 'HTTP redirect too deep' if limit == 0
url = URI.parse(uri_str)
http = Net::HTTP.new(url.host,url.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
response,content = http.get(url.path,header)
case response
when Net::HTTPSuccess then content
when Net::HTTPRedirection then getit(response['location'],header, limit - 1)
else
response.error!
end
end
data = "accountType=GOOGLE&Email=#{options[:username]}&Passwd=#{options[:password]}&service=grandcentral&source=brettterpstra-CLISMS-2.0"
res = postit('https://www.google.com/accounts/ClientLogin',data)
if res
authcode = res.match(/Auth=(.+)/)[1]
header = {'Authorization' => "GoogleLogin auth=#{authcode.strip}",'Content-Length' => '0'}
newres = getit('https://www.google.com/voice',header)
if newres
rnrse = newres.match(/'_rnr_se': '([^']+)'/)[1]
options[:numbers].each {|num|
data = "_rnr_se=#{rnrse}&phoneNumber=#{num.strip}&text=#{CGI.escape(options[:message])}&id="
finalres = postit('https://www.google.com/voice/sms/send/',data,header)
if finalres["ok"]
puts "Message sent to #{num}"
else
puts "Error sending to #{num}"
end
}
else
newres.error!
end
else
res.error!
end
```
Usage
You can set default account name, password and send-to numbers at the top of the script. These can be overridden by command line options at runtime. The standard syntax is:
voicesms.rb -n +15552525543,+15554342221 -m “The message to send” -u GVoiceUsername:GVoicePassword
Parameters can be in any order. The -n parameter takes one or more phone numbers in international format (+XX country code at the beginning, +1 for US numbers), separated by commas (no spaces).
The -m option (message) is required and can’t be set by default in the script. Just use -m “and a quoted message” and it will handle the rest.
-u defines a username/password combination for Google Voice. It’s probably most convenient to set these at the top of the script and ignore this parameter, but the option is there.
-h will provide the help information.
Aliasing
The point of the script is to let me automate SMS messages from my system, so its primary invocation will be from other scripts. However, it will function just fine as a command line texting utility, in which case you’d probably want to alias the core functions in your .bash_profile
. Set your default username and password in the script, and maybe a default destination number. If you want to send to a different numbers, you might want to make several aliases which include different -n #
parameters, one for each destination. The alias will look something like:
alias sms=”~/scripts/voicesms.rb -m”
Or, maybe:
alias smsjohn=”~/scripts/voicesms.rb -n +15554443333 -m”
Get it? Then you can just type smsjohn "And your message"
to send the message straight to John. Whoever that is.
What it does
It’s pretty simple, just a series of POST and GET requests to the Google API. It uses Google’s client authentication to get an initial auth code. Then it uses the auth code to get an auth token. Then, it posts your information to the API with the proper headers (the auth token) to complete the call. All of your data is sent securely over SSL (https) connections.
Have fun!