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 } }