Created
June 8, 2015 20:33
-
-
Save kashmervil/be2297485cf0ad74f427 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.IO; | |
using System.Linq; | |
using System.Net; | |
using System.Security.Cryptography; | |
using System.Text; | |
using System.Text.RegularExpressions; | |
namespace tweet | |
{ | |
public class OAuthInfo | |
{ | |
public string ConsumerKey { get; set; } | |
public string ConsumerSecret { get; set; } | |
public string AccessToken { get; set; } | |
public string AccessSecret { get; set; } | |
} | |
public class Twitter | |
{ | |
private readonly OAuthInfo _oauth; | |
public Twitter(OAuthInfo oauth) | |
{ | |
_oauth = oauth; | |
} | |
public string UpdateStatus(string message) | |
{ | |
var web = new RequestBuilder(_oauth, "POST", "https://api.twitter.com/1.1/statuses/update.json") | |
.AddParameter("status", message) | |
.Execute(); | |
return web; | |
} | |
public string UpdateStatuswithmedia(string message, string media) | |
{ | |
var web = new RequestBuilder(_oauth, "POST", "https://api.twitter.com/1.1/statuses/update.json") | |
.AddParameter("status", message) | |
.AddParameter("media_ids", media) | |
.Execute(); | |
return web; | |
} | |
public string UpdateMedia(string message) | |
{ | |
var web = new RequestBuilder(_oauth, "POST", "https://upload.twitter.com/1.1/media/upload.json") | |
.AddParameter("media", message) | |
.Execute(); | |
return web; | |
} | |
#region RequestBuilder | |
public class RequestBuilder | |
{ | |
private const string VERSION = "1.0"; | |
private const string SIGNATURE_METHOD = "HMAC-SHA1"; | |
private readonly OAuthInfo _oauth; | |
private readonly string _method; | |
private readonly IDictionary<string, string> _customParameters; | |
private readonly string _url; | |
public RequestBuilder(OAuthInfo oauth, string method, string url) | |
{ | |
_oauth = oauth; | |
_method = method; | |
_url = url; | |
_customParameters = new Dictionary<string, string>(); | |
} | |
public RequestBuilder AddParameter(string name, string value) | |
{ | |
_customParameters.Add(name, value.EscapeUriDataStringRfc3986()); | |
return this; | |
} | |
public string Execute() | |
{ | |
var content = ""; | |
try | |
{ | |
var timespan = GetTimestamp(); | |
var nonce = CreateNonce(); | |
var parameters = new Dictionary<string, string>(_customParameters); | |
AddOAuthParameters(parameters, timespan, nonce); | |
var signature = GenerateSignature(parameters); | |
var headerValue = GenerateAuthorizationHeaderValue(parameters, signature); | |
var request = WebRequest.Create(GetRequestUrl()); | |
request.Method = _method; | |
request.ContentType = "application/x-www-form-urlencoded"; | |
request.Headers.Add("Authorization", headerValue); | |
WriteRequestBody(request); | |
var response = request.GetResponse(); | |
using (var stream = response.GetResponseStream()) | |
{ | |
if (stream != null) | |
using (var reader = new StreamReader(stream)) | |
{ | |
content = reader.ReadToEnd(); | |
} | |
} | |
request.Abort(); | |
return content; | |
} | |
catch (Exception ex) | |
{ | |
Console.WriteLine("Error: {0}", ex.Message); | |
return null; | |
} | |
} | |
private void WriteRequestBody(WebRequest request) | |
{ | |
if (_method == "GET") | |
return; | |
var requestBody = Encoding.ASCII.GetBytes(GetCustomParametersString()); | |
using (var stream = request.GetRequestStream()) | |
stream.Write(requestBody, 0, requestBody.Length); | |
} | |
private string GetRequestUrl() | |
{ | |
if (_method != "GET" || _customParameters.Count == 0) | |
return _url; | |
return string.Format("{0}?{1}", _url, GetCustomParametersString()); | |
} | |
private string GetCustomParametersString() | |
{ | |
return _customParameters.Select(x => string.Format("{0}={1}", x.Key, x.Value)).Join("&"); | |
} | |
private string GenerateAuthorizationHeaderValue(IEnumerable<KeyValuePair<string, string>> parameters, | |
string signature) | |
{ | |
return new StringBuilder("OAuth ") | |
.Append(parameters.Concat(new KeyValuePair<string, string>("oauth_signature", signature)) | |
.Where(x => x.Key.StartsWith("oauth_")) | |
.Select(x => string.Format("{0}=\"{1}\"", x.Key, x.Value.EscapeUriDataStringRfc3986())) | |
.Join(",")) | |
.ToString(); | |
} | |
private string GenerateSignature(IEnumerable<KeyValuePair<string, string>> parameters) | |
{ | |
var dataToSign = new StringBuilder() | |
.Append(_method).Append("&") | |
.Append(_url.EscapeUriDataStringRfc3986()).Append("&") | |
.Append(parameters | |
.OrderBy(x => x.Key) | |
.Select(x => string.Format("{0}={1}", x.Key, x.Value)) | |
.Join("&") | |
.EscapeUriDataStringRfc3986()); | |
var signatureKey = string.Format("{0}&{1}", _oauth.ConsumerSecret.EscapeUriDataStringRfc3986(), | |
_oauth.AccessSecret.EscapeUriDataStringRfc3986()); | |
var sha1 = new HMACSHA1(Encoding.ASCII.GetBytes(signatureKey)); | |
var signatureBytes = sha1.ComputeHash(Encoding.ASCII.GetBytes(dataToSign.ToString())); | |
return Convert.ToBase64String(signatureBytes); | |
} | |
private void AddOAuthParameters(IDictionary<string, string> parameters, string timestamp, string nonce) | |
{ | |
parameters.Add("oauth_version", VERSION); | |
parameters.Add("oauth_consumer_key", _oauth.ConsumerKey); | |
parameters.Add("oauth_nonce", nonce); | |
parameters.Add("oauth_signature_method", SIGNATURE_METHOD); | |
parameters.Add("oauth_timestamp", timestamp); | |
parameters.Add("oauth_token", _oauth.AccessToken); | |
} | |
private static string GetTimestamp() | |
{ | |
return ((int) (DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds).ToString(); | |
} | |
private static string CreateNonce() | |
{ | |
return new Random().Next(0x0000000, 0x7fffffff).ToString("X8"); | |
} | |
} | |
#endregion | |
} | |
public static class TinyTwitterHelperExtensions | |
{ | |
public static string Join<T>(this IEnumerable<T> items, string separator) | |
{ | |
return string.Join(separator, items.ToArray()); | |
} | |
public static IEnumerable<T> Concat<T>(this IEnumerable<T> items, T value) | |
{ | |
return items.Concat(new[] {value}); | |
} | |
public static string EncodeRFC3986(this string value) | |
{ | |
// From Twitterizer http://www.twitterizer.net/ | |
if (string.IsNullOrEmpty(value)) | |
return string.Empty; | |
var encoded = Uri.EscapeDataString(value); | |
return Regex | |
.Replace(encoded, "(%[0-9a-f][0-9a-f])", c => c.Value.ToUpper()) | |
.Replace("(", "%28") | |
.Replace(")", "%29") | |
.Replace("$", "%24") | |
.Replace("!", "%21") | |
.Replace("*", "%2A") | |
.Replace("'", "%27") | |
.Replace("%7E", "~"); | |
} | |
public static string EscapeUriDataStringRfc3986(this string value) | |
{ | |
var escaped = new StringBuilder(); | |
const string validChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-._~"; | |
foreach (var c in value) | |
{ | |
if (validChars.Contains(c.ToString())) | |
{ | |
escaped.Append(c); | |
} | |
else | |
{ | |
escaped.Append("%" + Convert.ToByte(c).ToString("x2").ToUpper()); | |
} | |
} | |
// Return the fully-RFC3986-escaped string. | |
return escaped.ToString(); | |
} | |
} | |
internal static class TwitterSettings | |
{ | |
public static string CredentialsPath = ".twitter_credentials"; | |
public static OAuthInfo OAuthInfo = null; | |
} | |
public static class Program | |
{ | |
public static void Main(string[] args) | |
{ | |
if (args.Length != 2) | |
{ | |
Console.WriteLine("Error: Wrong parameters"); | |
PrintUsage(); | |
return; | |
} | |
if (!File.Exists(TwitterSettings.CredentialsPath)) | |
{ | |
File.CreateText(TwitterSettings.CredentialsPath); | |
Console.WriteLine("Error: Credentials file {0} is empty. Unable to authenticate", TwitterSettings.CredentialsPath); | |
PrintUsage(); | |
return; | |
} | |
var credentials = File.ReadAllLines(TwitterSettings.CredentialsPath); | |
if (credentials.Length != 4) | |
{ | |
Console.WriteLine("Error: Credentials file {0} is in invalid format.", | |
TwitterSettings.CredentialsPath); | |
PrintUsage(); | |
return; | |
} | |
TwitterSettings.OAuthInfo = new OAuthInfo | |
{ | |
ConsumerKey = credentials[0], | |
ConsumerSecret = credentials[1], | |
AccessToken = credentials[2], | |
AccessSecret = credentials[3] | |
};; | |
var twitter = new Twitter(TwitterSettings.OAuthInfo); | |
var photoPath = args[0]; | |
if (!File.Exists(photoPath)) | |
{ | |
Console.WriteLine("Error: PhotoPath doesn't exist"); | |
PrintUsage(); | |
return; | |
} | |
Console.WriteLine("Uploading media file..."); | |
var jsonResponse = twitter.UpdateMedia(Convert.ToBase64String(File.ReadAllBytes(args[0]))); | |
if (jsonResponse == null) | |
{ | |
Console.WriteLine("Failed to upload an image to twitter"); | |
return; | |
} | |
//Typical representation is {"media_id":0000,"media_id_string": ... | |
//We are interested in media_id. So... | |
var start = jsonResponse.IndexOf(":", StringComparison.Ordinal) + 1; | |
var size = jsonResponse.IndexOf(",", StringComparison.Ordinal) - start; | |
var mediaId = jsonResponse.Substring(start, size); | |
Console.WriteLine("New image id is received"); | |
Console.WriteLine("Sending tweet..."); | |
twitter.UpdateStatuswithmedia(args[1], mediaId); | |
Console.WriteLine("Successfully tweeted!"); | |
} | |
private static void PrintUsage() | |
{ | |
var usage = new StringBuilder().Append("Tweet usage:\n") | |
.Append(" tweet \"path_to_photo\" \"tweet text\"\n") | |
.AppendFormat("\nCredentials file '{0}' should be placed in the same folder as TwitterUploader.exe\n", | |
TwitterSettings.CredentialsPath) | |
.Append( | |
"Credentials file format: \n ConsumerKey\n ConsumerKeySecret\n AccessToken\n AccessTokenSecret"); | |
Console.WriteLine(usage.ToString()); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment