I wanted to be able to post tweets from a Powershell script. There are alot of old blog posts and examples of composing tweets before Twitter introduced oAuth. I've seen a few examples of Powershell scripts that can post tweets using external dll's to handle the oAuth authentication. I wanted to keep my script self contained and only use .NET Framework assemblies where necessary and not rely on 3rd party dlls.
The first step is to create a Twitter application by logging on to http://dev.twitter.com. Complete the Application Details making sure to set the Application Type - Access to at least Read and Write. Make a note of the following:
- Consumer key
- Consumer secret
- Access token
- Access token secret
You'll need to update the associated Powershell script variables with your real values.
[Reflection.Assembly]::LoadWithPartialName("System.Security") [Reflection.Assembly]::LoadWithPartialName("System.Net") $status = [System.Uri]::EscapeDataString("My first tweet from Powershell"); $oauth_consumer_key = "<consumer key>"; $oauth_consumer_secret = "<consumer secret>"; $oauth_token = "<auth token>"; $oauth_token_secret = "<auth token secret>"; $oauth_nonce = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes([System.DateTime]::Now.Ticks.ToString())); $ts = [System.DateTime]::UtcNow - [System.DateTime]::ParseExact("01/01/1970", "dd/MM/yyyy", $null).ToUniversalTime(); $oauth_timestamp = [System.Convert]::ToInt64($ts.TotalSeconds).ToString(); $signature = "POST&"; $signature += [System.Uri]::EscapeDataString("http://api.twitter.com/1/statuses/update.json") + "&"; $signature += [System.Uri]::EscapeDataString("oauth_consumer_key=" + $oauth_consumer_key + "&"); $signature += [System.Uri]::EscapeDataString("oauth_nonce=" + $oauth_nonce + "&"); $signature += [System.Uri]::EscapeDataString("oauth_signature_method=HMAC-SHA1&"); $signature += [System.Uri]::EscapeDataString("oauth_timestamp=" + $oauth_timestamp + "&"); $signature += [System.Uri]::EscapeDataString("oauth_token=" + $oauth_token + "&"); $signature += [System.Uri]::EscapeDataString("oauth_version=1.0&"); $signature += [System.Uri]::EscapeDataString("status=" + $status); $signature_key = [System.Uri]::EscapeDataString($oauth_consumer_secret) + "&" + [System.Uri]::EscapeDataString($oauth_token_secret); $hmacsha1 = new-object System.Security.Cryptography.HMACSHA1; $hmacsha1.Key = [System.Text.Encoding]::ASCII.GetBytes($signature_key); $oauth_signature = [System.Convert]::ToBase64String($hmacsha1.ComputeHash([System.Text.Encoding]::ASCII.GetBytes($signature))); $oauth_authorization = 'OAuth '; $oauth_authorization += 'oauth_consumer_key="' + [System.Uri]::EscapeDataString($oauth_consumer_key) + '",'; $oauth_authorization += 'oauth_nonce="' + [System.Uri]::EscapeDataString($oauth_nonce) + '",'; $oauth_authorization += 'oauth_signature="' + [System.Uri]::EscapeDataString($oauth_signature) + '",'; $oauth_authorization += 'oauth_signature_method="HMAC-SHA1",' $oauth_authorization += 'oauth_timestamp="' + [System.Uri]::EscapeDataString($oauth_timestamp) + '",' $oauth_authorization += 'oauth_token="' + [System.Uri]::EscapeDataString($oauth_token) + '",'; $oauth_authorization += 'oauth_version="1.0"'; $post_body = [System.Text.Encoding]::ASCII.GetBytes("status=" + $status); [System.Net.HttpWebRequest] $request = [System.Net.WebRequest]::Create("http://api.twitter.com/1/statuses/update.json"); $request.Method = "POST"; $request.Headers.Add("Authorization", $oauth_authorization); $request.ContentType = "application/x-www-form-urlencoded"; $body = $request.GetRequestStream(); $body.write($post_body, 0, $post_body.length); $body.flush(); $body.close(); $response = $request.GetResponse();
14 comments:
Great post...
but it's not working. To test, I'm using the script unmodified (except for the consumer and token key/secret strings).
I'm getting the response, "The remote server returned an error: (401) Unauthorized."
Any thoughts?
Rick
Hi Rick,
Are you using a proxy server ?
Cheers,
Ian.
Hello Ian!
Thank you this works really fine for me !
Peter Kriegel
http://www.admin-source.de
See you on Twitter ;-)
Great script !
One remark. I had a problem with $ts
Be careful, when use it. This part might be tricky, when you have Cultureinfo other than 'en-US'
You can replace $null with $culture
and $culture=New-Object System.Globalization.CultureInfo("en-US")
Should work ..
I get a return {"request":"\/1\/statuses\/update.json","error":"Timestamp out of bounds"} when I run the script. I'm in eastern timezone.
Has anyone else had this problem?
I found my problem. I seeing {"request":"\/1\/statuses\/update.json","error":"Timestamp out of bounds"} when I was troubleshooting with a program called Fiddler. I changed $oauth_timestamp to epoch time from http://www.epochconverter.com/ and was able to tweet successfully. Maybe twitter just takes Epoch time with no offset?
This is an excellent post. Thanks for sharing!
Zach
Doesn't work - error:
Exception calling "GetResponse" with "0" argument(s): "The remote server returned an error: (401) Unauthorized."
At line:47 char:1
+ $response = $request.GetResponse();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : WebException
This still works for v1.1 of the twitter api.
You just need to change the urls.
I've tried this on two different accounts now. Keep getting the 401 errors.
I made the second account with the twitter permissions set to read/write/direct with sign-on allowed. I copypasta'd your code right into PowerShell+ and updated the tokens. I've even gotten it to work by calling the curl request generated on the OAuth tool that creates a self-contained post and authorization strings, and that worked. So I can only assume it's a code issue at this point.
Going to post it to the dev site and maybe I'll get some idea from there. Will post back if I do.
Hi All, figured out the issue it does deal with time, what I noticed from the epoch site http://www.epochconverter.com/ was that my time was incorrect or off and I figured it was just a GMT issue and found that the command .touninversaltime was subtracting another 8 hours (PST) (28800 sec) which created the time offset. So to fix it just remove that portion of the code. $ts = [System.DateTime]::UtcNow - [System.DateTime]::ParseExact("01/01/1970", "dd/MM/yyyy", $null);#.ToUniversalTime();
Sorry not to be confusing just remove .ToUniversalTime(); from that line of code.
Could someone care to share their working code and setup with the 1.1 version of the API?
Hi! To the folks getting 401 errors - make sure you have made your app read write.
If you originally created a read only app you can't just change the settings - you have to regenerate the keys. Worth making a new app from scratch as a troubleshooting step.
My thanks to Ian for the great code.
Check this simple ps script that can access any of the v 1.1 TwitterAPIs including the Streaming api...
https://github.com/MeshkDevs/InvokeTwitterAPIs
Post a Comment