{"id":554,"date":"2016-12-16T13:18:36","date_gmt":"2016-12-16T12:18:36","guid":{"rendered":"https:\/\/solidt.eu\/site\/?p=554"},"modified":"2016-12-16T13:18:36","modified_gmt":"2016-12-16T12:18:36","slug":"c-simple-twitter","status":"publish","type":"post","link":"https:\/\/solidt.eu\/site\/c-simple-twitter\/","title":{"rendered":"C# Simple Twitter"},"content":{"rendered":"<pre lang=\"c#\">\r\n\r\nusing Newtonsoft.Json;\r\nusing Newtonsoft.Json.Linq;\r\nusing System;\r\nusing System.Collections.Generic;\r\nusing System.Globalization;\r\nusing System.IO;\r\nusing System.Linq;\r\nusing System.Net;\r\nusing System.Security.Cryptography;\r\nusing System.Text;\r\nusing System.Text.RegularExpressions;\r\n\r\nnamespace DCMSCode.DCMS\r\n{\r\n\tpublic class OAuthInfo\r\n\t{\r\n\t\tpublic string ConsumerKey { get; set; }\r\n\t\tpublic string ConsumerSecret { get; set; }\r\n\t\tpublic string AccessToken { get; set; }\r\n\t\tpublic string AccessSecret { get; set; }\r\n\t}\r\n\r\n    public static class TwitterUrls \r\n    {\r\n        public const string HomeTimeline = \"https:\/\/api.twitter.com\/1.1\/statuses\/home_timeline.json\";\r\n        public const string Mentions = \"https:\/\/api.twitter.com\/1.1\/statuses\/mentions.json\";\r\n        public const string UserTimeline = \"https:\/\/api.twitter.com\/1.1\/statuses\/user_timeline.json\";\r\n    }\r\n\r\n\tpublic class TinyTwitter\r\n\t{\r\n        public static string GetJTokenValue(JToken jtoken, string path) \r\n        {\r\n            return GetJTokenValue(String.Empty, jtoken, path);\r\n        }\r\n\r\n        public static string GetJTokenValue(string def, JToken jtoken, string path) \r\n        {\r\n            if (jtoken == null) { \r\n                return def;\r\n            } \r\n            else \r\n            {\r\n                var token = jtoken.SelectToken(path, false);\r\n                if (token == null) {\r\n                    return def;\r\n                } else {                    \r\n\t\t\t\t\treturn token.ToString();\r\n                }                \r\n            }\r\n        }\r\n\r\n        public static DateTime ParseTwitterDate(string twitterDate) {\r\n            DateTime date = DateTime.MinValue;\r\n            if (DateTime.TryParseExact(twitterDate, \"ddd MMM dd HH:mm:ss zz00 yyyy\", CultureInfo.InvariantCulture, DateTimeStyles.None, out date)) {\r\n                return date.ToLocalTime();\r\n            }\r\n            return date;\r\n        }\r\n\r\n\t\tprivate readonly OAuthInfo oauth;\r\n\r\n\t\tpublic TinyTwitter(OAuthInfo oauth)\r\n\t\t{\r\n\t\t\tthis.oauth = oauth;\r\n\t\t}\r\n\r\n\t\tpublic void UpdateStatus(string message)\r\n\t\t{\r\n\t\t\tnew RequestBuilder(oauth, \"POST\", \"https:\/\/api.twitter.com\/1.1\/statuses\/update.json\")\r\n\t\t\t\t.AddParameter(\"status\", message)\r\n\t\t\t\t.Execute();\r\n\t\t}\r\n\r\n\t\tpublic JToken GetTimeline(string url, string[,] args)\r\n\t\t{\r\n\t\t\tvar builder = new RequestBuilder(oauth, \"GET\", url);\r\n\r\n            for(int i=0;i<args.GetLength(0);i+=1) {                            \r\n                builder.AddParameter(args[i,0], args[i,1]);\r\n            }            \r\n\t\t\tvar responseContent = builder.Execute();\r\n            return JToken.Parse(responseContent);\r\n\t\t}\r\n\r\n\t\t#region RequestBuilder\r\n\r\n\t\tpublic class RequestBuilder\r\n\t\t{\r\n\t\t\tprivate const string VERSION = \"1.0\";\r\n\t\t\tprivate const string SIGNATURE_METHOD = \"HMAC-SHA1\";\r\n\r\n\t\t\tprivate readonly OAuthInfo oauth;\r\n\t\t\tprivate readonly string method;\r\n\t\t\tprivate readonly IDictionary<string, string> customParameters;\r\n\t\t\tprivate readonly string url;\r\n\r\n\t\t\tpublic RequestBuilder(OAuthInfo oauth, string method, string url)\r\n\t\t\t{\r\n\t\t\t\tthis.oauth = oauth;\r\n\t\t\t\tthis.method = method;\r\n\t\t\t\tthis.url = url;\r\n\t\t\t\tcustomParameters = new Dictionary<string, string>();\r\n\t\t\t}\r\n\r\n\t\t\tpublic RequestBuilder AddParameter(string name, string value)\r\n\t\t\t{\r\n\t\t\t\tcustomParameters.Add(name, value.EncodeRFC3986());\r\n\t\t\t\treturn this;\r\n\t\t\t}\r\n\r\n\t\t\tpublic string Execute()\r\n\t\t\t{\r\n\t\t\t\tvar timespan = GetTimestamp();\r\n\t\t\t\tvar nonce = CreateNonce();\r\n\r\n\t\t\t\tvar parameters = new Dictionary<string, string>(customParameters);\r\n\t\t\t\tAddOAuthParameters(parameters, timespan, nonce);\r\n\r\n\t\t\t\tvar signature = GenerateSignature(parameters);\r\n\t\t\t\tvar headerValue = GenerateAuthorizationHeaderValue(parameters, signature);\r\n\r\n\t\t\t\tvar request = (HttpWebRequest)WebRequest.Create(GetRequestUrl());\r\n\t\t\t\trequest.Method = method;\r\n\t\t\t\trequest.ContentType = \"application\/x-www-form-urlencoded\";\r\n\r\n\t\t\t\trequest.Headers.Add(\"Authorization\", headerValue);\r\n\r\n\t\t\t\tWriteRequestBody(request);\r\n\r\n\t\t\t\t\/\/ It looks like a bug in HttpWebRequest. It throws random TimeoutExceptions\r\n\t\t\t\t\/\/ after some requests. Abort the request seems to work. More info: \r\n\t\t\t\t\/\/ http:\/\/stackoverflow.com\/questions\/2252762\/getrequeststream-throws-timeout-exception-randomly\r\n\r\n\t\t\t\tvar response = request.GetResponse();\r\n\r\n\t\t\t\tstring content;\r\n\r\n\t\t\t\tusing (var stream = response.GetResponseStream())\r\n\t\t\t\t{\r\n\t\t\t\t\tusing (var reader = new StreamReader(stream))\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tcontent = reader.ReadToEnd();\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t\trequest.Abort();\r\n\r\n\t\t\t\treturn content;\r\n\t\t\t}\r\n\r\n\t\t\tprivate void WriteRequestBody(HttpWebRequest request)\r\n\t\t\t{\r\n\t\t\t\tif (method == \"GET\")\r\n\t\t\t\t\treturn;\r\n\r\n\t\t\t\tvar requestBody = Encoding.ASCII.GetBytes(GetCustomParametersString());\r\n\t\t\t\tusing (var stream = request.GetRequestStream())\r\n\t\t\t\t\tstream.Write(requestBody, 0, requestBody.Length);\r\n\t\t\t}\r\n\r\n\t\t\tprivate string GetRequestUrl()\r\n\t\t\t{\r\n\t\t\t\tif (method != \"GET\" || customParameters.Count == 0)\r\n\t\t\t\t\treturn url;\r\n\r\n\t\t\t\treturn string.Format(\"{0}?{1}\", url, GetCustomParametersString());\r\n\t\t\t}\r\n\r\n\t\t\tprivate string GetCustomParametersString()\r\n\t\t\t{\r\n\t\t\t\treturn customParameters.Select(x => string.Format(\"{0}={1}\", x.Key, x.Value)).Join(\"&\");\r\n\t\t\t}\r\n\r\n\t\t\tprivate string GenerateAuthorizationHeaderValue(IEnumerable<KeyValuePair<string, string>> parameters, string signature)\r\n\t\t\t{\r\n\t\t\t\treturn new StringBuilder(\"OAuth \")\r\n\t\t\t\t\t.Append(parameters.Concat(new KeyValuePair<string, string>(\"oauth_signature\", signature))\r\n\t\t\t\t\t\t\t\t.Where(x => x.Key.StartsWith(\"oauth_\"))\r\n\t\t\t\t\t\t\t\t.Select(x => string.Format(\"{0}=\\\"{1}\\\"\", x.Key, x.Value.EncodeRFC3986()))\r\n\t\t\t\t\t\t\t\t.Join(\",\"))\r\n\t\t\t\t\t.ToString();\r\n\t\t\t}\r\n\r\n\t\t\tprivate string GenerateSignature(IEnumerable<KeyValuePair<string, string>> parameters)\r\n\t\t\t{\r\n\t\t\t\tvar dataToSign = new StringBuilder()\r\n\t\t\t\t\t.Append(method).Append(\"&\")\r\n\t\t\t\t\t.Append(url.EncodeRFC3986()).Append(\"&\")\r\n\t\t\t\t\t.Append(parameters\r\n\t\t\t\t\t\t\t\t.OrderBy(x => x.Key)\r\n\t\t\t\t\t\t\t\t.Select(x => string.Format(\"{0}={1}\", x.Key, x.Value))\r\n\t\t\t\t\t\t\t\t.Join(\"&\")\r\n\t\t\t\t\t\t\t\t.EncodeRFC3986());\r\n\r\n\t\t\t\tvar signatureKey = string.Format(\"{0}&{1}\", oauth.ConsumerSecret.EncodeRFC3986(), oauth.AccessSecret.EncodeRFC3986());\r\n\t\t\t\tvar sha1 = new HMACSHA1(Encoding.ASCII.GetBytes(signatureKey));\r\n\r\n\t\t\t\tvar signatureBytes = sha1.ComputeHash(Encoding.ASCII.GetBytes(dataToSign.ToString()));\r\n\t\t\t\treturn Convert.ToBase64String(signatureBytes);\r\n\t\t\t}\r\n\r\n\t\t\tprivate void AddOAuthParameters(IDictionary<string, string> parameters, string timestamp, string nonce)\r\n\t\t\t{\r\n\t\t\t\tparameters.Add(\"oauth_version\", VERSION);\r\n\t\t\t\tparameters.Add(\"oauth_consumer_key\", oauth.ConsumerKey);\r\n\t\t\t\tparameters.Add(\"oauth_nonce\", nonce);\r\n\t\t\t\tparameters.Add(\"oauth_signature_method\", SIGNATURE_METHOD);\r\n\t\t\t\tparameters.Add(\"oauth_timestamp\", timestamp);\r\n\t\t\t\tparameters.Add(\"oauth_token\", oauth.AccessToken);\r\n\t\t\t}\r\n\r\n\t\t\tprivate static string GetTimestamp()\r\n\t\t\t{\r\n\t\t\t\treturn ((int)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds).ToString();\r\n\t\t\t}\r\n\r\n\t\t\tprivate static string CreateNonce()\r\n\t\t\t{\r\n\t\t\t\treturn new Random().Next(0x0000000, 0x7fffffff).ToString(\"X8\");\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t#endregion\r\n\t}\r\n\r\n\tpublic static class TinyTwitterHelperExtensions\r\n\t{\r\n\t\tpublic static string Join<T>(this IEnumerable<T> items, string separator)\r\n\t\t{\r\n\t\t\treturn string.Join(separator, items.ToArray());\r\n\t\t}\r\n\r\n\t\tpublic static IEnumerable<T> Concat<T>(this IEnumerable<T> items, T value)\r\n\t\t{\r\n\t\t\treturn items.Concat(new[] { value });\r\n\t\t}\r\n\r\n\t\tpublic static string EncodeRFC3986(this string value)\r\n\t\t{\r\n\t\t\t\/\/ From Twitterizer http:\/\/www.twitterizer.net\/\r\n\r\n\t\t\tif (string.IsNullOrEmpty(value))\r\n\t\t\t\treturn string.Empty;\r\n\r\n\t\t\tvar encoded = Uri.EscapeDataString(value);\r\n\r\n\t\t\treturn Regex\r\n\t\t\t\t.Replace(encoded, \"(%[0-9a-f][0-9a-f])\", c => c.Value.ToUpper())\r\n\t\t\t\t.Replace(\"(\", \"%28\")\r\n\t\t\t\t.Replace(\")\", \"%29\")\r\n\t\t\t\t.Replace(\"$\", \"%24\")\r\n\t\t\t\t.Replace(\"!\", \"%21\")\r\n\t\t\t\t.Replace(\"*\", \"%2A\")\r\n\t\t\t\t.Replace(\"'\", \"%27\")\r\n\t\t\t\t.Replace(\"%7E\", \"~\");\r\n\t\t}\r\n\t}\r\n}\r\n\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Net; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; namespace DCMSCode.DCMS { public class OAuthInfo { public string ConsumerKey { get; set; } public string ConsumerSecret { get; set; } public string AccessToken { get; set; } public string AccessSecret { get; [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false,"footnotes":""},"categories":[6],"tags":[],"class_list":["post-554","post","type-post","status-publish","format-standard","hentry","category-dotnet"],"_links":{"self":[{"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/posts\/554","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/comments?post=554"}],"version-history":[{"count":1,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/posts\/554\/revisions"}],"predecessor-version":[{"id":555,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/posts\/554\/revisions\/555"}],"wp:attachment":[{"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/media?parent=554"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/categories?post=554"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/solidt.eu\/site\/wp-json\/wp\/v2\/tags?post=554"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}