Google Chrome拡張からOAuthを使用してGoogleカレンダーにイベント登録する。

以前はてなブログに投稿した記事(URL失念)の改訂版です。

OAuth認証を利用し、Googleカレンダーにイベント登録するためのGoogle Chrome拡張を作ります。
#manifest.jsonは割愛します。

用意するもの

[chrome] Index of /trunk/src/chrome/common/extensions/docs/examples/extensions/oauth_contactsから以下の4つをダウンロードします。

  • chrome_ex_oauth.html
  • chrome_ex_oauthsimple.js
  • chrome_ex_oauth.js
  • onload.js

OAuth認証を利用するためのライブラリ群なのですが、中身はよくわかんなかったです。
ちなみにTutorial: OAuth – Google Chromeは未だにonload.jsを用意しろって書いてないですね…。onload.jsが必要になったのはchrome extensionのmanifestがv2でhtmlに直接スクリプトが書けなくなったのが原因で、呼び出し元はchrome_ex_oauth.htmlです。チュートリアルページにも詳しいサンプルコードがありますので、一度目を通しておくと役に立ちます。

background.js

スクリプトの本体です。今回はコンテキストメニューで呼び出すとイベントが登録されるようなものにしました。コード見たらだいたい何やってるかわかると思います。
注意点として、終日設定のイベントを登録する場合、時刻の登録は無し・終了日は設定したい日付に+1日する必要があります。カレンダーのIDはカレンダーの設定から取得出来ます。

chrome.contextMenus.create({
	"title" : "カレンダーに投稿",
	"type" : "normal",
	"contexts" : ["selection"],//テキストを選択した時のみ
	"onclick" : getClickHandler()
});

var oauth = ChromeExOAuth.initBackgroundPage({//OAuth設定
	"request_url": "https://www.google.com/accounts/OAuthGetRequestToken",
	"authorize_url": "https://www.google.com/accounts/OAuthAuthorizeToken",
	"access_url": "https://www.google.com/accounts/OAuthGetAccessToken",
	"consumer_key": "anonymous",
	"consumer_secret": "anonymous",
	"scope": "https://www.google.com/calendar/feeds/"
});


function getClickHandler() {
	return function(info, tab) {
		oauth.authorize(function(){//OAuthToken取得完了時に呼ばれる関数

			/**開始~終了日時設定**/

			var st ="";//開始日時
			var en ="";//終了日時

			var check = true;//終日設定フラグ

			if(check){//終日設定にする場合
				st = "yyyy-mm-dd";
				en = "stの日付に+1日した値";//+1しないと意図したとおりに設定出来ません
			}
			else {
				st = "yyyy-mm-dd(開始日)" + "T" + "hh" + ":" + "mm" + ":00.000+09:00";
				en = "yyyy-mm-dd(終了日)" + "T" + "hh" + ":" + "mm" + ":00.000+09:00";
			}
			/**設定終わり**/

			/**登録したいイベントのJSON*/
			var body = JSON.stringify({

				"data": {
					"title": "イベントのタイトル",
				"details": "イベントの内容",
				"transparency": "opaque",
				"status": "confirmed",
				//"location": "場所",
				"when": [
				{
					"start": st,
					"end": en
				}
				]
			}
			});

			oauth.sendSignedRequest(//リクエストの送信

				"https://www.google.com/calendar/feeds/" + "登録したいカレンダーのID" + "/private/full",

				function(response){//コールバック関数
					try{
						var p_res = JSON.parse(response);
						if(p_res.error){//エラー処理
							var err = p_res.error;
							alert("Error:" + err.message);
						}else{
							alert("登録完了しました");
						}
					} catch(e) {
						alert("Error:" + response);
					}
				},

				{
					"method": "POST",
					"headers": {"Content-Type": "application/json"},
					"body" : body
				}
			);
		});
	}
}

background.html

スクリプトを呼び出すためのbackground.html

<!doctype html>
<html>
	<head>
		<meta charset="utf-8" />
		<script src="chrome_ex_oauthsimple.js"></script>
		<script src="chrome_ex_oauth.js"></script>
		<script src="background.js"></script>
	</head>
	<body>
		<!--バックグラウンドです!-->
	</body>
</html>

初回呼び出し時にのみOAuth認証画面に飛ばされたあと、通常通り使えるようになります。
認証を解除したい場合は、拡張が使用しているlocalStorage変数を削除すれば大丈夫です。

シャープのスマートフォンでなかよしボックス機能を使う。

A01しか知らないけどシャープのスマホなら多分できるはず。
(なかよしボックス:自分が送った特定の人物宛のメールとその人物からのメールの一覧が時系列順で見られる機能)

電話帳→なかよしボックスを使いたい相手を選択→吹き出しをタップ

A01使って2年近く経とうとしてるのに今の今まで知りませんでした。しかも着信・発信まで見られるしすげえ便利。

Windows8でWindows Update(KB2822241)に失敗する場合の対策。

Windows8にしてからUIがクソすぎてモニタ叩き割りそうになったことが何度もあった以外は特に大きなトラブルにも見舞われなかったのですが、Windows UpdateのKB2822241のインストール(正確には再起動中に行う構成)に何度も失敗したため、いろいろ調べてみました。
(ちなみにエラーコードでぐぐっても全然解決策見つからなかったのと、毎回コードが違ってまるで参考にならなかったので、コードはメモってません。)

KB2822241のインストール失敗 – マイクロソフト コミュニティ
ここが一番参考になりました。プリインストール系の何かがいろいろモリモリ動いてるので、それと競合してだめなんだろうなーって気がしたのでクリーンブートを試してみたら見事にうまくいきました。
Windows Vista、Windows 7、または Windows 8 でクリーン ブートを実行して問題のトラブルシューティングを行う方法

しかし何と競合してだめだったのかわからないのがモヤモヤしますね…。いらないサービスとスタートアップは切れるだけ切ってるんですが…。

jsoupでAndroidからWebスクレーピングしてTwitterに投稿する。

はてなブログやってたときにTagSoupでAndroidからWebスクレーピングする方法を書いて結構参照されたのですが、今はjsoupというjQueryライクなライブラリがあるそうなので、これを使ってスクレーピングしてみました。ついでにスクレーピングした内容をListViewに突っ込んで、クリックすると内容をTwitterに投稿するようにしました。通信とスクレーピング処理はAsyncTaskクラスを使って別スレッドで実行するようにしています。

サンプルではretrorocket.bizのaタグのテキストとhref属性の値を取り出しました。
あと、注意点としてAndroidで使う外部jarは全部libsディレクトリに入れないと動きません。これに気づかず2時間無駄にしました。

メインアクティビティのクラス

public class JsoupAndTestActivity extends Activity {
	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		DownloadTask task = new DownloadTask(JsoupAndTestActivity.this);
		task.execute("start");
	}
}

DownloadTaskはAsyncTaskの拡張です。ここからスクレーピング用の処理を呼び出します。本来ならActivityではなくListViewを渡すべきなんだろうなーと思うのですが、いつもActivityを渡してしまいます。やっぱりだめなのかな…。

DownloadTaskクラス

//<入力値、プログレス用の値、処理完了後の値>
public class DownloadTask extends AsyncTask<String, Integer, Elements> {
	private JsoupAndTestActivity mActivity = null;
	private ProgressDialog dlg;
	private ArrayAdapter<String> adapter;// リスト用のアダプタ
	private ListView list;

	public DownloadTask(JsoupAndTestActivity activity) {
		this.mActivity = activity;
		this.dlg = new ProgressDialog(mActivity);
		this.list = (ListView) mActivity.findViewById(R.id.listView1);
		this.adapter = new ArrayAdapter<String>(
				mActivity.getApplicationContext(),
				android.R.layout.simple_list_item_1);
	}

	/** 前処理(プログレスバー等の用意) **/
	@Override
	protected void onPreExecute() {
		dlg.setTitle("処理中");
		dlg.setMessage("しばらくお待ちください");
		dlg.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
		dlg.setCancelable(true);
		dlg.setCancelable(true);
		dlg.setMax(100);
		dlg.setProgress(0);
		dlg.show();
	}

	/** バックグラウンドのメイン処理 **/
	@Override
	protected Elements doInBackground(String... params) {
		//params[0]:start
		Elements aTags = null;
		publishProgress(30);
		try {
			String url = "https://retrorocket.biz/";
			Document doc = Jsoup.connect(url).get();
			aTags = doc.getElementsByTag("a");
		} catch (IOException e) {
			e.printStackTrace();
		}
		publishProgress(70);
		return aTags;
	}

	/** プログレスバーの更新 **/
	@Override
	protected void onProgressUpdate(Integer... values) {
		dlg.setProgress(values[0]);
	}

	/** doInBackground完了後の処理 **/
	@Override
	protected void onPostExecute(Elements result) {
		publishProgress(90);
		if (result == null) {
			Toast.makeText(mActivity, "結果を取得できませんでした", Toast.LENGTH_LONG)
					.show();
		} else {
			//本来ならdoInBackground側で実行すべきかもしれない
			for (Element elem : result) {
				String text = elem.text();
				String href = elem.attr("href");
				adapter.add(text + " " + href);
			}
		}
		
		//リストにセット
		list.setAdapter(adapter);
		//クリックイベントを設定
                list.setOnItemClickListener(new ClickEvent());
		dlg.dismiss();
	}

	@Override
	protected void onCancelled() {
		Toast.makeText(mActivity, "キャンセルされました", Toast.LENGTH_LONG).show();
		dlg.dismiss();
	}

}

タスクに渡すのはString(デバッグするのにString渡しただけなので、別にVoidでもいいです)で、処理中の進行度はInteger、処理完了後の値の型はElementsです。doInBackgroundでスクレーピングを実行しています。progressDialogに渡す値は適当に設定しました。
次はリストをクリックした時にTwitterに投稿するためのクラスです。

ClickEventクラス

public class ClickEvent implements OnItemClickListener {

	//Twitterクラスのインスタンスを作成
	private Twitter twitter = TwitterFactory.getSingleton();

	//adapter:AdapterView・view:選択したView・
	//position:選択された位置・id:IDを示すlong値
	@Override
	public void onItemClick(AdapterView<?> adapter, View view, int position,
			long id) {
		
		//* viewから取得するパターン */
		TextView textview = (TextView) view;
		String text = (String) textview.getText();
		
		//*adapterから取得するパターン*/
		//ListView listView = (ListView) adapter;
		//String text = (String) listView.getItemAtPosition(position);
		
		try {
			twitter4j.Status status = twitter.updateStatus(text);
			//以下statusを使った処理等
			
		} catch (TwitterException e) {
			e.printStackTrace();
		}

	}

}

twitter4j.propertiesはsrcに配置しないと動きませんでした。

実行結果

実行結果

実行結果


リストをタップするとTwitterに投稿されます。

Twitter4Jを使ってt.coを展開したタイムラインを取得した後何かつぶやく。

この記事は2011/04/30にはてなブログに投稿した”Twitter4Jを使ってタイムラインを取得した後何かつぶやく – Radical No.11 #?”の改訂版です。

Twitter4Jを使ってt.coを展開したタイムラインを取得した後何かつぶやきます。
前書いた記事にのっとってEclipseで実行する方法とWindowsのコマンドラインから実行する方法を書きたいと思います。
(たしかフォロワーさん向けに書いたから実行方法まで載せたような記憶があります。)
あと、昔書いたクラスパスの部分に盛大に間違ってるところがあって変な声が出ました。カレントディレクトリ含めるなって言いながらカレントディレクトリ指定しろとか意味わかんねーだろ。これずっと放置してたとかしにたくなってきますね。

準備

あらかじめTwitter4Jの最新バージョンをDLしてどこか適当な場所に解凍しておいてください。
この記事ではTwitter4J ver.3.0.3を使用しています。
クラス名はTwitterSampleとして進めます。

Eclipseでコンパイル・実行する

まずtwitter4j-core-3.0.3.jarをクラスパスに通す必要があります。
Eclipseのパッケージエクスプローラ「クラス名」右クリック->「ビルド・パス」->「ビルド・パス」の構成
をクリックし,
「ライブラリー」タブ->「外部jar追加」->DLしたTwitter4Jのlibフォルダの「twitter4j-core-バージョン.jar」を選択
で,「外部ライブラリー」のところに選択したライブラリーが追加されてることを確認して下さい.

次に認証用のプロパティの設定をします。
パッケージエクスプローラの「src」を右クリック->「新規」->「ファイル」でファイル名を「twitter4j.properties」にして保存
twitter4j.propertiesに

debug=true
oauth.consumerKey=コンシューマーキー
oauth.consumerSecret=コンシューマーキーシークレット
oauth.accessToken=アクセストークン
oauth.accessTokenSecret=アクセストークンシークレット

を記述することでプロパティを設定出来ます。debugはfalseでもよいのですが、trueにしておくとコンソールにログを吐いてくれるので困ったときになにかと便利です。なくてもよい場合はdebugの行を消してください。
twitter4JはAPI v1.1に完全対応していますが、twitter4j.propertiesのrestBaseURLにAPI v1のURLを指定することでAPI v1を使うことができます。

実行用のクラス本体のコードは以下です。コードのコメント見たら何やってるか大体わかると思うので、詳しくはコードを見てください。

import java.io.*;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import twitter4j.*;

public class TwitterSample {
	public static void main(String[] args) throws IOException {
		
		//プロパティに認証情報を記述しておく
		Twitter twitter = TwitterFactory.getSingleton();
		
		System.out.println("*====Home_Timeline===*");
		
		try {//HomeTL取得開始
			Paging page = new Paging(1, 10);//1ページ目の10件分を取得
			List<Status> statuses = twitter.getHomeTimeline(page);
			for (Status status : statuses) {
				
				String tweet = status.getText();//発言内容

				URLEntity[] entities = status.getURLEntities();//entity取得
				for (URLEntity entity : entities) {
					
					String ex_url = entity.getExpandedURL();//展開後のURL
					String tco = entity.getURL();//t.co

					Pattern p = Pattern.compile(tco);
					Matcher m = p.matcher(tweet);
					tweet = m.replaceAll(ex_url);//置換

				}
				User user = status.getUser();//発言したユーザのインスタンス
				System.out.println(user.getScreenName()// ユーザのアカウント名
						+ " [" + user.getName() + "] " + ":"// ユーザのニックネーム
						+ tweet // ツイート
				);
			}

		} catch (TwitterException e) {
			e.printStackTrace();
		}
		
		//発言の投稿
		System.out.print("何か入力してください> ");
		BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
		String post  = reader.readLine();
		try {
			Status status = twitter.updateStatus(post);
		    System.out.println("投稿に成功しました [" + status.getText() + "]");
		} catch (TwitterException e) {
			e.printStackTrace();
		}
		
	}

}

Eclipseを使わずコマンドラインから実行する方法

適当なフォルダを作る.→そのフォルダの中に上記の「twitter4j.properties」と「TwitterSample.java」を突っ込む

$cd さっきつくったフォルダのパス

でカレントディレクトリをさっき作ったフォルダにします。
Windowsならフォルダのアドレスバー右クリックしたり、フォルダをコマンドプロンプトにドロップするとパスが表示されます。

#コンパイルする
$javac -classpath twitter4j-core-バージョン名.jarまでのパス TwitterSample.java
#実行する
$java -classpath twitter4j-core-バージョン名.jarまでのパス;. TwitterSample

実行時は「クラスパスは、複数指定する場合Windowsなら;でUnixなら:で区切って指定する」「-classpathオプションを使うときは.(ドット)を忘れない(=カレントディレクトリを含める)」に気をつければ実行できると思います。
環境変数にCLASSPATHを指定してしまえば-classpathを指定しなくても実行できるようになります。詳しくは以下のページを御覧ください。
Javaの道:基本事項(5.クラスパス)