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:ダウンロード