Send SMS with Google Voice

Home / Send SMS with Google Voice

As I’m moving more code that I’ve written in the past to my blog, I remembered I posted this code a long while on CodePaste. It allows you to send an SMS text message via your Google Voice account.

It’s a simple bit of code that’s authenticating via your Google account and then getting the proper auth codes in order to send the text message.


Honestly, I don’t know for sure if it still works since Google has been making changes to their authentication services and such, but here’s the code for posterity’s sake.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Collections.Specialized;
using System.IO;
using System.Text.RegularExpressions;
using System.Web.Script.Serialization;
using System.Dynamic;
using System.Collections;
using System.Collections.ObjectModel;

namespace SMSTest
{
    class Program
    {
        static void Main(string[] args)
        {
            string authUrl = @"https://www.google.com/accounts/ClientLogin";
            string authVoiceUrl = @"https://www.google.com/voice";
            string smsUrl = @"https://www.google.com/voice/sms/send/";

            try
            {
                // First, let's get the auth code - requires Google credentials
                StringBuilder sb = new StringBuilder();
                NameValueCollection kvp = new NameValueCollection();
                kvp.Add("accountType", "GOOGLE");
                kvp.Add("Email", "your email here");
                kvp.Add("Passwd", "your password here");
                kvp.Add("service", "grandcentral");
                kvp.Add("source", "long2know.com-test-1.0");

                foreach (string key in kvp.Keys)
                {
                    sb.Append(string.Format("{0}={1}&", key, kvp[key]));
                }

                ASCIIEncoding encoding = new ASCIIEncoding();
                byte[] data = encoding.GetBytes(sb.ToString());

                HttpWebRequest myRequest = (HttpWebRequest)WebRequest.Create(authUrl);
                myRequest.Method = "POST";
                myRequest.ContentType = "application/x-www-form-urlencoded";
                myRequest.ContentLength = data.Length;
                Stream newStream = myRequest.GetRequestStream();
                newStream.Write(data, 0, data.Length);
                newStream.Close();

                StreamReader sr = new StreamReader(myRequest.GetResponse().GetResponseStream());
                string readResponse = sr.ReadToEnd();

                // Parse SID, Auth, LSID
                Regex regAuth = new Regex(@"Auth=(.+)", RegexOptions.IgnoreCase);
                Regex regSid = new Regex(@"SID=(.+)", RegexOptions.IgnoreCase);
                Regex regLsid = new Regex(@"LSID=(.+)", RegexOptions.IgnoreCase);
                Regex regRnrse = new Regex(@"'_rnr_se': '([^']+)'", RegexOptions.IgnoreCase);

                Match matchAuth = regAuth.Match(readResponse);
                Match matchSid = regSid.Match(readResponse);
                Match matchLsid = regLsid.Match(readResponse);

                string auth = string.Empty;
                string sid = string.Empty;
                string lsid = string.Empty;
                string rnrse = string.Empty;

                if (matchAuth.Success && matchSid.Success && matchLsid.Success)
                {
                    auth = matchAuth.Groups[0].Value;
                    sid = matchSid.Groups[0].Value;
                    lsid = matchLsid.Groups[0].Value;
                }
                else
                {
                    throw new WebException("Could not authenticate.", new Exception("Failed to retrieve auth, sid, or lsid"));
                }

                // Now, we need to get the rnr_se code
                myRequest = (HttpWebRequest)WebRequest.Create(authVoiceUrl);
                myRequest.Headers.Add(HttpRequestHeader.Authorization, string.Format("GoogleLogin {0}", auth));

                sr = new StreamReader(myRequest.GetResponse().GetResponseStream());
                readResponse = sr.ReadToEnd();

                Match matchRnrse = regRnrse.Match(readResponse);

                if (matchRnrse.Success)
                {
                    rnrse = matchRnrse.Groups[1].Value;
                }
                else
                {
                    throw new WebException("Could not authenticate.", new Exception("Failed to retrieve rnr_se"));
                }

                // Finally, let's send the message
                sb = new StringBuilder();
                kvp = new NameValueCollection();
                kvp.Add("_rnr_se", System.Web.HttpUtility.UrlEncode(rnrse));
                kvp.Add("phoneNumber", "18885551010"); // country code + area code + phone number (international notation)
                kvp.Add("text", System.Web.HttpUtility.UrlEncode("Here is my test SMS a console app!"));
                kvp.Add("id", "");

                foreach (string key in kvp.Keys)
                {
                    sb.Append(string.Format("{0}={1}&", key, kvp[key]));
                }

                data = encoding.GetBytes(sb.ToString());

                myRequest = (HttpWebRequest)WebRequest.Create(smsUrl);
                myRequest.Headers.Add(HttpRequestHeader.Authorization, string.Format("GoogleLogin {0}", auth));
                myRequest.Method = "POST";
                myRequest.ContentType = "application/x-www-form-urlencoded";
                myRequest.ContentLength = data.Length;
                newStream = myRequest.GetRequestStream();
                newStream.Write(data, 0, data.Length);
                newStream.Close();

                sr = new StreamReader(myRequest.GetResponse().GetResponseStream());
                readResponse = sr.ReadToEnd();

                // Deserialize into a dynamic type - uses DynamicJsonConverter (courtesy of this post:  http://stackoverflow.com/questions/3142495/deserialize-json-into-c-sharp-dynamic-object)
                var serializer = new JavaScriptSerializer();
                serializer.RegisterConverters(new[] { new DynamicJsonConverter() });

                dynamic resp = serializer.Deserialize(readResponse, typeof(object));

                // Alternatively, you use a simple dictionary dynamic type
                //JavaScriptSerializer js = new JavaScriptSerializer();
                //var json = js.Deserialize<dynamic>(readResponse);

                if (resp.ok)
                {
                    Console.WriteLine("Message was sent successfully.");
                }
                else
                {
                    Console.WriteLine("Message was not sent successfully.");
                }


            }
            catch (WebException e)
            {
                Console.WriteLine(string.Format("Web Exception: {0}", e.InnerException));
            }

        }
    }

    public class DynamicJsonConverter : JavaScriptConverter
    {
        public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
        {
            if (dictionary == null)
                throw new ArgumentNullException("dictionary");

            return type == typeof(object) ? new DynamicJsonObject(dictionary) : null;
        }

        public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
        {
            throw new NotImplementedException();
        }

        public override IEnumerable<Type> SupportedTypes
        {
            get { return new ReadOnlyCollection<Type>(new List<Type>(new[] { typeof(object) })); }
        }

        #region Nested type: DynamicJsonObject

        private sealed class DynamicJsonObject : DynamicObject
        {
            private readonly IDictionary<string, object> _dictionary;

            public DynamicJsonObject(IDictionary<string, object> dictionary)
            {
                if (dictionary == null)
                    throw new ArgumentNullException("dictionary");
                _dictionary = dictionary;
            }

            public override string ToString()
            {
                var sb = new StringBuilder("{");
                ToString(sb);
                return sb.ToString();
            }

            private void ToString(StringBuilder sb)
            {
                var firstInDictionary = true;
                foreach (var pair in _dictionary)
                {
                    if (!firstInDictionary)
                        sb.Append(",");
                    firstInDictionary = false;
                    var value = pair.Value;
                    var name = pair.Key;
                    if (value is string)
                    {
                        sb.AppendFormat("{0}:\"{1}\"", name, value);
                    }
                    else if (value is IDictionary<string, object>)
                    {
                        new DynamicJsonObject((IDictionary<string, object>)value).ToString(sb);
                    }
                    else if (value is ArrayList)
                    {
                        sb.Append(name + ":[");
                        var firstInArray = true;
                        foreach (var arrayValue in (ArrayList)value)
                        {
                            if (!firstInArray)
                                sb.Append(",");
                            firstInArray = false;
                            if (arrayValue is IDictionary<string, object>)
                                new DynamicJsonObject((IDictionary<string, object>)arrayValue).ToString(sb);
                            else if (arrayValue is string)
                                sb.AppendFormat("\"{0}\"", arrayValue);
                            else
                                sb.AppendFormat("{0}", arrayValue);

                        }
                        sb.Append("]");
                    }
                    else
                    {
                        sb.AppendFormat("{0}:{1}", name, value);
                    }
                }
                sb.Append("}");
            }

            public override bool TryGetMember(GetMemberBinder binder, out object result)
            {
                if (!_dictionary.TryGetValue(binder.Name, out result))
                {
                    // return null to avoid exception.  caller can check for null this way...
                    result = null;
                    return true;
                }

                var dictionary = result as IDictionary<string, object>;
                if (dictionary != null)
                {
                    result = new DynamicJsonObject(dictionary);
                    return true;
                }

                var arrayList = result as ArrayList;
                if (arrayList != null && arrayList.Count > 0)
                {
                    if (arrayList[0] is IDictionary<string, object>)
                        result = new List<object>(arrayList.Cast<IDictionary<string, object>>().Select(x => new DynamicJsonObject(x)));
                    else
                        result = new List<object>(arrayList.Cast<object>());
                }

                return true;
            }
        }

        #endregion
    }
}

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.