Archive for the ‘Twitter’ Category

7月
06

Android Market公開を目指してAndroidアプリを開発する!(翻訳機能実装編)の続きです。

今回は、翻訳結果をTweetする機能を実装していきたいと思います。

先日、TranslatAIRを対応させましたが、認証にはXAuthを採用していますので、今回もそうすることにします。
TranslatAIRのTweet機能をXAuth対応する(準備編)
TranslatAIRのTweet機能をXAuth対応する(実装編)

まず、新たにモデルクラスを2つ作りました。
XAuthに使う情報を集約したクラスとTweetする際に必要になる情報を集約したクラスです。

●XAuthModelクラス

package jp.flashcast.translator.android.model;

public class XAuthModel {
	public static final String consumer_key = "xxxxxxxxxxxxxxxxxxxxxx";
	public static final String consumer_secret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
	private String oauth_token;
	private String oauth_token_secret;

	public XAuthModel() {
		oauth_token = "";
		oauth_token_secret = "";
	}

	public void setOAuthToken(String oauth_token) {
		this.oauth_token = oauth_token;
	}

	public String getOAuthToken() {
		return oauth_token;
	}

	public void setOAuthTokenSecret(String oauth_token_secret) {
		this.oauth_token_secret = oauth_token_secret;
	}

	public String getOAuthTokenSecret() {
		return oauth_token_secret;
	}
}

※ 4、5行目は適宜変更してください。

●TweetModelクラス

package jp.flashcast.translator.android.model;

public class TweetModel {
	private String account;
	private String password;
	private String status;
	private boolean success;

	public TweetModel() {
		account = "xxxxxxxxxx";
		password = "xxxxxxxxxx";
		status = "";
	}
	public void setAccount(String account) {
		this.account = account;
	}

	public String getAccount() {
		return account;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public String getPassword() {
		return password;
	}

	public void setStatus(String status) {
		this.status = status;
	}

	public String getStatus() {
		return status;
	}

	public void setSuccess(boolean success) {
		this.success = success;
	}

	public boolean isSuccess() {
		return success;
	}
}

※ 10、11行目は適宜変更してください。

次に、XAuthで認証して、翻訳結果をTweetする部分です。

こちらも、前回のgoogle翻訳API呼び出しと同じように、Threadクラスを継承した別クラスとして実装しました。ソースをすべてご紹介すると、ずいぶん長くなってしまいますので割愛しますが、要注意事項をご紹介したいと思います。

XAuthの部分がある程度できた時点でテストしてみたのですが、なぜかHttpステータス417が返ってきました。

Expectation Failed 417

今まで、417って、ほとんど目にしたことがありませんでした。

417 Expectation Failed
期待するヘッダに失敗。その拡張はレスポンスできず、またはプロキシサーバが次に到達するサーバがレスポンスできないと判断している。
具体例として、Expect:ヘッダに100-continue以外の変なものを入れた場合や、そもそもサーバが100 Continueが扱えない場合に返す。

HTTPステータスコード – Wikipediaより引用。

なるほどなるほど~

Expect:100-continue

ってなんだろう?

Expect リクエストヘッダフィールドは、特定のサーバの振る舞いがクライアントによって要求されている事を示すために使われる。

リクエスト中のExpect フィールドにある期待値{expectation values} を理解できない、あるいはそれに従えないサーバは、適切なエラーステータスを返さなければならない。サーバは、その期待{expectations} に一つでも添えない場合は、417 (Expectation Failed) ステータスを返さなければならないが、そのリクエストが他に問題を持つような場合などは、他の 4xx ステータスを返してもよい。

このヘッダフィールドは、将来の拡張によって許される拡張された構文によって定義される。もし、サーバがサポートしていない expectation-extension を含んだ Expect フィールドを持つリクエストを受けた時は、417 (Expectation Failed) ステータスを返さなければならない。

どうやらHttpヘッダーのExpectフィールドには、クラアントが希望する拡張構文が指定できるようです。
※ 「100-continue」は現時点で定められている唯一の拡張構文。

100 (Continue) ステータス (section 10.1.1 参照) は、オリジンサーバがクライアントがリクエストボディを送る前に (リクエストヘッダに基づいた) リクエストを受け入れようとする場合に、リクエストボディを伴ったリクエストメッセージを送る事をクライアントに決めさせるという目的を持つ。いくつかのケースでは、サーバがボディを見る事も無くメッセージを受けつけていない場合にクライアントがボディを送る事は、不適切でひどく効率が悪くなる事がある。

[Studying HTTP] HTTP Status Codeより引用。

そういうこと!

以下のような、やり取りがされるのだと解釈しました。

●Expect:100-continueが扱え、かつ、リクエストヘッダーに問題がない場合
クライアント:「これからこんな感じのデータ送るけど大丈夫?とりあえず、ヘッダーだけ送るので見てみて。」
※ Expect:100-continueを指定。ヘッダーのみを送信。

サーバ:「これなら大丈夫だよ。本文も送って。」

クライアント:「送りまーす。」

●Expect:100-continueは扱えるが、リクエストヘッダーに問題がある場合
クライアント:「これからこんな感じのデータ送るけど大丈夫?とりあえず、ヘッダーだけ送るので見てみて。」
※ Expect:100-continueを指定。ヘッダーのみを送信。

サーバ:「これ無理。処理できないよ。」

クライアント:「了解。じゃあ、本文送るのやめるね。」

●Expect:100-continueが扱えない場合
クライアント:「これからこんな感じのデータ送るけど大丈夫?とりあえず、ヘッダーだけ送るので見てみて。」
※ Expect:100-continueを指定。ヘッダーのみを送信。

サーバ:「いやいや、ヘッダーだけ送られても判断できないよ。」
※ このときHttpステータス417を返す。

クライアント:「あ、そうなんだ。ごめん。」

どうやら、twitterでは、Expect:100-continueを扱えないようです。

では、このエラーを回避するにはどうしたらよいか?
Expect:100-continueをHttpヘッダーに指定しなければ良いのです。

今回、twitterのAPIを呼び出すところでは、

	private HttpClient client;

	public TweetThread(Handler handler, Runnable runnable, TweetModel tmodel, XAuthModel xmodel) {
		this.handler = handler;
		this.runnable = runnable;
		this.tmodel = tmodel;
		this.xmodel = xmodel;
		this.client = new DefaultHttpClient();
	}

のように、「HttpClient」クラスを利用しています。
この場合、デフォルトで指定されるようです。

		client.getParams().setParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE, false);

こうすることで、HttpヘッダーにExpect:100-continueを指定しないようにすることができます。これで、エラーも発生しなくなりました。

未実装だった、Tweetボタンクリック時の処理を実装します。

        btnTweet.setOnClickListener(new OnClickListener() {

			public void onClick(View v) {
				if (!gmodel.getTranslated().equals("")) {
					tweeting = true;

					if (tmodel == null) {
						tmodel = new TweetModel();
					}
					if (xmodel == null) {
						xmodel = new XAuthModel();
					}
					tmodel.setStatus(gmodel.getTranslated());

					Tweet();
				}
			}

        });

これで、翻訳結果をtweetすることができるようになりました!

tweeted

サンプルソース

2010年7月8日追記
申し訳ありません。上記サンプルソースに一部バグがありました。
twitterの投稿で、単語であれば問題ないのですが、英文のようにスペースが混在している文章を投稿しようとするとエラーになる、というものでした。対応版をアップしましたので、あらためてこちらからどうぞ。

6月
07

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
但し、ActionScriptの関数、encodeURIComponentではエンコードできない文字があるようです(「!」「*」とか)。
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、是非使ってみてください!

サンプルソース

5月
30

google翻訳APIを使ったAIRアプリを作る!(Twitterに投稿編)でtwitterのAPIでサポートされている認証は、OAuth方式とBASIC認証と書きました。

Twitter API 仕様書 第二十版 (2009年4月16日版) より引用(2009/12/8時点の最新版です)。

どうやら、OAuth認証とBASIC認証をサポートしているようです。
とりあえずBASIC認証を使うことにします。

現在はXAuth方式もサポートされています。
ですが、BASIC認証が2010年6月30日に廃止されます。

以前リリースした簡易翻訳AIRアプリケーション、TranslatAIRが翻訳結果をtweetするという機能を持っていますが、BASIC認証を採用しているので、利用する認証方式を変更したいと思います。

@twitterapi’s countdown to Basic Auth removalでBASIC認証廃止へのカウントダウンが開始されています。

OAuthとは?

簡潔に説明するのはむずかしいので、参考サイトをご紹介します。
APIアクセス権を委譲するプロトコル、OAuthを知る - @IT

さらに調べていると、図付きでわかりやすいサイトを見つけました。あわせてこちらもどうぞ。
※ twitterの例ではありませんが、概念は同じです。パラメータも一部twitterのものと異なります。
Yahoo!デベロッパーネットワーク – OAuth – フロー

XAuthとは?

OAuthの簡易版といったところでしょうか。
Yahoo!デベロッパーネットワーク – OAuth – フローにある、処理フローの図で言うところの「3.Access Tokenの取得」のみで認証します。
※ 付加するパラメータはOAuthのものとは異なります。

OAuthでは、サービスプロバイダの同意画面を表示させるためにブラウザが立ち上がりますが、ブラウザが起動できない、起動したくない、または、ブラウザを起動するのがふさわしくないような場合、XAuthを使います。

一番の違いは、ID、パスワードの取り扱いです。OAuthの場合は、サービスプロバイダ側に預けるのに対し、XAuthの場合は、それを実装しているクライアント側(コンシューマ)に預けることになります。つまり、クライアントに預けたID、パスワードを悪用される危険性が全くないとは言えないということです。これは、XAuthのデメリットであり、クライアントを使うユーザに対する脅威ですので、開発するにあたっては、このリスクを十分理解しないといけません。

とはいうものの、一般的には、WebアプリではOAuthを、デスクトップアプリではXAuthを使うという感じで、使い分けられているようです。

今回はXAuth方式を採用したいと思います。

実装前の準備

前置きが長くなりましたが、今回はtwitterでXAuth方式を使う際の下準備についてまとめたいと思います。

1.アプリケーション登録申請
Create cool applications! | dev.twitter.comにアクセスし、「Register an app」を選択します。

top

twitterのアカウントでログインします。

login

アプリケーション登録申請画面が開きますので、項目を入力します。

app

「アプリケーションを登録する」ボタンをクリックすると、同意画面が表示されます。

agree

「I Accept」を選択します。

translatair

アプリケーションの登録申請が完了しました!

2.メールで申請
ちょこちょこっとサンプルコードを書いて試していたところ、以下のようなエラーが出て、一向に進みません。

[RPC Fault faultString="HTTP 要求に関するエラー" faultCode="Server.Error.Request" faultDetail="エラー : [IOErrorEvent type="ioError" bubbles=false cancelable=false eventPhase=2 text="Error #2032: Stream Error. URL: https://api.twitter.com/oauth/access_token" errorID=2032]. URL: https://api.twitter.com/oauth/access_token”]

Twitter API Wiki / Twitter REST API Method: oauth access_token for xAuthをよく読むと、以下のようにありました。

In order to get access to this method, you must apply by sending an email to api@twitter.com ― all other applications will receive an HTTP 401 error. Web-based applications will not be granted access, except on a temporary basis for when they are converting from basic-authentication support to full OAuth support.

OAuth方式を使用する場合は必要ないようなのですが、XAuth方式を利用する際にはメールでapi@twitter.com宛てに申請して、許可をもらわないといけないようです。ちょっとだけはまりました。要注意です。

以下のような内容でメールしました。

タイトル:access_token for xAuth
本文:Hi. My name is mojyamojya in twitter. Please my application to use the xauth. The application name is TranslatAIR.

以前は、何度もやり取りしないとなかなか許可がもらえず、とても手間がかかったようですが、私の場合、サクッと解決しました。メールしたのは深夜だったのですが、翌朝には「XAuth、もう使えるようになってるよ」的な返事が来ていました。ラッキーです。

以上が、デスクトップアプリケーションがtwitterのAPIコール時にXAuth方式を利用するまでに必要な下準備です。実装編は、次回ご紹介したいと思います。

12月
21

やっと、アプリが完成したので、リリースしましたっ!

名前は、TranslatAIRです。翻訳するAIRアプリということで、translateとAIRを合体してつくった造語です。

基本機能は、今までブログに書いてきたものと同等ですが、いくつか機能を追加しました。

  1. 翻訳結果を表示する機能。
  2. 任意の文章を翻訳する機能。
  3. 翻訳履歴を保存・閲覧する機能。
  4. 設定変更機能
  5. クリップボードの中身を翻訳結果に差し替える機能。

などです。

1は説明するのが難しいのですが、「小さい画面にじわっと浮き出て、じわっと消えていく」と言った感じでしょうか。

info1info2info3info4info5

2、3、4に関しては各画面を用意しました。

翻訳画面

翻訳画面

翻訳履歴画面

翻訳履歴画面

設定変更画面

設定変更画面

これらの画面を呼び出すためのメニューを、アイコンのところに実装しました。

menu

5のクリップボードの中身を翻訳結果に差し替える機能は、その名前の通りで、翻訳後にペーストすると、中身が翻訳されているといった感じになります。

と、こんなところでしょうか。
詳しくはこちらをご覧ください。

以外に便利だと思いますので、是非、使ってみてください!

TranslatAIR

12月
08

flashcast:フリーで働くITエンジニア集団のブログ: google翻訳APIを使ったAIRアプリを作る!(意図しない翻訳を避ける編)の続きです。

基本機能はある程度できつつあります。

ただ、翻訳するだけのアプリではなんとなく物足りないので、何か付加価値のある機能をつけたいと思います。そこで考えたのが、翻訳結果をtwitterに投稿する機能です!

まずは、仕様を決めたいと思います。

翻訳部分の仕様は、以下のようになっています。

翻訳処理を実行する前に、ユーザの簡単なアクションを待つような仕組みにしたいと思います。

  • クリップボードの値が変わったときに、WindowsのタスクトレイやMacのドックのアイコンをアニメーションさせる。
  • アニメーションしている間にアイコンがクリック(ユーザの簡単なアクション)されたことをトリガーに翻訳処理を実行する。
  • アイコンがクリックされた後は、アイコンのアニメーションを停止する。

flashcast:フリーで働くITエンジニア集団のブログ: google翻訳APIを使ったAIRアプリを作る!(意図しない翻訳を避ける編)より引用。

これに加えて、以下のような仕様を追加したいと思います。

  • 正常に翻訳結果を得られたら、一度停止したアイコンを、再度アニメーションさせる。
  • 翻訳時と同じようにアニメーションしている間に、アイコンがクリックされたことをトリガーにtwitterに翻訳結果を投稿する。
  • アイコンがクリックされた後は、アイコンのアニメーションを停止する。

まとめると、、、
クリップボードに文字列をコピーしてから、twitterに翻訳結果が投稿されるまで、合計2回してアイコンが回転します。1回目の回転中にアイコンをクリックすると翻訳され、2回目の回転中にアイコンをクリックすると、twitterに翻訳結果が投稿されることになります。

※ ただ、アイコンをクリックしないと回転は停止しないため、twitterに翻訳結果を投稿しないと、次の翻訳はできないので不便です。その点は、今後、改善していきたいと思います。

まずは、twitterのAPI仕様を調べます。

[観] Twitter API 仕様書 (勝手に日本語訳シリーズ)が非常に参考になります。有志でこういったことをしてもらえると本当に助かります。

twitterは登録制なので、APIを利用するにあたっても、当然認証が必要になります。

public_timeline の取得等一部の API を除くほとんどの API で、認証を使用する。応答に protected なユーザに関する情報が含まれる可能性のある API は認証が必須となっている。
現在、OAuth認証とBASIC認証が使用可能。

Twitter API 仕様書 第二十版 (2009年4月16日版) より引用(2009/12/8時点の最新版です)。

どうやら、OAuth認証とBASIC認証をサポートしているようです。
とりあえずBASIC認証を使うことにします。

それから、つぶやきを投稿するAPIはupdateというもののようです。

update
自分のステータスを更新(update)する。引数 status は必須。
この API は必ず POST を使って発行すること。update が成功した場合は、format で指定した形式で応答が返る

URL: http://twitter.com/statuses/update.format
(format は xml, json のうちのいずれかを指定)

引数:
status=ステータス (必須)
ステータス(発言、投稿内容)を指定する。必ず URL エンコードすること。
ステータスは 160文字以内におさめること。ただし、140文字を超えた部分は必ずしも表示される保証はない。

in_reply_to_status_id (オプション)
返信(reply)対象のステータスIDを指定する。どのステータスに対する返信か明示するのに使用する
存在しない、あるいはアクセス制限のかかっているステータスIDを指定した場合は無視される

source=クライアント名 (オプション)
ステータスの投稿に使用しているクライアント名を指定する。「クライアント名」をWebフォームから申請し、承認を得ることで、Twitter の Webページ上に“from クライアント名”付きで発言が掲載されるようになる。
OAuth 認証による API 実行時は、本引数は無視される(OAuth アプリケーション登録時のアプリケーション名、URLが適用される)。
なお、本引数は公式のAPI仕様書には掲載されていない。

メソッド: POST
API制限: 適用対象外

訳者による注記:
2007年4月はじめごろまでは GET でも構わなかった。現在は、GET は使えなくなっている

同じくTwitter API 仕様書 第二十版 (2009年4月16日版) より引用。

なるほどです。

早速実装していきたいと思います。

ActionScriptでBASIC認証するにはどうするんだろう?
調べていたら、、、すぐに見つかりました。
ID、パスワードをBase64エンコードしたものを、HTTPヘッダーのAuthorizationフィールドに設定してあげればいいようです。

某開発者の独り言: AdobeAIRでBasic認証をする
こちらを参考に実装してみました。

package
{
	import com.adobe.serialization.json.JSON;

	import flash.net.URLVariables;

	import mx.rpc.events.FaultEvent;
	import mx.rpc.events.ResultEvent;
	import mx.rpc.http.HTTPService;
	import mx.utils.Base64Encoder;

	public class TwitterManager
	{
		private var service:HTTPService;

		public function TwitterManager()
		{
		}

		public function initTwitterManager():void {
			service = new HTTPService;
			service.method = "post";
			service.url = "http://twitter.com/statuses/update.json";
			service.resultFormat = "text";
			service.addEventListener(ResultEvent.RESULT, onResult);
			service.addEventListener(FaultEvent.FAULT, onFault);
		}

		public function Tweet(message:String):void {
			var forms:URLVariables = new URLVariables();
			forms.status = message;

			var encoder:Base64Encoder = new Base64Encoder();
			encoder.encode("username:password");

			service.headers["Authorization"] = "Basic " + encoder.toString();
			service.request = forms;
			service.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());
		}

	}
}
34行目は、とりあえずサンプルソースのため、username、passwordとしています。ご自分のtwitterのIDとパスワードに適宜変更してください。

次にアイコンを再びアニメーションさせる部分です。
翻訳結果が得られたときに、アニメーションを再開させるようにしたいと思います

flashcast:フリーで働くITエンジニア集団のブログ: google翻訳APIを使ったAIRアプリを作る!(翻訳部分の実装編)で作成したModelクラスに、翻訳結果を保存するメンバーを追加します。

package
{
	[Bindable]
	public class TranslateModel
	{
		public var _original:String;
		public var _translated:String;

		public function TranslateModel()
		{
		}

		public function set Original(original:String):void {
			_original = original;
		}

		public function get Original():String {
			return _original;
		}

		public function set Translated(translated:String):void {
			_translated = translated;
		}

		public function get Translated():String {
			return _translated;
		}

	}
}

メインのロジックでは、翻訳結果が変更されたかどうか監視するようにします。

package
{
	import flash.events.Event;

	import mx.binding.utils.ChangeWatcher;
	import mx.core.Application;
	import mx.events.PropertyChangeEvent;

	public class TranslatorSample6Base extends Application
	{
		private var imanager:IconManager;
		private var cmanager:ClipboardManager;
		private var gmanager:TranslateManager;
		private var tmanager:TwitterManager;

		private var gmodel:TranslateModel;

		private var tweet:Boolean;

		public function TranslatorSample5Base():void {
		}

		public function initApp():void {
			gmodel = new TranslateModel();

			imanager = new IconManager();
			cmanager = new ClipboardManager();
			gmanager = new TranslateManager();
			tmanager = new TwitterManager();

			imanager.initIconManager();
			cmanager.initClipboardManager(gmodel);
			gmanager.initTranslateManager(gmodel);
			tmanager.initTwitterManager();

			ChangeWatcher.watch(gmodel, '_original', onClipboardChangeHandler);
			ChangeWatcher.watch(gmodel, '_translated', onTranslatedChangeHandler);

			imanager.addEventListener(TranslatorSampleEvent.ICON_CLICK, onIconClick);
		}

		private function onClipboardChangeHandler(event:PropertyChangeEvent):void {
			if ((event.oldValue as String) != "") {
				this.addEventListener(Event.ENTER_FRAME, imanager.iconAnimate);
			}
		}

		private function onIconClick(event:TranslatorSampleEvent):void {
			this.removeEventListener(Event.ENTER_FRAME, imanager.iconAnimate);

			if (tweet) {
				tmanager.Tweet(gmodel.Translated.substr(0, 140));
				tweet = false;
			}
			else {
				gmanager.Translate("1.0", gmodel.Original, "en", "ja");
			}
		}

		private function onTranslatedChangeHandler(event:PropertyChangeEvent):void {
			this.addEventListener(Event.ENTER_FRAME, imanager.iconAnimate);
			tweet = true;
		}
	}
}

翻訳時と同じように、アニメーションしているアイコンをクリックするとつぶやくようにしています。また、twitterは140文字以内という制限があるため、140文字を超えているものは141文字目以降を削除してtwitterに投稿します。

実行時のデバックトレースです。つぶやいた翻訳結果を出力しています。

sample6

これでつぶやき機能を持つ、翻訳アプリが出来ました。twitterを翻訳結果の履歴管理に利用するのもいいかもしれませんね(followerには大迷惑かもしれませんが・・・)。

サンプルソース

サンプルソースをダウンロードできるようにしておきます。

  • Search:
  • flashcastとは?

    東京を中心に、現在フリーランスとして活動しているITエンジニア、および、かつてフリーランスとして活動していた起業家達が立ち上げたコミュニティーです。

    みんなで集まって面白いことをやろう!形に残そう!ということで、ブログをはじめました。

    技術情報や、フリーエンジニアに役立つ情報などを、ご紹介できたらと思っています。

    お問い合わせ:
    info@flashcast.jp
  • カレンダー

    2010年7月
    日曜日 月曜日 火曜日 水曜日 木曜日 金曜日 土曜日
    « 6月    
     123
    45678910
    11121314151617
    18192021222324
    25262728293031
  • メンバー紹介

    もじゃもじゃ
    flashcastのリーダー

    3年ほどフリーのITエンジニアとして活動。現在は、社員2名の株式会社を経営しています。

    一攫千金を夢見る野心家です。

    ライブキャスト

    yasu
    ダイバー

    自宅サーバーでホームページを作り始めました。

    少しずつ記事を増やしていきますので足を運んでください。

    よろしくお願いします。

    sa-sa-ki.jp

    のら
    たびびと

    ねこ好きに悪人はいなーいっ!!

    バイクや車も好きです。

    めぐ
    デザイナーのたまご

    音楽とデザインとお酒をこよなく愛する永遠のダイエッター。

    現在ペンタブレットでイラストを勉強中。

    Hiro
    コンサル

    PMやSEの案件を業務委託で請けることが多いですが、小規模案件も受託でやっています。

    得意な分野はマイクロソフト製品や関連技術によるシステム構築です。

    KEI
    取締役の風格

    最年少なのに、メンバーで1番の貫禄の持ち主!?

    C#や.netなどサーバ側の開発が得意。

    ろっきー
    美食家★パパ

    自分にとっての息抜きは、ドライブして温泉に入って、美味しいご飯を食べる事。

    ココロとカラダのリフレッシュを大切にし、日々の仕事に励む一児の父親です。

    郵便番号検索

    my-hobby

    とのさま
    げーむのおうさま

    大人なのに好きなことしかやらない駄目人間。

    Web系が得意、アクセスは苦手><

    tonosamart.com

    セクレタリアト
    ギャンブラー

    フリーランス時代は仲間の現場を探すことが多く、それをきっかけに会社を設立。

    現在はSI業に特化せず、他の業種にも興味を持ち始めています。

    メドレー株式会社

  • 広告