{"id":1226,"date":"2009-06-03T20:16:13","date_gmt":"2009-06-03T11:16:13","guid":{"rendered":"https:\/\/regex.info\/blog\/lua\/twitter"},"modified":"2009-06-03T20:16:13","modified_gmt":"2009-06-03T11:16:13","slug":"twitter","status":"publish","type":"page","link":"https:\/\/regex.info\/blog\/lua\/twitter","title":{"rendered":"Twitter OAuth Authentication Routines in Lua, for Lightroom Plugins"},"content":{"rendered":"\n\n<div style='float:right; border:solid 1px #88F; margin: 0 0 20px 2em; padding: 15px 20px; background-color: #922'>\n<a href='\/blog\/lua'>More Lua code<\/a>\n<\/div>\n\n\n<p>Having added Twitter support to a bunch of <a\nhref='\/blog\/lightroom-goodies' class='quiet'>my Lightroom\nplugins<\/a>, I thought I'd go ahead and share the Lua routine that does the\n<a href='http:\/\/en.wikipedia.org\/wiki\/OAuth' class='quiet'>OAuth<\/a>\nauthentication.<\/p>\n\n<p>The OAuth concept seems snazzy, but wow, the documentation is scattered\nand it was difficult to pin down exactly what was required. It would have\nbeen nice had they just shown a simple example all the way through, so\nperhaps this code can serve as that (at least for the subset of OAuth that\nTwitter requires).<\/p>\n\n<p>This code depends on the <a href='\/blog\/lua\/sha1'\nclass='quiet'>SHA-1 and HMAC-SHA1<\/a> and <a\nhref='\/blog\/lua\/json'>JSON<\/a> routines I've also\nreleased, but unlike that code, is also dependent on the environment\nprovided to plugins by Lightroom.<\/p>\n\n\n<div style='margin: 60px auto 10px auto; width:40%; text-align:center; padding: 10px 0' class='bg-A'>\n\n<a href='\/code\/TwitterAuthentication.lua' class='quiet'>Download <b>TwitterAuthentication.lua<\/b><\/a>\n<br\/>Version 6: Jan 13, 2011\n<br\/>(handles request-header auth, all https)\n<\/div>\n\n<p style='margin:20px 0 60px 0; color:#F88'><span style='color:red'>Warning:<\/span> Version 6 is a huge update from\nthe previous release, to handle big changes at Twitter. I've used this code\nin my plugins for years, but have not vetted that all the changes work\noutside my own personal build environment. I hope it does.<\/p>\n\n<p>Here are some of the header docs, extracted from the file itself....<\/p>\n\n<div style='border-left: 3px solid #444; margin-left:1em; padding: 5px 1em; background-color:#222'>\n<p>The code exposes two public functions:<\/p>\n\n<pre>\n   Twitter_AuthenticateNewCredentials()\n   Twitter_SendTweet(credential_bundle, status_text)\n<\/pre>\n\n<p>The first leads the user through the procedure to grant your application\npermission to send tweets on their behalf. It returns a &#8220;credential\nbundle&#8221; (a Lua table) that can be cached locally (such as in the plugin\npreferences &mdash; see <tt>LrPrefs<\/tt>) and used for sending subsequent tweets\nforever, or until the user your application at Twitter unpermissions.<\/p>\n\n<p>For example, if you have <tt>TWITTER_CREDENTIALS<\/tt> in your\n<tt>exportPresetFields<\/tt> list (with its default set to <tt>nil<\/tt>) and <tt>P<\/tt> is the\nlocal copy of the property table for the plugin (e.g. as passed to\n<tt>sectionsForBottomOfDialog<\/tt>, you might have:<\/p>\n\n<pre style='font-size:95%'>\n  f:view {\n     bind_to_object = P,\n     place = 'overlapping',\n     fill_horizontal = 1,\n\n     f:static_text {\n        fill_horizontal = 1,\n        visible = LrBinding.keyIsNotNil 'TWITTER_CREDENTIALS',\n        LrView.bind {\n           key = 'TWITTER_CREDENTIALS',\n           transform = function(credentials)\n                          return LOC(\"$$$\/xxx=Authenticated to Twitter as @^1\",\n                                     credentials.screen_name)\n                       end\n        },\n     },\n     f:push_button {\n        visible = LrBinding.keyIsNil 'TWITTER_CREDENTIALS',\n        enabled = LrBinding.keyIsNotNil '_authenticating_at_twitter',\n        title   = \"Authenticate at Twitter\",\n        action  = function()\n                     LrFunctionContext.postAsyncTaskWithContext(\"authenticate at twitter\",\n                        function(context)\n                           context:addFailureHandler(function(status, error)\n                                                        LrDialogs.message(\"INTERNAL ERROR\", error, \"critical\")\n                                                     end)\n                           context:addCleanupHandler(function()\n                                                        _authenticating_at_twitter = nil\n                                                     end)\n                           _authenticating_at_twitter = true\n                           P.TWITTER_CREDENTIALS = Twitter_AuthenticateNewCredentials()\n                        end)\n                  end\n     }\n  }\n<\/pre>\n\n<p>and then later during export...<\/p>\n\n<pre style='font-size:95%'>\n  local P = exportContext.propertyTable\n\n  if P.TWITTER_CREDENTIALS then\n     local result = Twitter_SendTweet(P.TWITTER_CREDENTIALS,\n                                      \"I just did something with Lightroom!\")\n     if result == nil then\n        -- user has revoked permission, so we'll uncache the credential bundle\n        P.TWITTER_CREDENTIALS = nil\n     end\n  end\n<\/pre>\n\n<p>You will have to update the code to have it reference the specific\n<i>Consumer Key<\/i> and <i>Consumer Secret<\/i> granted to your application\nwhen you <a href='http:\/\/twitter.com\/oauth_clients\/new' class='quiet'>registered it at Twitter<\/a>.<\/p>\n\n<\/div>\n\n","protected":false},"excerpt":{"rendered":"<p>Having added Twitter support to a bunch of my Lightroom plugins, I thought I'd go ahead and share the Lua routine that does the OAuth authentication.<\/p> <p>The OAuth concept seems snazzy, but wow, the documentation is scattered and it was difficult to pin down exactly what was required. It would have been nice had they just shown a simple example all the way through, so perhaps this code can serve as that (at least for the subset of OAuth that Twitter requires).<\/p> <p>This code depends on the SHA-1 and HMAC-SHA1 and JSON routines I've also released, but unlike that code, [...]","protected":false},"author":1,"featured_media":0,"parent":1222,"menu_order":0,"comment_status":"open","ping_status":"open","template":"","meta":{"footnotes":""},"_links":{"self":[{"href":"https:\/\/regex.info\/blog\/wp-json\/wp\/v2\/pages\/1226"}],"collection":[{"href":"https:\/\/regex.info\/blog\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/regex.info\/blog\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/regex.info\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/regex.info\/blog\/wp-json\/wp\/v2\/comments?post=1226"}],"version-history":[{"count":0,"href":"https:\/\/regex.info\/blog\/wp-json\/wp\/v2\/pages\/1226\/revisions"}],"up":[{"embeddable":true,"href":"https:\/\/regex.info\/blog\/wp-json\/wp\/v2\/pages\/1222"}],"wp:attachment":[{"href":"https:\/\/regex.info\/blog\/wp-json\/wp\/v2\/media?parent=1226"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}