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!