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