I am trying to get a logging class that posts items to Twitter. I have created an app. Here’s the code, but when I try to run, I get a 401 Unauthorized response. Can someone tell me if I am doing something wrong?
public class TwitterLogger : BitFactory.Logging.Logger
{
const string TweetUrl = "http://api.twitter.com/1/statuses/update.json";
public TwitterLogger()
{
}
public TwitterLogger(string accessToken, string accessTokenSecret, string consumerKey, string consumerSecret)
{
AccessToken = accessToken;
AccessTokenSecret = accessTokenSecret;
ConsumerKey = consumerKey;
ConsumerSecret = consumerSecret;
}
public string AccessToken
{
get; set;
}
public string AccessTokenSecret
{
get; set;
}
public string ConsumerKey
{
get; set;
}
public string ConsumerSecret
{
get; set;
}
protected string GetOAuthUrlEncode(string aValue)
{
var newValue = HttpUtility.UrlEncode(aValue).Replace("+", "%20");
// UrlEncode escapes with lowercase characters (e.g. %2f) but oAuth needs %2F
newValue = System.Text.RegularExpressions.Regex.Replace(newValue, "(%[0-9a-f][0-9a-f])", c => c.Value.ToUpper());
// these characters are not escaped by UrlEncode() but needed to be escaped
newValue = newValue.Replace("(", "%28").Replace(")", "%29").Replace("$", "%24").Replace("!", "%21").Replace("*", "%2A").Replace("'", "%27");
// these characters are escaped by UrlEncode() but will fail if unescaped!
newValue = newValue.Replace("%7E", "~");
return newValue;
}
protected string GetTimeStamp()
{
TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
return Convert.ToInt64(ts.TotalSeconds).ToString();
}
protected string GetNonce()
{
return Guid.NewGuid().ToString();
}
protected string GetOAuthBaseString(string anHttpMethod, string aBaseUri, SortedDictionary<string, string> someParameters)
{
var paramStrings = new List<String>();
foreach (string key in someParameters.Keys)
paramStrings.Add(key + "=" + someParameters[key]);
return anHttpMethod.ToUpper() + "&" + GetOAuthUrlEncode(aBaseUri) + "&" + GetOAuthUrlEncode(String.Join("&", paramStrings.ToArray()));
}
protected string GetAuthSignature(SortedDictionary<string, string> someParameters)
{
var hmacsha1 = new System.Security.Cryptography.HMACSHA1(Encoding.ASCII.GetBytes(ConsumerSecret + "&" + AccessTokenSecret));
var bytes = hmacsha1.ComputeHash(Encoding.ASCII.GetBytes(GetOAuthBaseString("POST", TweetUrl, someParameters)));
return Convert.ToBase64String(bytes);
}
protected SortedDictionary<string, string> GetOAuthParameters()
{
return new SortedDictionary<string, string>()
{
{ "oauth_nonce", GetNonce() },
{ "oauth_consumer_key", ConsumerKey },
{ "oauth_signature_method", "HMAC-SHA1" },
{ "oauth_timestamp", GetTimeStamp() },
{ "oauth_token", AccessToken },
{ "oauth_version", "1.0" },
//{ "oauth_signature", GetAuthSignature() }, // add later in process
};
}
protected string GetOAuthString(SortedDictionary<string, string> baseParameters, SortedDictionary<string, string> additionalParameters)
{
var sb = new StringBuilder();
sb.Append("OAuth ");
var paramList = new List<string>();
foreach (string key in baseParameters.Keys)
paramList.Add(key + "=" + "\"" + baseParameters[key] + "\"");
sb.Append(string.Join(",", paramList.ToArray()));
var allParameters = new SortedDictionary<string, string>(baseParameters);
foreach (KeyValuePair<string, string> kv in additionalParameters)
allParameters.Add(kv.Key, kv.Value);
sb.Append(",oauth_signature=\"" + GetOAuthUrlEncode(GetAuthSignature(allParameters)) + "\"");
return sb.ToString();
}
protected override bool DoLog(LogEntry aLogEntry)
{
// without this, you may receive a '417 Expectation Failed' error
ServicePointManager.Expect100Continue = false;
using (WebClient wClient = new WebClient())
{
var parameters = GetOAuthParameters();
var additionalParameters = new SortedDictionary<string, string>() { { "status", GetOAuthUrlEncode(aLogEntry.Message) } };
var theString = GetOAuthString(parameters, additionalParameters);
wClient.Headers.Add("Authorization", theString);
var nvc = new NameValueCollection();
nvc["status"] = aLogEntry.Message;
wClient.UploadValues(TweetUrl, nvc);
}
return true;
}
}