TranslatAIRのTweet機能をXAuth対応する(準備編)の続きです。
ActionScriptのOAuthライブラリや、twitterのAPIライブラリを使おうかとも思ったのですが、コールするAPIは以下の2つだけなので、今回は自前で作ることにしました。
- https://api.twitter.com/oauth/access_token(アクセストークンを取得する)
- http://twitter.com/statuses/update.json(ステータスを更新する)
※ 後者は前から使ってる翻訳結果をtweetするAPIです。
ライブラリのソースコードはかなり参考にさせていただきました。
q-oauth – Project Hosting on Google Code
oauth-as3 – Project Hosting on Google Code
などです。
特にq-outhというライブラリの方には、それをコールするサンプルアプリのソースコードも入っていましたので、非常に参考になりました。
パラメータで必要になってくる署名を生成するところには、ライブラリを利用しています。
as3crypto – Project Hosting on Google Codeというライブラリです。
まずは、アクセストークンを取得します。
●HTTPリクエスト
- POST
●URL
- https://api.twitter.com/oauth/access_token
●パラメータ
- oauth_consumer_key:twitter登録時に発行されるコンシューマキー
- oauth_nonce:ユニークID
- oauth_signature_method:HMAC-SHA1固定
- oauth_timestamp:現在時刻のタイムスタンプ
- oauth_version:1.0固定
- x_auth_mode:client_auth固定
- x_auth_username:twitterアカウント
- x_auth_password:twitterパスワード
- oauth_signature:署名
※ 署名を生成する部分は後述します。
●応答結果
- oauth_token:アクセストークン
- oauth_token_secret:署名キー
- user_id:twitter側で管理しているユーザID(だと思います)
- screen_name:表示名
- x_auth_expires:トークンの有効期限
※ このうち上2つを使います。
※ 有効期限は0が返ってくるので無期限になります。
やりかたはいろいろあると思いますが、私はまず、XAuthでのみ使用する情報をまとめるモデルクラスを作りました。
ソースコードはこんな感じです。
※ 6、7行目のキーはアプリケーション登録時に発行されるものに読み替えてください。
package { public class XAuthModel { private static var access_token_url:String = "https://api.twitter.com/oauth/access_token" private static var consumer_key:String = "Consumer key"; private static var consumer_secret:String = "Consumer secret"; private var _status:String; private var oauth_token:String; private var oauth_token_secret:String; private var oauth_signature:String; private var _authorized:Boolean; public function XAuthModel() { _status = ""; oauth_token = ""; oauth_token_secret = ""; oauth_signature = ""; _authorized = false; } public function get AccessTokenUrl():String { return access_token_url; } public function set Status(status:String):void { _status = status; } public function get Status():String { return _status; } public function set OAuthSignature(signature:String):void { oauth_signature = signature; } public function get OAuthSignature():String { return oauth_signature; } public function set OAuthToken(token:String):void { oauth_token = token; } public function get OAuthToken():String { return oauth_token; } public function set OAuthTokenSecret(secret:String):void { oauth_token_secret = secret; } public function get OAuthTokenSecret():String { return oauth_token_secret; } public function get ConsumerKey():String { return consumer_key; } public function get ConsumerSecret():String { return consumer_secret; } public function set isAuthorized(authorized:Boolean):void { _authorized = authorized; } public function get isAuthorized():Boolean { return _authorized; } } }
アクセストークンを取得する部分はこんな感じです(ちょっと長いですが)。
※ 49、50行目のusername、passwordは、適宜置き換えてください。
package { import com.adobe.serialization.json.JSON; import com.hurlant.crypto.Crypto; import com.hurlant.crypto.hash.HMAC; import com.hurlant.util.Base64; import com.hurlant.util.Hex; import flash.net.URLVariables; import flash.utils.ByteArray; import mx.rpc.events.FaultEvent; import mx.rpc.events.ResultEvent; import mx.rpc.http.HTTPService; import mx.utils.UIDUtil; public class TwitterManager { private var _model:XAuthModel; public function TwitterManager() { } public function initTwitterManager(model:XAuthModel):void { _model = model; } public function Tweet(message:String):void { if (!_model.isAuthorized) { getAuthToken(message); } else { //setStatus(message); } } private function getAuthToken(status:String):void { _model.Status = status; var requestTokenService:HTTPService = new HTTPService(); requestTokenService.method = "POST"; requestTokenService.url = _model.AccessTokenUrl; requestTokenService.addEventListener(ResultEvent.RESULT, onAuthTokenResult); requestTokenService.addEventListener(FaultEvent.FAULT, onAuthTokenFault); var forms:URLVariables = new URLVariables(); forms.x_auth_mode = "client_auth"; forms.x_auth_password = "password"; forms.x_auth_username = "username"; forms.oauth_signature = getSignature(requestTokenService.method, requestTokenService.url, forms); requestTokenService.request = forms; requestTokenService.send(); } private function getSignature(method:String, url:String, forms:URLVariables):String { var now:Date = new Date(); forms.oauth_consumer_key = _model.ConsumerKey; forms.oauth_nonce = UIDUtil.getUID(now); forms.oauth_signature_method = "HMAC-SHA1"; forms.oauth_timestamp = now.time.toString().substring(0, 10); forms.oauth_version = "1.0"; if (_model.OAuthToken.length) { forms.oauth_token = _model.OAuthToken; } var sortArr:Array = UrlVariableToArray(forms); var sigBase:String = URLEncoding.encode(method) + "&" + URLEncoding.encode(url) + "&" + URLEncoding.encode(sortArr.join("&")); var sigkeybase:String = URLEncoding.encode(_model.ConsumerSecret) + "&"; if (_model.OAuthTokenSecret.length){ sigkeybase += URLEncoding.encode(_model.OAuthTokenSecret); } var hmac:HMAC = Crypto.getHMAC("sha1"); var sig_key:ByteArray = Hex.toArray(Hex.fromString(sigkeybase)); var data:ByteArray = Hex.toArray(Hex.fromString(sigBase)); var signature:String = Base64.encodeByteArray(hmac.compute(sig_key, data)); return signature; } private function UrlVariableToArray(variables:URLVariables):Array{ var arr:Array = new Array(); for(var key:String in variables){ arr.push(key + "=" + URLEncoding.encode(variables[key])); } arr.sort(); return arr; } private function onAuthTokenResult(event:ResultEvent):void { var results:Array = event.result.toString().split("&"); for each (var token:String in results) { if (_model.OAuthToken == "" || _model.OAuthTokenSecret == "") { var values:Array = token.split("="); if (values.length == 2) { if (values[0] == "oauth_token") { _model.OAuthToken = values[1]; } else if (values[0] == "oauth_token_secret") { _model.OAuthTokenSecret = values[1]; } } } else { break; } } _model.isAuthorized = true; } private function onAuthTokenFault(event:FaultEvent):void { trace("投稿失敗...n" + event.fault.toString()); } } }
57~83行目のgetSignature関数は署名を生成する処理です。
SHA1方式のハッシュ値を署名として、パラメータに付加します。
●対象となるテキスト
- HTTPリクエスト:POST
- URL:コールするAPIのURL
- パラメータ:POSTするパラメータのキーと値を「&」ですべて繋げたもの
これらをURLエンコードして、さらに「&」で繋げます。
●署名キー
- consumer_secret
Extended UTF-8 in OAuth ActionScript library
こちらのソースコードを、ものすごく参考にさせていただきました!
Twitter API Wiki / Twitter REST API Method: oauth access_token for xAuthには、署名の対象となるテキストの一例があります。こんな感じになります。
Example Signature Base String:
POST&https%3A%2F%2Fapi.twitter.com%2Foauth%2Faccess_token&oauth_consumer_key%3Dri8JxYK2ZdwSV5xIUfNNvQ%26oauth_nonce%3DqfQ4ux5qRH9GaH8tVwDCwInLy6z8snR6wiq8lKcD6s%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1267817662%26oauth_version%3D1.0%26x_auth_mode%3Dclient_auth%26x_auth_password%3Dxyz12242134%26x_auth_username%3Depisod
翻訳結果をtweetするところも基本的には同じですが、不要なパラメータと追加するパラメータ、追加する署名キーがあります。
●不要なパラメータ
- x_auth_mode
- x_auth_username
- x_auth_password
※ アクセストークンが取得できているので、これらを再度送る必要はありません。
●追加するパラメータ
- oauth_token
●追加する署名キー
- oauth_token_secret
※ このキーを追加するのはgetSignature関数に実装されています。
tweetするところは、こんな感じです。
private function setStatus(status:String):void { var updateService:HTTPService = new HTTPService; updateService.method = "POST"; updateService.url = "http://twitter.com/statuses/update.json"; updateService.resultFormat = "text"; updateService.addEventListener(ResultEvent.RESULT, onResult); updateService.addEventListener(FaultEvent.FAULT, onFault); var forms:URLVariables = new URLVariables(); forms.status = status; forms.oauth_signature = getSignature(updateService.method, updateService.url, forms); updateService.request = forms; updateService.send(); } private function onResult(event:ResultEvent):void { var json:Object = JSON.decode(event.result.toString()); trace(json.text); } private function onFault(event:FaultEvent):void { trace("投稿失敗...n" + event.fault.toString()); }
このソースコードを上のTwitterManagerクラスに付け足し、setStatus関数をコールするコードを、onAuthTokenResult(コールバック)関数の最後に付け足せば、XAuthで認証した流れで、翻訳結果をtweetすることが出来るようになります。
ということで、XAuth対応したTranslatAIR、是非使ってみてください!
サンプルソース
- TranslatorSample8:ダウンロード