<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	>

<channel>
	<title>KLab若手エンジニアの これなぁに？</title>
	<atom:link href="http://lab.klab.org/young/feed/" rel="self" type="application/rss+xml" />
	<link>http://lab.klab.org/young</link>
	<description>- KLab株式会社 若手エンジニアブログ -</description>
	<pubDate>Tue, 31 Aug 2010 15:21:11 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.6.2</generator>
	<language>en</language>
			<item>
		<title>せっかくだから俺はAndroidソースコードを読むぜ(1)</title>
		<link>http://lab.klab.org/young/2010/08/%e3%81%9b%e3%81%a3%e3%81%8b%e3%81%8f%e3%81%a0%e3%81%8b%e3%82%89%e4%bf%ba%e3%81%afandroid%e3%82%bd%e3%83%bc%e3%82%b9%e3%82%b3%e3%83%bc%e3%83%89%e3%82%92%e8%aa%ad%e3%82%80%e3%81%9c1/</link>
		<comments>http://lab.klab.org/young/2010/08/%e3%81%9b%e3%81%a3%e3%81%8b%e3%81%8f%e3%81%a0%e3%81%8b%e3%82%89%e4%bf%ba%e3%81%afandroid%e3%82%bd%e3%83%bc%e3%82%b9%e3%82%b3%e3%83%bc%e3%83%89%e3%82%92%e8%aa%ad%e3%82%80%e3%81%9c1/#comments</comments>
		<pubDate>Tue, 31 Aug 2010 14:59:54 +0000</pubDate>
		<dc:creator>nakazawa-k</dc:creator>
		
		<category><![CDATA[Android]]></category>

		<category><![CDATA[C++]]></category>

		<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">https://labmgr.sdc.klab.org/young/?p=1686</guid>
		<description><![CDATA[どうもこんにちは、コンバット○前です。
嘘です。nakazawa-kです。この数日少しずつ涼しくなってきていますね。
KLab若手エンジニアブログでiPhoneやiPadばかり書かれていてAndroidが
全く書かれていないことに気付いたので、少しずつ勉強した内容などを書いてみます。
現在KLabの社内ではMacユーザ率の上昇に合わせてiPhone開発者増加の
兆しが見えているのですが、開発の取っつきやすさではAndroidだって負けちゃいない。
なんたってMacが無くても不自由なく開発出来る(←ここ重要!)のですから。
というわけで(どういうわけか)Androidなお話です。
今回はカメラからの映像に適当なオーバーレイ要素を追加してリアルタイム
エンコードすることがAndroidとそれを走らせているハードウェア上で
実現出来るのか?を調べるためにMediaRecorder関連のソースを追いかけてみました。
ひとつひとつ丹念に見ていけばOSのソースって案外辿って行けて面白いよ!というのをなんとなく感じて頂けると幸いです。


Androidは最高の学習環境!?

Androidはご存じの通りオープンソースなモバイル向けOSで、その比較的
新しいコードがAOSP(Android Open Source Project)として公開されています。
ちなみにそのリリース方法はLinuxのカーネル本体とは異なり、Google社が各OS世代の早期パートナー企業(端末メーカ)との間で最新のコードベースでの開発を行い、端末リリース前後のタイミングでAOSPでのコード公開を行う、という流れになっているようです。開発に協力するパートナー企業としては最新OSを搭載した端末を他社に先駆けて市場へ投入出来、またOSに対して自社の望む仕様を公式仕様として追加し易いというメリットが考えられます。
閑話休題、ソースコードが公開されているので、「ここはどういう風に
実装されているんだろう?」という疑問を持った際に、必要であれば
OSのコードまでさかのぼって調査することが出来ます。
とはいえ、片っ端からコードの山をひっくり返していけば目的のコードへ
辿り着ける、というわけではありません。なんといってもコード量が莫大です。
gitからソースコードを取得することが出来ますが、AOSPのドキュメントに
従い全て取得すると4.1GB(10/08/30時点)となります。
これ位のコード量になってくると全体に対してgrepをかけるのも一苦労ですし、
検索結果に物凄い数のファイルが見つかって途方に暮れたりするものです。
このような大量のファイルから必要な情報を効率的に得るためには、システムの
アーキテクチャ認識を持ち、『必要なものがありそうな場所の勘が働くようにする』
ことが結構重要なことだと思います。
システムの中での呼び出しの流れとデータの流れ(デバイスからの入出力を含め)が
掴めてくると、割と膨大なソースとも付き合いやすくなるのではないでしょうか!?
目の前で動作している端末がどのようなアーキテクチャ、システムで動作しているのかを
学ぶことは非常に良い勉強になるものと思います。せっかくソースコードが
公開されているのでどんどん読みましょう。
ということでだいぶ前置きが長くなってしまいました。nakazawa-kがソースを読み始めたというのが今回の話です。


今回のターゲット:MediaRecorder

先にお断りしておきます。今回の話は、ブログ記事1本分では決着しませんでした。
目的の達成は次回までお預けとなってしまっています。
発端は「Androidのカメラから入力された映像データに対してリアルタイムに
オーバーレイをかけ、H.264等で出力することは出来ないものか」と思ったところでした。
ビデオカメラ(CamCoder)といえば通常、MediaRecorder http://developer.android.com/reference/android/media/MediaRecorder.html
クラスを用いてソースと出力フォーマット、出力圧縮形式、
出力先を指定した上でstart()を呼んでやれば後は適当にやってくれるという感じです。
逆に、この映像に対して加工を行うことが出来ればARアプリなどで自分の見ていた
世界を動画に記録してそのままYouTubeなどにアップロードする、というような
使い方が出来そうです(ARアプリ内で完結するなら、当然それらをメタデータとして
別途格納しておき、再生時にオーバーレイする方法が考えられますが)。
これを実現するためには


+------------+               +----------------+
&#124; 映像ソース &#124; --フィルタ--> &#124;動画出力エンジン&#124;
+------------+               +----------------+

としてやる必要があります。しかし、残念ながらMediaRecorderで提供されているのは
前述のような入出力設定のみで、実際の動画出力などがどのような仕組みにて
提供されているかは見えなくなっています。


前準備

まずは今回やりたいことがどのようにして実現出来るか、あたりを付けるためにざっと情報を整理してみます。
一般にH.264等のリアルタイムエンコードを行うには携帯端末向けのCPUでは力不足です。そのため、ほとんどの携帯端末ではH.264等の圧縮を丸ごと行う、あるいは部分的にサポートするDSPを用いたエンコードを行っていると考えられます。この入力データに任意のストリームを渡せるのか、それともカメラからの入力を統合チップ内部でバイパスして渡すような方法を採らざるを得ないのか、によって今回の目的の実現可能性は大きく変わってきます(場合によっては一部WindowsMobile機のようにWMVエンコードのソフトウェア実装を持ち込んで力業でエンコードするような方針になります)。
ソースを読み始める前に、少しだけデータシート(というかチップのパンフ的な資料)を見てみましょう。
手元に転がっていたHT-03Aに搭載されている統合チップはMSM7200Aで、どうやら
http://www.datasheetpro.com/268119_download_MSM7200A_datasheet.html で見ることが出来ます。
性能的には30fpsでWVGAの動画をキャプチャ出来るほどのチップであることは分かりますが、残念ながら動画エンコード機能に対して任意のストリームを投入出来るか否かについて判断は出来ませんでした。ひょっとすると入力(カメラ)と出力(動画圧縮エンジン)のやりとりをチップ内で完結させなければスループットを出せない(=フレームレートが不足する)かもしれません。
まあ多少フレームサイズやフレームレートが落ちても、加工済みの動画がリアルタイム出力出来れば良いのです。仮にカメラからのJPEG出力系を流用してMJEPGになったとしても、大失敗ではないのです。


本題

それでは、AndroidのMediaRecorderがどのような実装になっているのかを見ていきましょう。
Androidのアーキテクチャは
http://developer.android.com/guide/basics/what-is-android.html#os_architecture
に記載されているような形になっていています。
今回の場合、Javaで書かれたカメラアプリがAndroid独自のJava系VMである
Dalvik Virtual Machineにて実行され、内部からMedia Frameworkが呼び出され、
そこからLinux KernelのCamera DriverやFlash Memory Driverなどを呼び出していると
考えられます。
とりあえず、コードを取得しましょう。
curl http://android.git.kernel.org/repo > repo
chmod +x repo
sudo mv repo /usr/local/bin
repo [...]]]></description>
			<content:encoded><![CDATA[<p>どうもこんにちは、コンバット○前です。</p>
<p>嘘です。nakazawa-kです。この数日少しずつ涼しくなってきていますね。</p>
<p>KLab若手エンジニアブログでiPhoneやiPadばかり書かれていてAndroidが<br />
全く書かれていないことに気付いたので、少しずつ勉強した内容などを書いてみます。</p>
<p>現在KLabの社内ではMacユーザ率の上昇に合わせてiPhone開発者増加の<br />
兆しが見えているのですが、開発の取っつきやすさではAndroidだって負けちゃいない。<br />
なんたってMacが無くても不自由なく開発出来る(←ここ重要!)のですから。</p>
<p>というわけで(どういうわけか)Androidなお話です。<br />
今回はカメラからの映像に適当なオーバーレイ要素を追加してリアルタイム<br />
エンコードすることがAndroidとそれを走らせているハードウェア上で<br />
実現出来るのか?を調べるためにMediaRecorder関連のソースを追いかけてみました。</p>
<p>ひとつひとつ丹念に見ていけばOSのソースって案外辿って行けて面白いよ!というのをなんとなく感じて頂けると幸いです。</p>
<p><span id="more-1686"></span></p>
<ul>
<li>Androidは最高の学習環境!?</li>
</ul>
<p>Androidはご存じの通りオープンソースなモバイル向けOSで、その比較的<br />
新しいコードがAOSP(Android Open Source Project)として公開されています。</p>
<blockquote><p>ちなみにそのリリース方法はLinuxのカーネル本体とは異なり、Google社が各OS世代の早期パートナー企業(端末メーカ)との間で最新のコードベースでの開発を行い、端末リリース前後のタイミングでAOSPでのコード公開を行う、という流れになっているようです。開発に協力するパートナー企業としては最新OSを搭載した端末を他社に先駆けて市場へ投入出来、またOSに対して自社の望む仕様を公式仕様として追加し易いというメリットが考えられます。</p></blockquote>
<p>閑話休題、ソースコードが公開されているので、「ここはどういう風に<br />
実装されているんだろう?」という疑問を持った際に、必要であれば<br />
OSのコードまでさかのぼって調査することが出来ます。</p>
<p>とはいえ、片っ端からコードの山をひっくり返していけば目的のコードへ<br />
辿り着ける、というわけではありません。なんといってもコード量が莫大です。<br />
gitからソースコードを取得することが出来ますが、AOSPのドキュメントに<br />
従い全て取得すると4.1GB(10/08/30時点)となります。</p>
<p>これ位のコード量になってくると全体に対してgrepをかけるのも一苦労ですし、<br />
検索結果に物凄い数のファイルが見つかって途方に暮れたりするものです。<br />
このような大量のファイルから必要な情報を効率的に得るためには、システムの<br />
アーキテクチャ認識を持ち、『必要なものがありそうな場所の勘が働くようにする』<br />
ことが結構重要なことだと思います。<br />
システムの中での呼び出しの流れとデータの流れ(デバイスからの入出力を含め)が<br />
掴めてくると、割と膨大なソースとも付き合いやすくなるのではないでしょうか!?</p>
<p>目の前で動作している端末がどのようなアーキテクチャ、システムで動作しているのかを<br />
学ぶことは非常に良い勉強になるものと思います。せっかくソースコードが<br />
公開されているのでどんどん読みましょう。</p>
<p>ということでだいぶ前置きが長くなってしまいました。nakazawa-kがソースを読み始めたというのが今回の話です。</p>
<hr />
<ul>
<li>今回のターゲット:MediaRecorder</li>
</ul>
<p><center>先にお断りしておきます。今回の話は、ブログ記事1本分では決着しませんでした。<br />
目的の達成は次回までお預けとなってしまっています。</center></p>
<p>発端は「Androidのカメラから入力された映像データに対してリアルタイムに<br />
オーバーレイをかけ、H.264等で出力することは出来ないものか」と思ったところでした。</p>
<p>ビデオカメラ(CamCoder)といえば通常、MediaRecorder <a href="http://developer.android.com/reference/android/media/MediaRecorder.html">http://developer.android.com/reference/android/media/MediaRecorder.html</a><br />
クラスを用いてソースと出力フォーマット、出力圧縮形式、<br />
出力先を指定した上でstart()を呼んでやれば後は適当にやってくれるという感じです。<br />
逆に、この映像に対して加工を行うことが出来ればARアプリなどで自分の見ていた<br />
世界を動画に記録してそのままYouTubeなどにアップロードする、というような<br />
使い方が出来そうです(ARアプリ内で完結するなら、当然それらをメタデータとして<br />
別途格納しておき、再生時にオーバーレイする方法が考えられますが)。<br />
これを実現するためには</p>
<pre>

+------------+               +----------------+
| 映像ソース | --フィルタ--> |動画出力エンジン|
+------------+               +----------------+
</pre>
<p>としてやる必要があります。しかし、残念ながらMediaRecorderで提供されているのは<br />
前述のような入出力設定のみで、実際の動画出力などがどのような仕組みにて<br />
提供されているかは見えなくなっています。</p>
<hr />
<ul>
<li>前準備</li>
</ul>
<p>まずは今回やりたいことがどのようにして実現出来るか、あたりを付けるためにざっと情報を整理してみます。<br />
一般にH.264等のリアルタイムエンコードを行うには携帯端末向けのCPUでは力不足です。そのため、ほとんどの携帯端末ではH.264等の圧縮を丸ごと行う、あるいは部分的にサポートするDSPを用いたエンコードを行っていると考えられます。この入力データに任意のストリームを渡せるのか、それともカメラからの入力を統合チップ内部でバイパスして渡すような方法を採らざるを得ないのか、によって今回の目的の実現可能性は大きく変わってきます(場合によっては一部WindowsMobile機のようにWMVエンコードのソフトウェア実装を持ち込んで力業でエンコードするような方針になります)。</p>
<p>ソースを読み始める前に、少しだけデータシート(というかチップのパンフ的な資料)を見てみましょう。<br />
手元に転がっていたHT-03Aに搭載されている統合チップはMSM7200Aで、どうやら<br />
<a href="http://www.datasheetpro.com/268119_download_MSM7200A_datasheet.html">http://www.datasheetpro.com/268119_download_MSM7200A_datasheet.html</a> で見ることが出来ます。<br />
性能的には30fpsでWVGAの動画をキャプチャ出来るほどのチップであることは分かりますが、残念ながら動画エンコード機能に対して任意のストリームを投入出来るか否かについて判断は出来ませんでした。ひょっとすると入力(カメラ)と出力(動画圧縮エンジン)のやりとりをチップ内で完結させなければスループットを出せない(=フレームレートが不足する)かもしれません。<br />
まあ多少フレームサイズやフレームレートが落ちても、加工済みの動画がリアルタイム出力出来れば良いのです。仮にカメラからのJPEG出力系を流用してMJEPGになったとしても、大失敗ではないのです。</p>
<hr />
<ul>
<li>本題</li>
</ul>
<p>それでは、AndroidのMediaRecorderがどのような実装になっているのかを見ていきましょう。</p>
<p>Androidのアーキテクチャは<br />
<a href="http://developer.android.com/guide/basics/what-is-android.html#os_architecture">http://developer.android.com/guide/basics/what-is-android.html#os_architecture</a><br />
に記載されているような形になっていています。<br />
今回の場合、Javaで書かれたカメラアプリがAndroid独自のJava系VMである<br />
Dalvik Virtual Machineにて実行され、内部からMedia Frameworkが呼び出され、<br />
そこからLinux KernelのCamera DriverやFlash Memory Driverなどを呼び出していると<br />
考えられます。</p>
<p>とりあえず、コードを取得しましょう。</p>
<blockquote><p>curl http://android.git.kernel.org/repo > repo<br />
chmod +x repo<br />
sudo mv repo /usr/local/bin<br />
repo init -u git://android.git.kernel.org/platform/manifest.git<br />
(AOSPのドキュメントではinit-uとなっていますが、正しくは上記の通りです)</p></blockquote>
<p>まず、おさらいの部分ですがカメラアプリのコードから追いましょう。<br />
アプリケーションのソースはpackages/apps/Camera以下にあります。<br />
MediaRecorderの初期化を行っているのはpackages/apps/Camera/src/com/android/camera/VideoCamera.java<br />
VideoCameraクラスのinitializeRecorderメソッドです。</p>
<pre class="prettyprint"><code>
 895         mMediaRecorder = new MediaRecorder();
 896
 897         mMediaRecorder.setCamera(mCameraDevice);
 898         mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
 899         mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
 900         mMediaRecorder.setProfile(mProfile);
 901         mMediaRecorder.setMaxDuration(mMaxVideoDurationInMs);
...
</code></pre>
<p>以下、基本的に録画開始を指示するstart()メソッドの流れを追いかけていきます。<br />
次は当然、android.media.MediaRecorderの定義を調べます。<br />
早速すこしずつややこしくなってきます。ほとんどがプロキシのリレーです。<br />
MediaRecorderの実装はほとんどJavaで行われておらず、ネイティブ実装になっています。<br />
Java側のMediaRecorder定義はframeworks/base/media以下にあります。<br />
frameworks/base/media/java/android/media/MediaRecorder.javaでは</p>
<pre class="prettyprint"><code> 58 public class MediaRecorder
 59 {
 60     static {
 61         System.loadLibrary("media_jni");
 62         native_init();
 63     }
...
513     public native void start() throws IllegalStateException;
</code></pre>
<p>との定義がされています。JNIで処理をC系のライブラリに委ねている形です。<br />
これに対応するコードがframeworks/base/media/jni/android_media_MediaRecorder.cppにあります。</p>
<pre class="prettyprint"><code>339 static void
340 android_media_MediaRecorder_start(JNIEnv *env, jobject thiz)
341 {
342     LOGV("start");
343     sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
344     process_media_recorder_call(env, mr->start(), "java/lang/RuntimeException", "start failed.");
345 }
...
466     {"start",                "()V",                             (void *)android_media_MediaRecorder_start},
</code></pre>
<p>C++側でのMediaRecorderクラスは</p>
<pre class="prettyprint"><code>421 static void
422 android_media_MediaRecorder_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
423 {
424     LOGV("setup");
425     sp<MediaRecorder> mr = new MediaRecorder();
</code></pre>
<p>で作られます。このあたりは<br />
frameworks/base/include/media/mediarecorder.hに定義があります。<br />
各種定数もこのファイルに定義があるので参考になります。<br />
実装はこちらです。frameworks/base/media/libmedia/mediarecorder.cpp<br />
このファイルではインスタンスが存在する場合、そのメソッドを呼び出したり<br />
例外処理を行っているだけなので、重要なのはインスタンスの生成部分です。</p>
<pre class="prettyprint"><code>586 MediaRecorder::MediaRecorder()
587 {
588     LOGV("constructor");
589
590     const sp<IMediaPlayerService>&#038; service(getMediaPlayerService());
591     if (service != NULL) {
592         mMediaRecorder = service->createMediaRecorder(getpid());
593     }
594     if (mMediaRecorder != NULL) {
595         mCurrentState = MEDIA_RECORDER_IDLE;
596     }
597     doCleanUp();
598 }
</code></pre>
<p>この呼び出しはframeworks/base/media/libmediaplayerservice/MediaPlayerService.cppの</p>
<pre class="prettyprint"><code> 229 sp<IMediaRecorder> MediaPlayerService::createMediaRecorder(pid_t pid)
 230 {
 231 #ifndef NO_OPENCORE
 232     sp<MediaRecorderClient> recorder = new MediaRecorderClient(this, pid);
 233     wp<MediaRecorderClient> w = recorder;
 234     Mutex::Autolock lock(mLock);
 235     mMediaRecorderClients.add(w);
 236 #else
 237     sp<MediaRecorderClient> recorder = NULL;
 238 #endif
 239     LOGV("Create new media recorder client from pid %d", pid);
 240     return recorder;
 241 }
</code></pre>
<p>に対応しています。ここで、OpenCoreライブラリを利用しない設定でのビルド時には<br />
レコーダを無効化しています。MediaRecorderClientクラスを追いかけましょう。<br />
frameworks/base/media/libmediaplayerservice/MediaRecorderClient.cppです。</p>
<pre class="prettyprint"><code>292 MediaRecorderClient::MediaRecorderClient(const sp<MediaPlayerService>&#038; service, pid_t pid)
293 {
294     LOGV("Client constructor");
295     mPid = pid;
296
297 #if BUILD_WITH_FULL_STAGEFRIGHT
298     char value[PROPERTY_VALUE_MAX];
299     if (property_get("media.stagefright.enable-record", value, NULL)
300         &#038;&#038; (!strcmp(value, "1") || !strcasecmp(value, "true"))) {
301         mRecorder = new StagefrightRecorder;
302     } else
303 #endif
304 #ifndef NO_OPENCORE
305     {
306         mRecorder = new PVMediaRecorder();
307     }
308 #else
309     {
310         mRecorder = NULL;
311     }
312 #endif
313
314     mMediaPlayerService = service;
315 }
</code></pre>
<p>OpenCoreが利用出来る際にはPVMediaRecorderを生成していますね。<br />
PVでOpenCoreと来ればpacket-videoのOpenCoreです。OpenCoreはAndroid内で<br />
利用されているマルチメディア処理ライブラリで、公式サイトは<br />
<a href="http://www.opencore.net/">http://www.opencore.net/</a> です。repoコマンドで取得したツリーでは、<br />
external/opencore/doc/にドキュメントも取得されているはずです。</p>
<p>実はここまで読み進めた後でOpenCoreのドキュメントを読まずに<br />
external/opencore/engines/author/src/pvauthorengine.cppあたりまで<br />
追いかけてみたのですが、細かな個別実装ばかりが見えてきて全体が<br />
掴みにくくなってきたのでまずはOpenCoreのドキュメントを読み、ざっと<br />
構造を把握してから続きを読むように方針転換しました。</p>
<p>かなり長くなったので今回はこのあたりにして、次回OpenCoreの構造あたりから<br />
続けていきたいと思います。</p>
]]></content:encoded>
			<wfw:commentRss>http://lab.klab.org/young/2010/08/%e3%81%9b%e3%81%a3%e3%81%8b%e3%81%8f%e3%81%a0%e3%81%8b%e3%82%89%e4%bf%ba%e3%81%afandroid%e3%82%bd%e3%83%bc%e3%82%b9%e3%82%b3%e3%83%bc%e3%83%89%e3%82%92%e8%aa%ad%e3%82%80%e3%81%9c1/feed/</wfw:commentRss>
		</item>
		<item>
		<title>PHP Xdebug のProfile の手軽な共有ツールを作ったよ</title>
		<link>http://lab.klab.org/young/2010/08/php-xdebug-%e3%81%aeprofile-%e3%81%ae%e6%89%8b%e8%bb%bd%e3%81%aa%e5%85%b1%e6%9c%89%e3%83%84%e3%83%bc%e3%83%ab%e3%82%92%e4%bd%9c%e3%81%a3%e3%81%9f%e3%82%88/</link>
		<comments>http://lab.klab.org/young/2010/08/php-xdebug-%e3%81%aeprofile-%e3%81%ae%e6%89%8b%e8%bb%bd%e3%81%aa%e5%85%b1%e6%9c%89%e3%83%84%e3%83%bc%e3%83%ab%e3%82%92%e4%bd%9c%e3%81%a3%e3%81%9f%e3%82%88/#comments</comments>
		<pubDate>Tue, 31 Aug 2010 04:58:04 +0000</pubDate>
		<dc:creator>sasaki-k</dc:creator>
		
		<category><![CDATA[Tool]]></category>

		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">https://labmgr.sdc.klab.org/young/?p=1681</guid>
		<description><![CDATA[お疲れ様です。sasaki-kです。毎日暑いですね。
KLabではPHPで作られたフレームワークの高速化が熱いです。
高速化にあたっては、計測が重要です。
KLabではphpのxdebug extensionを使用し、プロファイル結果をcachegrindファイル形式で取得しWinCacheGrind で解析しています。
また皆でレビューするために Webgrind で解析結果を共有したり、言葉で説明しています。しかし、 Webgrindは一見してボトルネックが分かりにくかったり、言葉では説明がもどかしかったりしていました。
一方、KLabではCodepaste の社内版があり、 フォーマット済みのコードをフォームに貼りつけ、ブラウザのアドレスバーからURLをコピーしてIRC貼りつけてレビュー対象のコードを手軽に共有しています。

WinCacheGrind
WebGrind
codepaste 

「Codepasteみたいにプロファイル結果も共有できたらいいのに・・・」と思い、もっと良いものを作りたいなと思い探してみました。
結果、xdebugtoolkit という cachegrind ファイルをコールグラフを表現したdot ファイルへ変換してくれるスクリプトを発見しました。

xdebugtoolkit 

ボトルネックとなる関数が以下のように色で表示されていて一目で分かりやすく、コールパスもまとめられていてすっきり見やすいです。

呼び出し回数が多い関数はより濃い黄色
自身の実行時間がかかっている関数はより濃い紫
両方の特徴をもつ関数はより濃い赤 (=黄色+紫)

「こりゃいい！」ということで cachegrindファイルをuploadすると、ユニークな短いURLで xdebugtoolkit の結果画像を共有できる社内向けWebサービスを作りました。
またcachegrindファイルを保持しておく必要があるWebGrindを共有サービスにするにあたっての問題は、同ファイルは一般に巨大で、disk fullを起こさないようすぐに削除しなくてはならなかった点にありました。
ご紹介した方法だとdotファイルに変換された段階で小さくなり、かつcachegrindファイルを消してしまっても構わないので都度dotファイルから画像生成すればファイル削除を気にする必要がなさそうです。
このサービスを使ってみんなお手軽にProfileできたらいいなと思っています。
]]></description>
			<content:encoded><![CDATA[<p>お疲れ様です。sasaki-kです。毎日暑いですね。</p>
<p>KLabではPHPで作られたフレームワークの高速化が熱いです。<br />
高速化にあたっては、計測が重要です。</p>
<p>KLabではphpのxdebug extensionを使用し、プロファイル結果をcachegrindファイル形式で取得しWinCacheGrind で解析しています。</p>
<p>また皆でレビューするために Webgrind で解析結果を共有したり、言葉で説明しています。しかし、 Webgrindは一見してボトルネックが分かりにくかったり、言葉では説明がもどかしかったりしていました。</p>
<p>一方、KLabではCodepaste の社内版があり、 フォーマット済みのコードをフォームに貼りつけ、ブラウザのアドレスバーからURLをコピーしてIRC貼りつけてレビュー対象のコードを手軽に共有しています。</p>
<ul>
<li><a href="http://sourceforge.net/projects/wincachegrind/">WinCacheGrind</a></li>
<li><a href="http://code.google.com/p/webgrind/">WebGrind</a></li>
<li><a href="http://paste.bradleygill.com/">codepaste</a> </li>
</ul>
<p>「Codepasteみたいにプロファイル結果も共有できたらいいのに・・・」と思い、もっと良いものを作りたいなと思い探してみました。<span id="more-1681"></span></p>
<p>結果、xdebugtoolkit という cachegrind ファイルをコールグラフを表現したdot ファイルへ変換してくれるスクリプトを発見しました。</p>
<ul>
<li><a href="http://code.google.com/p/xdebugtoolkit/">xdebugtoolkit</a> </li>
</ul>
<p>ボトルネックとなる関数が以下のように色で表示されていて一目で分かりやすく、コールパスもまとめられていてすっきり見やすいです。</p>
<ul>
<li>呼び出し回数が多い関数はより濃い黄色</li>
<li>自身の実行時間がかかっている関数はより濃い紫</li>
<li>両方の特徴をもつ関数はより濃い赤 (=黄色+紫)</li>
</ul>
<p>「こりゃいい！」ということで cachegrindファイルをuploadすると、ユニークな短いURLで xdebugtoolkit の結果画像を共有できる社内向けWebサービスを作りました。</p>
<p>またcachegrindファイルを保持しておく必要があるWebGrindを共有サービスにするにあたっての問題は、同ファイルは一般に巨大で、disk fullを起こさないようすぐに削除しなくてはならなかった点にありました。</p>
<p>ご紹介した方法だとdotファイルに変換された段階で小さくなり、かつcachegrindファイルを消してしまっても構わないので都度dotファイルから画像生成すればファイル削除を気にする必要がなさそうです。</p>
<p>このサービスを使ってみんなお手軽にProfileできたらいいなと思っています。</p>
]]></content:encoded>
			<wfw:commentRss>http://lab.klab.org/young/2010/08/php-xdebug-%e3%81%aeprofile-%e3%81%ae%e6%89%8b%e8%bb%bd%e3%81%aa%e5%85%b1%e6%9c%89%e3%83%84%e3%83%bc%e3%83%ab%e3%82%92%e4%bd%9c%e3%81%a3%e3%81%9f%e3%82%88/feed/</wfw:commentRss>
		</item>
		<item>
		<title>PHP で Gmail の OAuth+SMTP を使う</title>
		<link>http://lab.klab.org/young/2010/08/php-%e3%81%a7-gmail-%e3%81%ae-oauthsmtp-%e3%82%92%e4%bd%bf%e3%81%86/</link>
		<comments>http://lab.klab.org/young/2010/08/php-%e3%81%a7-gmail-%e3%81%ae-oauthsmtp-%e3%82%92%e4%bd%bf%e3%81%86/#comments</comments>
		<pubDate>Tue, 31 Aug 2010 02:38:21 +0000</pubDate>
		<dc:creator>takada-at</dc:creator>
		
		<category><![CDATA[OAuth]]></category>

		<category><![CDATA[SMTP]]></category>

		<category><![CDATA[php]]></category>

		<category><![CDATA[api]]></category>

		<category><![CDATA[gmail]]></category>

		<guid isPermaLink="false">https://labmgr.sdc.klab.org/young/?p=1675</guid>
		<description><![CDATA[はじめまして。
KLabにインターンでお邪魔させていただいている nakatani-s です。
インターンで製作しているアプリの中で、GmailのSMTPサーバにOAuth認証で入る部分があります。

こちらにあるように、GmailはIMAP・SMTPともにOAuth認証に対応しているのですが、OAuth+IMAPのサンプルコードはあってもOAuth+SMTPのサンプルって中々見つからないんです。
そこで、OAuth+SMTP in Gmail のシンプルなPHPコードを公開して、少しでも世のお役に立てたらなと思うわけです。
いえ、僕が探した範囲なので本当はあるかもしれませんよ？でも恥ずかしいので知っている方も生温かい目でスルーしてください。
開発中にたくさんの助言・アイディアを下さり、インターンの身にこのような機会を与えてくださったKLabの方々には頭が上がらない思いです。
目次

必要な環境
下準備
動作確認
コードと解説
まとめ
参考

必要な環境
PHPが動くサーバが必要です。サーバのルートに自分でファイルが置ける必要もあります。
下準備
まずはGoogleに、サーバをOAuthのConsumerとして利用するための申請をし
ましょう。これはこちらのページを参考にしました。
OAuth Consumer Key と OAuth Consumer Secret は後で使うので、忘れないようにしておいて下さい。
次に、GoogleオフィシャルのOAuth+IMAPのプログラムを動かしてみます。
Gmail IMAP and SMTP using OAuth - Libraries and SamplesのPHP Sampleをダウンロード・解凍し、 xoauth-php-samples/common.php を開きます。
先ほど仕入れた OAuth Consumer Key と OAuth Consumer Secret の情報を、以下の部分に加えて下さい。
$THREE_LEGGED_CONSUMER_KEY = 'your_consumer_key';  // ここと
$THREE_LEGGED_SIGNATURE_METHOD = 'HMAC-SHA1';
$THREE_LEGGED_CONSUMER_SECRET_HMAC = 'your_consumer_secret';  // ここ


それが終わったら xoauth-php-samples/ ディレクトリ以下のファイルを全て自分のサーバにアップロードし、ブラウザ上から three-legged.php にアクセスしてみて下さい。
表示されたページでエディットボックスにご自分のGmailアドレスを入力し、実行ボタンを押すと、GoogleのOAuth認証画面にリダイレクトされます。
これで、&#8221;Total messages: X&#8221; と表示されたら、ここまではOKです。
ちなみに、僕はここで入力するアドレスをtypoしたせいで、社員の方を巻き込み６時間は悩みましたw
動作確認
こちらのPHPコードをダウンロードしていただき、エディタで開いて下さい。
190-195行目を、ご自分の環境に合わせて編集します。
  $from = [...]]]></description>
			<content:encoded><![CDATA[<p>はじめまして。<br />
KLabにインターンでお邪魔させていただいている nakatani-s です。</p>
<p>インターンで製作しているアプリの中で、GmailのSMTPサーバにOAuth認証で入る部分があります。<br />
<a href="http://googlecode.blogspot.com/2010/03/oauth-access-to-imapsmtp-in-gmail.html"><br />
こちら</a>にあるように、GmailはIMAP・SMTPともにOAuth認証に対応しているのですが、OAuth+IMAPのサンプルコードはあってもOAuth+SMTPのサンプルって中々見つからないんです。<br />
そこで、OAuth+SMTP in Gmail のシンプルなPHPコードを公開して、少しでも世のお役に立てたらなと思うわけです。<br />
いえ、僕が探した範囲なので本当はあるかもしれませんよ？でも恥ずかしいので知っている方も生温かい目でスルーしてください。</p>
<p>開発中にたくさんの助言・アイディアを下さり、インターンの身にこのような機会を与えてくださったKLabの方々には頭が上がらない思いです。</p>
<p><strong>目次</strong></p>
<ol>
<li>必要な環境</li>
<li>下準備</li>
<li>動作確認</li>
<li>コードと解説</li>
<li>まとめ</li>
<li>参考</li>
</ol>
<p><strong>必要な環境</strong></p>
<p>PHPが動くサーバが必要です。サーバのルートに自分でファイルが置ける必要もあります。</p>
<p><strong>下準備</strong></p>
<p>まずはGoogleに、サーバをOAuthのConsumerとして利用するための申請をし<br />
ましょう。これは<a href="http://blog.smartnetwork.co.jp/staff/node/46">こちらのページ</a>を参考にしました。<br />
OAuth Consumer Key と OAuth Consumer Secret は後で使うので、忘れないようにしておいて下さい。</p>
<p>次に、GoogleオフィシャルのOAuth+IMAPのプログラムを動かしてみます。<br />
<a href="http://code.google.com/intl/ja/apis/gmail/oauth/code.html">Gmail IMAP and SMTP using OAuth - Libraries and Samples</a>のPHP Sampleをダウンロード・解凍し、 xoauth-php-samples/common.php を開きます。<br />
先ほど仕入れた OAuth Consumer Key と OAuth Consumer Secret の情報を、以下の部分に加えて下さい。</p>
<pre class="prettyprint"><code>$THREE_LEGGED_CONSUMER_KEY = 'your_consumer_key';  // ここと
$THREE_LEGGED_SIGNATURE_METHOD = 'HMAC-SHA1';
$THREE_LEGGED_CONSUMER_SECRET_HMAC = 'your_consumer_secret';  // ここ
</code></pre>
</p>
<p>それが終わったら xoauth-php-samples/ ディレクトリ以下のファイルを<strong>全て</strong>自分のサーバにアップロードし、ブラウザ上から three-legged.php にアクセスしてみて下さい。<br />
表示されたページでエディットボックスにご自分のGmailアドレスを入力し、実行ボタンを押すと、GoogleのOAuth認証画面にリダイレクトされます。</p>
<p>これで、&#8221;Total messages: X&#8221; と表示されたら、ここまではOKです。</p>
<p>ちなみに、僕はここで入力するアドレスをtypoしたせいで、社員の方を巻き込み６時間は悩みましたw</p>
<p><strong>動作確認</strong></p>
<p><a href="http://lab.klab.org/young/wp-content/uploads/data/code/three-legged-smtp.php.txt">こちら</a>のPHPコードをダウンロードしていただき、エディタで開いて下さい。</p>
<p>190-195行目を、ご自分の環境に合わせて編集します。</p>
<pre class="prettyprint"><code>  $from = 'your_account@gmail.com'; /* OAuth認証で使ったアカウントを指定 */
  $namefrom = 'your_name';          /* ご自由に */
  $to = 'someone@someserver.foo';       /* 送り先 */
  $nameto = 'someone';              /* 送り相手の名前 */
  $subject = 'test mail';           /* 件名 */
  $message = 'OAuth+SMTP test';     /* 本文 */
</code></pre>
</p>
<p>これが終わったら、サーバ上の common.php や three-legged.php と同じディレクトリ上に置いて下さい。ブラウザから three-legged-smtp.php にアクセスすると、指定した送信先にメールが届くはずです。</p>
<p><strong>コードと解説</strong></p>
<p>three-legged-smtp.php の解説をさせていただきます。<br />
お気づきな方はお気づきでしょうが、ほぼ three-legged.php と同内容です。<br />
内部で行われている処理の流れを簡単に説明すると、</p>
<ol>
<li>OAuth認証をし、Googleから認証情報をもらう(1-113行目)</li>
<li>もらった認証情報を使ってssl通信を開始し、SMTPプロトコルで通信する権利を得る(114-187行目)</li>
<li>SMTPプロトコルでメールを送る(190-212行目)</li>
<li>Googleとのコネクションをクローズする(124-126行目)</li>
</ol>
<p>という感じです。<br />
この「OAuth認証をし、Googleから認証情報をもらう(1-113行目)」と「もらった認証情報を使ってssl通信を開始し、SMTPプロトコルで通信する権利を得る(114-187行目)」の部分が、OAuth+IMAP通信を行っていた three-legged.phpとほぼ共通なので、ほぼ同内容となっています。</p>
<p>さて、元のコードから追加・変更・削除した部分を説明します。</p>
<p>22-23行目: インクルードファイルの変更</p>
<pre class="prettyprint"><code>require_once 'Zend/Mail/Protocol/Smtp.php'; /* Imap.php -> Smtp.php */
// require_once 'Zend/Mail/Storage/Imap.php';  /* 必要無し */
</code></pre>
</p>
<p>121-123行目: ssl通信時に送るURLを、SMTP用のものに変更</p>
<pre class="prettyprint"><code>  $url = 'https://mail.google.com/mail/b/' .
       $email_address .
       '/smtp/';                /* imap -> smtp */
</code></pre>
</p>
<p>167-216行目: コメント参照。<br />
要は、<a  href="http://code.google.com/intl/ja/apis/gmail/oauth/protocol.html">Googleのドキュメント</a>に従ってssl通信をしているだけです。</p>
<pre class="prettyprint"><code>  /* GmailのSMTPサーバと通信するためのsocketを開く */
  $smtpServer = "tls://smtp.gmail.com";
  $port = "465";                /* 465番ポートでうまくいかなかったら587番も試してみてください */
  $timeout = "45";
  $localhost = $_SERVER['REMOTE_ADDR'];
  $newLine = "\r\n";

  $smtpConnect = fsockopen($smtpServer, $port, $errno, $errstr, $timeout);

  $smtpResponse = fgets($smtpConnect, 4096);

  /* SMTP+OAuthの認証手続き */
  /* http://code.google.com/intl/ja/apis/gmail/oauth/protocol.html に規格があります */
  fputs($smtpConnect, "HELO $localhost". $newLine);
  $smtpResponse = fgets($smtpConnect, 4096);

  fputs($smtpConnect,"AUTH XOAUTH" . $newLine);
  $smtpResponse = fgets($smtpConnect, 4096);

  fputs($smtpConnect, $initClientRequestEncoded . $newLine);
  $smtpResponse = fgets($smtpConnect, 4096);

  /* 認証終了後にメールを送る */
  $from = 'your_account@gmail.com'; /* OAuth認証で使ったアカウントを指定 */
  $namefrom = 'your_name';          /* ご自由に */
  $to = 'someone@someserver.foo';       /* 送り先 */
  $nameto = 'someone';              /* 送り相手の名前 */
  $subject = 'test mail';           /* 件名 */
  $message = 'OAuth+SMTP test';     /* 本文 */

  fputs($smtpConnect, "MAIL FROM: <$from>" . $newLine);
  $smtpResponse = fgets($smtpConnect, 4096);

  fputs($smtpConnect, "RCPT TO: <$to>" . $newLine);
  $smtpResponse = fgets($smtpConnect, 4096);

  fputs($smtpConnect, "DATA" . $newLine);
  $smtpResponse = fgets($smtpConnect, 4096);

  $headers = "MIME-Version: 1.0" . $newLine;
  $headers .= "Content-type: text/html; charset=iso-8859-1" . $newLine; /* charsetはお好みで */
  $headers .= "To: $nameto $to1" . $newLine;
  $headers .= "From: $namefrom <$from>" . $newLine;

  fputs($smtpConnect, "To: <$to>\r\nFrom: $from\r\nSubject: $subject\r\n$headers\r\n\r\n$message\r\n.\r\n");
  $smtpResponse = fgets($smtpConnect, 4096);

  fputs($smtpConnect,"QUIT" . $newLine);
  $smtpResponse = fgets($smtpConnect, 4096);
  fclose($smtpConnect);
</code></pre>
</p>
<p>218-245行目: OAuth+IMAPでのメール受信に固有の部分をコメントアウト</p>
<pre class="prettyprint"><code>  /* IMAP用の認証なので必要無し */
  /**
   * Make the IMAP connection and send the auth request
   */
  /* $imap = new Zend_Mail_Protocol_Imap('imap.gmail.com', '993', true); */
  /* $authenticateParams = array('XOAUTH', $initClientRequestEncoded); */
  /* $imap->requestAndResponse('AUTHENTICATE', $authenticateParams); */

  /* 以下、GMailからメールを受信して表示する部分なので必要無し */
  /**
   * Print the INBOX message count and the subject of all messages
   * in the INBOX
   */
  /* $storage = new Zend_Mail_Storage_Imap($imap); */

  /* include 'header.php';  */
  /* echo '&lt;h1&gt;Total messages: ' . $storage->countMessages() . "&lt;/h1&gt;\n"; */

  /**
   * Retrieve first 5 messages.  If retrieving more, you'll want
   * to directly use Zend_Mail_Protocol_Imap and do a batch retrieval,
   * plus retrieve only the headers
   */
  /* echo 'First five messages:
<ul>'; */
  /* for ($i = 1; $i <= $storage->countMessages() &#038;&#038; $i <= 5; $i++ ){  */
  /*   echo '
<li>' . htmlentities($storage->getMessage($i)->subject) . "</li>

\n"; */
  /* } */
  /* echo '</ul>

'; */
</code></pre>
</p>
<p><strong>まとめ</strong></p>
<p>いかがでしたか？ Google Code に置いてあるコードをベースに、OAuth+SMTP認証ができました。<br />
しかし、このコードでは Google からOAuth認証情報を得る部分が PHP のZend Framework の内部に隠されてしまっています。<br />
インターン中は時間がないのですが、なるべくいろいろな言語に流用できるように認証情報取得部分もホワイトボックス化したいと思います。<br />
もしできたら、<a href="http://lay-sakura.blogspot.com/">僕の個人ブログ</a>にでもコードを書きたいと思っています。</p>
<p>最後までお読み下さりありがとうございました。</p>
<p><strong>ソースコード</strong></p>
<p><a href="http://lab.klab.org/young/wp-content/uploads/data/code/three-legged-smtp.php.txt">http://lab.klab.org/young/wp-content/uploads/data/code/three-legged-smtp.php.txt</a></p>
<p><strong>参考</strong></p>
<ul>
<li><a href="http://www.daniweb.com/forums/thread105510.html">smtp mail in php - PHP</a></li>
<li><a href="http://gihyo.jp/dev/feature/01/oauth/0001">ゼロから学ぶ<br />
     OAuth：第1回　OAuthとは？―OAuthの概念とOAuthでできるこ<br />
     と｜gihyo.jp … 技術評論社</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://lab.klab.org/young/2010/08/php-%e3%81%a7-gmail-%e3%81%ae-oauthsmtp-%e3%82%92%e4%bd%bf%e3%81%86/feed/</wfw:commentRss>
		</item>
		<item>
		<title>第1回チキチキIRCボットファイターズ！ 祭りの後</title>
		<link>http://lab.klab.org/young/2010/08/%e7%ac%ac1%e5%9b%9e%e3%83%81%e3%82%ad%e3%83%81%e3%82%adirc%e3%83%9c%e3%83%83%e3%83%88%e3%83%95%e3%82%a1%e3%82%a4%e3%82%bf%e3%83%bc%e3%82%ba%ef%bc%81-%e7%a5%ad%e3%82%8a%e3%81%ae%e5%be%8c/</link>
		<comments>http://lab.klab.org/young/2010/08/%e7%ac%ac1%e5%9b%9e%e3%83%81%e3%82%ad%e3%83%81%e3%82%adirc%e3%83%9c%e3%83%83%e3%83%88%e3%83%95%e3%82%a1%e3%82%a4%e3%82%bf%e3%83%bc%e3%82%ba%ef%bc%81-%e7%a5%ad%e3%82%8a%e3%81%ae%e5%be%8c/#comments</comments>
		<pubDate>Mon, 30 Aug 2010 04:08:43 +0000</pubDate>
		<dc:creator>sasaki-k</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">https://labmgr.sdc.klab.org/young/?p=1672</guid>
		<description><![CDATA[ご無沙汰してました! sasaki-k です。
遅れてしまい申し訳ございません。先日ご案内した表題の「チキチキIRCボットファイターズ!」社内投票と読者の皆様のはてなブックマークと、いいね！を合計した結果をご報告いたします。


1位  最終兵器 Haskell によるゲームボット(takada-at)
2位 Jsminブラウザで動くIRCボット(osuga-h)
3位 Monty the Python bot (nakazawa-k)

以前 takada-atさんが開発したAIが社内でも話題になっていたのですが、そのIRC版が好評だったようです。またはてなブックマーク、「いいね!」ボタンも1票づついただけて文句なしの1位でした！ちょっとした笑いを誘う粗品を贈呈させていただきましたー。
2位のosuga-hさんは締切り過ぎてしまったのですが、JavaScriptでボットを作るということが新鮮で、かつはてなブックマークも1票いただいて見事2位となりました。
3位のnakazawa-kさんのボットもご本人の熱い思いとともに、社内に増殖しつつある(?)pythonista達に火をつけたようです(?)で3位となりました。
エントリーいただいた皆様、ブックマークやいいねボタンを押していただいた皆様どうもありがとうございました！！
最後となりましたが、私の拙い案内文のためKLab社外からのエントリーも可能と誤解を受けてしまった方もいらっしゃったようです。実際には社内イベントでした。この場をお借りしてお詫び申し上げます。
]]></description>
			<content:encoded><![CDATA[<p>ご無沙汰してました! sasaki-k です。</p>
<p>遅れてしまい申し訳ございません。先日ご案内した表題の「チキチキIRCボットファイターズ!」社内投票と読者の皆様のはてなブックマークと、いいね！を合計した結果をご報告いたします。<br />
<span id="more-1672"></span></p>
<ul>
<li>1位  <a href="http://lab.klab.org/young/2010/08/irc%E3%83%9C%E3%83%83%E3%83%88%E3%82%B3%E3%83%B3%E3%83%86%E3%82%B9%E3%83%88%E3%82%A8%E3%83%B3%E3%83%AA-%E6%9C%80%E7%B5%82%E5%85%B5%E5%99%A8haskell%E3%81%AB%E3%82%88%E3%82%8B%E3%82%B2%E3%83%BC%E3%83%A0/">最終兵器 Haskell によるゲームボット(takada-at)</a>
<li>2位 <a href="http://lab.klab.org/young/2010/08/irc%E3%83%9C%E3%83%83%E3%83%88%E3%82%B3%E3%83%B3%E3%83%86%E3%82%B9%E3%83%88%E3%82%A8%E3%83%B3%E3%83%88%E3%83%AA-jsmin-%E3%83%96%E3%83%A9%E3%82%A6%E3%82%B6%E3%81%A7%E5%8B%95%E3%81%8Firc%E3%83%9C/">Jsminブラウザで動くIRCボット(osuga-h)</a>
<li>3位 <a href="http://lab.klab.org/young/2010/08/irc%E3%83%9C%E3%83%83%E3%83%88%E3%82%B3%E3%83%B3%E3%83%86%E3%82%B9%E3%83%88%E3%82%A8%E3%83%B3%E3%83%88%E3%83%AA-monty-the-python-bot/">Monty the Python bot (nakazawa-k)</a>
</ul>
<p>以前 takada-atさんが開発したAIが社内でも話題になっていたのですが、そのIRC版が好評だったようです。またはてなブックマーク、「いいね!」ボタンも1票づついただけて文句なしの1位でした！ちょっとした笑いを誘う粗品を贈呈させていただきましたー。</p>
<p>2位のosuga-hさんは締切り過ぎてしまったのですが、JavaScriptでボットを作るということが新鮮で、かつはてなブックマークも1票いただいて見事2位となりました。</p>
<p>3位のnakazawa-kさんのボットもご本人の熱い思いとともに、社内に増殖しつつある(?)pythonista達に火をつけたようです(?)で3位となりました。</p>
<p>エントリーいただいた皆様、ブックマークやいいねボタンを押していただいた皆様どうもありがとうございました！！</p>
<p>最後となりましたが、私の拙い案内文のためKLab社外からのエントリーも可能と誤解を受けてしまった方もいらっしゃったようです。実際には社内イベントでした。この場をお借りしてお詫び申し上げます。</p>
]]></content:encoded>
			<wfw:commentRss>http://lab.klab.org/young/2010/08/%e7%ac%ac1%e5%9b%9e%e3%83%81%e3%82%ad%e3%83%81%e3%82%adirc%e3%83%9c%e3%83%83%e3%83%88%e3%83%95%e3%82%a1%e3%82%a4%e3%82%bf%e3%83%bc%e3%82%ba%ef%bc%81-%e7%a5%ad%e3%82%8a%e3%81%ae%e5%be%8c/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Twitterで140文字の制限を超えてみせる？？？</title>
		<link>http://lab.klab.org/young/2010/08/twitter%e3%81%a7140%e6%96%87%e5%ad%97%e3%81%ae%e5%88%b6%e9%99%90%e3%82%92%e8%b6%85%e3%81%88%e3%81%a6%e3%81%bf%e3%81%9b%e3%82%8b%ef%bc%9f%ef%bc%9f%ef%bc%9f/</link>
		<comments>http://lab.klab.org/young/2010/08/twitter%e3%81%a7140%e6%96%87%e5%ad%97%e3%81%ae%e5%88%b6%e9%99%90%e3%82%92%e8%b6%85%e3%81%88%e3%81%a6%e3%81%bf%e3%81%9b%e3%82%8b%ef%bc%9f%ef%bc%9f%ef%bc%9f/#comments</comments>
		<pubDate>Fri, 27 Aug 2010 10:54:53 +0000</pubDate>
		<dc:creator>sano-n</dc:creator>
		
		<category><![CDATA[Java]]></category>

		<category><![CDATA[GAE]]></category>

		<category><![CDATA[HTML5]]></category>

		<category><![CDATA[twitpic]]></category>

		<category><![CDATA[twitter]]></category>

		<category><![CDATA[twitter4j]]></category>

		<guid isPermaLink="false">https://labmgr.sdc.klab.org/young/?p=1646</guid>
		<description><![CDATA[umjammer です、
先日 Twitter にて 140 文字以上投稿されるという事件が起きました。もうそのバグは塞がれたみたいですが、人間制限を設けられるとやはり超えてみたいと思うものです。じゃあ超えてみせましょう(笑)
140文字以上tweetできるTwitter利用サイト、その名も「Too言ったー」。

http://umjammer09.appspot.com/
まず Twitter のアカウントは取得しておいてください。初めて使用する場合には「signin」のリンクをクリックしてください。OAuth を用いた例の認証が行われるので、許可してあげてください。
signin 後、サイトのテキストボックスに長文を入れて「Too言っとく」ボタンを押せば140文字の制限を超えて投稿できます。投稿後しばらくすると「成功しました」のダイアログが出ます。「Preview」ボタンは投稿する前にイメージを把握することができます。投稿後は中程の「View Your Post」リンクをクリックすれば今投稿したものを見ることができます。
Twitter ユーザなら何をしているか一目瞭然ですね。
改行とかエラー処理を真面目に行っていないので何か問題があってもご容赦ください。
まとめ
冗談みたいなサービスですが、結構新しめの技術を使っています。
HTML5 の canvas を使用しています。canvas にテキストエリアに入力した文字をレンダリングして toDataURL メソッドで画像にして GAE に送ってそこから API を使って投稿しています。ブラウザによっては動かない可能性があります。
JavaScript は苦手なので GWT と gwt-g2d を使用しました。本家の canvas を使用せずに gwt-g2d を使用したのは本家には文字を描くメソッドがなかった(ハズ)からです。GWT は楽ですね。もう少し言うなら canvas 周りのグラフィックス API を早く統一して欲しいです、出来れば Java 2D 準拠で。Google さん主導なんだから最低でも android API 準拠にしてくれればと思います。
ソースはこちら。
参考サイト

http://sites.google.com/site/elekmole/twitter4jtop/00-preparation/twitpic-yfrog
http://zenjiro.wordpress.com/2010/06/19/gaej%E3%81%A8twitter4j%E3%81%A7twitter%E3%81%AEoauth/
http://d.hatena.ne.jp/favril/20100316/1268725431

補足
私も Twitter ユーザですし、140 文字だからこそ Twitter の文化があるということは十分理解しています。あくまで技術検証を目的とした洒落のサービスだということをご理解願います。
]]></description>
			<content:encoded><![CDATA[<p>umjammer です、</p>
<p>先日 Twitter にて <a href="http://jp.techcrunch.com/archives/20100814long-tweet-is-long-t-co-bug-lets-you-go-way-over-140/">140 文字以上投稿されるという事件</a>が起きました。もうそのバグは塞がれたみたいですが、人間制限を設けられるとやはり超えてみたいと思うものです。じゃあ超えてみせましょう(笑)</p>
<p>140文字以上tweetできるTwitter利用サイト、その名も「Too言ったー」。</p>
<p><a href="http://umjammer09.appspot.com"><img alt="toowitter" src="http://umjammer09.appspot.com/toowitter.png" title="toowitter" class="alignnone" width="204" height="76" style="background-color:#c8edf5"/></a></p>
<p><a href="http://umjammer09.appspot.com/">http://umjammer09.appspot.com/</a></p>
<p>まず Twitter のアカウントは取得しておいてください。初めて使用する場合には「signin」のリンクをクリックしてください。OAuth を用いた例の認証が行われるので、許可してあげてください。<br />
signin 後、サイトのテキストボックスに長文を入れて「Too言っとく」ボタンを押せば140文字の制限を超えて投稿できます。投稿後しばらくすると「成功しました」のダイアログが出ます。「Preview」ボタンは投稿する前にイメージを把握することができます。投稿後は中程の「View Your Post」リンクをクリックすれば今投稿したものを見ることができます。<br />
Twitter ユーザなら何をしているか一目瞭然ですね。<br />
改行とかエラー処理を真面目に行っていないので何か問題があってもご容赦ください。</p>
<p><strong>まとめ</strong><br />
冗談みたいなサービスですが、結構新しめの技術を使っています。</p>
<p>HTML5 の canvas を使用しています。canvas にテキストエリアに入力した文字をレンダリングして toDataURL メソッドで画像にして GAE に送ってそこから <a href="http://twitter4j.org/ja/index.html">API</a> を使って投稿しています。ブラウザによっては動かない可能性があります。</p>
<p>JavaScript は苦手なので GWT と <a href="http://code.google.com/p/gwt-g2d/">gwt-g2d</a> を使用しました。<a href="http://code.google.com/p/google-web-toolkit-incubator/wiki/GWTCanvas">本家の canvas</a> を使用せずに gwt-g2d を使用したのは本家には文字を描くメソッドがなかった(ハズ)からです。GWT は楽ですね。もう少し言うなら canvas 周りのグラフィックス API を早く統一して欲しいです、出来れば Java 2D 準拠で。Google さん主導なんだから最低でも android API 準拠にしてくれればと思います。</p>
<p>ソースは<a href="http://code.google.com/p/umjammer/source/browse/trunk/vavi-apps-gae09">こちら</a>。</p>
<p><strong>参考サイト</strong></p>
<ul>
<li><a href="http://sites.google.com/site/elekmole/twitter4jtop/00-preparation/twitpic-yfrog">http://sites.google.com/site/elekmole/twitter4jtop/00-preparation/twitpic-yfrog</a></li>
<li><a href="http://zenjiro.wordpress.com/2010/06/19/gaej%E3%81%A8twitter4j%E3%81%A7twitter%E3%81%AEoauth/">http://zenjiro.wordpress.com/2010/06/19/gaej%E3%81%A8twitter4j%E3%81%A7twitter%E3%81%AEoauth/</a></li>
<li><a href="http://d.hatena.ne.jp/favril/20100316/1268725431">http://d.hatena.ne.jp/favril/20100316/1268725431</a></li>
</ul>
<p><strong>補足</strong><br />
私も Twitter ユーザですし、140 文字だからこそ Twitter の文化があるということは十分理解しています。あくまで技術検証を目的とした洒落のサービスだということをご理解願います。</p>
]]></content:encoded>
			<wfw:commentRss>http://lab.klab.org/young/2010/08/twitter%e3%81%a7140%e6%96%87%e5%ad%97%e3%81%ae%e5%88%b6%e9%99%90%e3%82%92%e8%b6%85%e3%81%88%e3%81%a6%e3%81%bf%e3%81%9b%e3%82%8b%ef%bc%9f%ef%bc%9f%ef%bc%9f/feed/</wfw:commentRss>
		</item>
		<item>
		<title>IRCボットコンテストエントリ: Jsmin ブラウザで動くIRCボット</title>
		<link>http://lab.klab.org/young/2010/08/irc%e3%83%9c%e3%83%83%e3%83%88%e3%82%b3%e3%83%b3%e3%83%86%e3%82%b9%e3%83%88%e3%82%a8%e3%83%b3%e3%83%88%e3%83%aa-jsmin-%e3%83%96%e3%83%a9%e3%82%a6%e3%82%b6%e3%81%a7%e5%8b%95%e3%81%8firc%e3%83%9c/</link>
		<comments>http://lab.klab.org/young/2010/08/irc%e3%83%9c%e3%83%83%e3%83%88%e3%82%b3%e3%83%b3%e3%83%86%e3%82%b9%e3%83%88%e3%82%a8%e3%83%b3%e3%83%88%e3%83%aa-jsmin-%e3%83%96%e3%83%a9%e3%82%a6%e3%82%b6%e3%81%a7%e5%8b%95%e3%81%8firc%e3%83%9c/#comments</comments>
		<pubDate>Thu, 19 Aug 2010 13:24:03 +0000</pubDate>
		<dc:creator>osuga-h</dc:creator>
		
		<category><![CDATA[JavaScript]]></category>

		<guid isPermaLink="false">https://labmgr.sdc.klab.org/young/?p=1637</guid>
		<description><![CDATA[太平洋高気圧、お前本気出しすぎ。
あとちょっとだけ手加減してもたぶん大丈夫だぞ。
夏いですね。
入社して10日ちょっとのosuga-hです。よろしくお願いします。
締め切りを過ぎてしまいましたが、自分もIRCボットコンテストに向けてブラウザ(Chrome)で動作し(ロジックは)Javascriptで書かれた、Botを作ったので紹介させてください。
普段はBotのような、機能や性格が重要なものは作らないのですが、「そういえばJavascriptで書かれたBotって聞いたことないぞ」と思い立ってしまったので、実装しました。
たまには手段のために目的を選らばなくったっていいじゃない！
■Jsmin
ジャスミンと名づけたこのボットは以下のような機能を持っています。

ハードコーディングされた人口無能
[human]あーなるほどね
[Jsmin]本当にわかってるの？
IRC経由で人口無能のルールを追加する機能
[human]add りんご ごりら
[human]りんご
[Jsmin]ごりら
Javascriptを実行する機能
[human]exp function add( a , b ){ return a + b ; } add( 1 , 2 )
[Jsmin]3
Javascriptをブラウザコンテキストで実行する機能
[human]forceEval alert( &#8220;moge&#8221; )
俺の端末に突如alertによるダイアログがポップアップする 

4つ目はお遊びですね。
Jsminは以下の2つのポリシーで実装しました。

ブラウザで動く
ロジックはJSで書く

あとは簡単に実装方法を紹介します。
■IRCのプロトコル部分
ソケット通信を行う必要があり、またIRCのプロトコルも喋らなければならず、ここを1から実装するのはつらかったので
その昔所属していた組織でのっぴきならない事情があったためIRCクライアントを自作したときに使った flexircclient というActionScriptのライブラリにJSとのインターフェースを実装しました。
なのでPure Javascriptではありません。
HTML5ならWebSocketあるじゃんという話もあるのですが、Handshakeなどで問題がおきそうだったので今回は完全に選択肢から除外しました。
■人口無能ルールの追加
送られてきたルールはlocalStorageに保存しています。
なので、ブラウザを閉じても学習（？）成果は保存されます。
また、Chromeでは開発者ツールを使うことでlocalStorageの中身をいじくれるので、メンテナンスも簡単です。
■Javascriptコードの実行
危険なAPIの呼び出しなどをしてほしくないので安全なコンテキストで送られてきたJSのコードを評価したいですね。
そのために今回はWebWorkerを使いました。
WebWorkerの中ではdocumentやwindowといったオブジェクトにアクセスできません。
このWebWorkerをサンドボックスとして使うアイデアはこちらで紹介されていました。
ただwhile(1){}とかやられると、困ります。
やるなよ絶対やるなよ。
■ブラウザコンテキストでのコード実行
この機能は、location.href=&#8221;http://www.google.com&#8221;とかやられて、ボットがIRCから出て行ってしまったり、
while(true){alert(&#8221;＼(＾o＾)／&#8221;);}とかやられて俺の作業が妨害されたりしたら、面白いんじゃねーの？
っていうためだけに存在しています。
実現の方法はただただevalするだけです。
なんのチェックもしてません。
■ソースコード
SWFとかもあるので動作に必要なものはこちらにまとめておきました。
興味のある方はご覧ください。
JSの部分だけですがソースを貼り付けておきます。
//Flash initialization
var flashvars = { };
    params = {
        menu: "false",
        scale: "noScale",
 [...]]]></description>
			<content:encoded><![CDATA[<p>太平洋高気圧、お前本気出しすぎ。<br />
あとちょっとだけ手加減してもたぶん大丈夫だぞ。</p>
<p>夏いですね。</p>
<p>入社して10日ちょっとのosuga-hです。よろしくお願いします。</p>
<p>締め切りを過ぎてしまいましたが、自分もIRCボットコンテストに向けてブラウザ(Chrome)で動作し(ロジックは)Javascriptで書かれた、Botを作ったので紹介させてください。</p>
<p><span id="more-1637"></span>普段はBotのような、機能や性格が重要なものは作らないのですが、「そういえばJavascriptで書かれたBotって聞いたことないぞ」と思い立ってしまったので、実装しました。</p>
<p>たまには手段のために目的を選らばなくったっていいじゃない！</p>
<h3>■Jsmin</h3>
<p>ジャスミンと名づけたこのボットは以下のような機能を持っています。</p>
<ol>
<li>ハードコーディングされた人口無能<br />
[human]あーなるほどね<br />
[Jsmin]本当にわかってるの？</li>
<li>IRC経由で人口無能のルールを追加する機能<br />
[human]add りんご ごりら<br />
[human]りんご<br />
[Jsmin]ごりら</li>
<li>Javascriptを実行する機能<br />
[human]exp function add( a , b ){ return a + b ; } add( 1 , 2 )<br />
[Jsmin]3</li>
<li><strong>Javascriptをブラウザコンテキストで実行する機能<br />
[human]forceEval alert( &#8220;moge&#8221; )<br />
俺の端末に突如alertによるダイアログがポップアップする </strong></li>
</ol>
<p>4つ目はお遊びですね。</p>
<p>Jsminは以下の2つのポリシーで実装しました。</p>
<ul>
<li>ブラウザで動く</li>
<li>ロジックはJSで書く</li>
</ul>
<p>あとは簡単に実装方法を紹介します。</p>
<h3>■IRCのプロトコル部分</h3>
<p>ソケット通信を行う必要があり、またIRCのプロトコルも喋らなければならず、ここを1から実装するのはつらかったので<br />
その昔所属していた組織でのっぴきならない事情があったためIRCクライアントを自作したときに使った <a href="http://code.google.com/p/flexircclient/">flexircclient</a> というActionScriptのライブラリにJSとのインターフェースを実装しました。</p>
<p>なのでPure Javascriptではありません。</p>
<p>HTML5ならWebSocketあるじゃんという話もあるのですが、Handshakeなどで問題がおきそうだったので今回は完全に選択肢から除外しました。</p>
<h3>■人口無能ルールの追加</h3>
<p>送られてきたルールはlocalStorageに保存しています。<br />
なので、ブラウザを閉じても学習（？）成果は保存されます。</p>
<p>また、Chromeでは開発者ツールを使うことでlocalStorageの中身をいじくれるので、メンテナンスも簡単です。</p>
<h3>■Javascriptコードの実行</h3>
<p>危険なAPIの呼び出しなどをしてほしくないので安全なコンテキストで送られてきたJSのコードを評価したいですね。<br />
そのために今回はWebWorkerを使いました。<br />
WebWorkerの中ではdocumentやwindowといったオブジェクトにアクセスできません。</p>
<p>このWebWorkerをサンドボックスとして使うアイデアは<a href="http://d.hatena.ne.jp/yanagia/20100406/1270566471">こちら</a>で紹介されていました。</p>
<p>ただwhile(1){}とかやられると、困ります。<br />
やるなよ絶対やるなよ。</p>
<h3>■ブラウザコンテキストでのコード実行</h3>
<p>この機能は、location.href=&#8221;http://www.google.com&#8221;とかやられて、ボットがIRCから出て行ってしまったり、<br />
while(true){alert(&#8221;＼(＾o＾)／&#8221;);}とかやられて俺の作業が妨害されたりしたら、面白いんじゃねーの？<br />
っていうためだけに存在しています。</p>
<p>実現の方法はただただevalするだけです。<br />
なんのチェックもしてません。</p>
<h3>■ソースコード</h3>
<p>SWFとかもあるので動作に必要なものは<a href="http://lab.klab.org/young/wp-content/uploads/data/code/jsmin_bot.zip">こちら</a>にまとめておきました。<br />
興味のある方はご覧ください。</p>
<p>JSの部分だけですがソースを貼り付けておきます。</p>
<pre class="prettyprint"><code>//Flash initialization
var flashvars = { };
    params = {
        menu: "false",
        scale: "noScale",
        allowFullscreen: "false",
        allowScriptAccess: "always",
        bgcolor: "#FFFFFF"
    },
    attributes = { id:"FlexIRCClient" };

swfobject.embedSWF("FlexIRCClient.swf", "altContent", "1px", "1px", "9.0.0", "expressInstall.swf", flashvars, params, attributes);

//Util
function log( text ){ document.getElementById("cmd").innerHTML = text + "
" + document.getElementById("cmd").innerHTML  ; }
function $ (){ return document.getElementById.apply( document , arguments ); }

//Interface for swf
//  initialized after onReady event
var client ;

//------------------------------------------------------
// Event handlers of SWF

//SWF is on ready to connect IRC
function onReady(){
    client = document.FlexIRCClient ;
    $("message").innerHTML = "";
    $("form").style.display = "block";

}
function connect(){
    $("message").innerHTML = "connecting..." ;
    $("form").style.display = "none" ;
    client.connect( $("name").value , $("host").value , $("channel").value ) ;
}

//entered to channel
function onEnterChannel( ){
    $("message").innerHTML = $("name").value + "entered to channel '" + $("channel").value + "'" ;
    mergeLocalStorage();
}

//on recieve message
function onMessage( message ){
    setTimeout( function(){
        var i , n , func ;
        for( i = 0 , n = reg.length ; i &lt; n ; i++ ){
            if( message.match( reg[i][0] ) ){
                func = reg[i][1] ;
                var a = reg[i].slice( 2 );
                a.push( message );

                func.apply( this , a );
                break;
            }else{

            }
        }
    },10 );
}

//------------------------------------------------------
// Bot settings
var reg = [
    [ /なるほどね?。?$/ , muno          , "本当にわかってるの？" ] ,
    [ /ってことか。?$/  , muno          , "マジで！？"           ] ,
    [ /^実は/           , muno          , "マジで！？"           ] ,
    [ /^add/            , addRule       ] ,
    [ /^exp/            , exp           ] ,
    [ /^forceEval/      , forceEval     ]
] ,
sandbox = new Worker( "sandbox.worker.js" ); //webworker for sandbox
sandbox.onmessage = function ( event ){      //on receive result from webworker
    data = event.data ;
    client.send( data ); //send result to irc
}

function muno( text ){
    client.send( text );
}

function exp( text ){
    var code = text.replace( "exp" , " "); //parse irc message

    sandbox.postMessage( code ); //pass code to webworker
}

function forceEval( text ){
    var code = text.replace( "forceEval " , " "); //parse irc message

    try{
        var result = eval( code ); //run code
        client.send( result );
    }catch( e ){
        client.send( e.toString() );
    }
}

function addRule( message ){
    var param = message.replace( "add " , "" ).split( " " ); //parse irc message
    if( param.length != 2 ){ return; }

    localStorage[ param[0] ] = param[1] ; // save to localStorage
    reg.push( [ param[0] , muno , param[1] ] );
}

/**
 * load rules from localStorage
 */
function mergeLocalStorage(){
    var key , val ;
    for( key in localStorage ){
        val = localStorage[key];
        reg.push( [ key , muno , val ] );
    }
}
</code></pre>
]]></content:encoded>
			<wfw:commentRss>http://lab.klab.org/young/2010/08/irc%e3%83%9c%e3%83%83%e3%83%88%e3%82%b3%e3%83%b3%e3%83%86%e3%82%b9%e3%83%88%e3%82%a8%e3%83%b3%e3%83%88%e3%83%aa-jsmin-%e3%83%96%e3%83%a9%e3%82%a6%e3%82%b6%e3%81%a7%e5%8b%95%e3%81%8firc%e3%83%9c/feed/</wfw:commentRss>
		</item>
		<item>
		<title>IRCボットコンテストエントリ: Monty the Python bot</title>
		<link>http://lab.klab.org/young/2010/08/irc%e3%83%9c%e3%83%83%e3%83%88%e3%82%b3%e3%83%b3%e3%83%86%e3%82%b9%e3%83%88%e3%82%a8%e3%83%b3%e3%83%88%e3%83%aa-monty-the-python-bot/</link>
		<comments>http://lab.klab.org/young/2010/08/irc%e3%83%9c%e3%83%83%e3%83%88%e3%82%b3%e3%83%b3%e3%83%86%e3%82%b9%e3%83%88%e3%82%a8%e3%83%b3%e3%83%88%e3%83%aa-monty-the-python-bot/#comments</comments>
		<pubDate>Wed, 18 Aug 2010 14:11:21 +0000</pubDate>
		<dc:creator>nakazawa-k</dc:creator>
		
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">https://labmgr.sdc.klab.org/young/?p=1634</guid>
		<description><![CDATA[こんばんは、最近なんだかバテ気味の若手ブログ初登場、nakazawa-kです。
よろしくお願いします。
どうやらIRCボットのコンテストをやるらしいと聞いて息巻き、大急ぎでざくっと実装してから実に2ヶ月ほど寝かせてしまったボットを投下してみます。
最初のお題が出た瞬間に思いついたのが「Pythonボット」でした。『Pythonで書かれた』という意味ではなく『Pythonを実行してくれる』ということです。

(nakazawa-k) >>>print "hello!"
(bot) hello!

こういう風にIRCがPythonコードであふれたら素敵だと思いませんか!? 私は思います。作りましょう。これで機能が決まりました。
次は超重要、名前です。
みなさんパイソンといえば何を思い浮かべるでしょう。
ニシキヘビ? いえいえパイソンといえばモンティ・パイソンです。テリー・ギリアムです。
Pythonを実行してくれるボットの名前にモンティ以上のものはないでしょう。
ということでPythonで書かれたPythonコードを実行してくれるモンティボットを作ってみました。

実装を始めるにあたり、まず目標をKLabの社内にある「どぶろく制度」を使って『1時間以内にサクッと動くようにする』と設定しました。
ちなみに「どぶろく制度」とは標準業務時間の10%を好きに使い、上司に断ることなく自分の興味が赴くまま研究や開発を行えるというものです。Googleの20%ルールや3Mの15%ルール(こちらは不文律ですね)と似たものです。
標準業務時間の10%とは、だらだらとやっていてはすぐに過ぎ去ってしまう位です。KLab入社間もなかったnakazawa-kにとって、IRCボットは打って付けのネタでした。
閑話休題。目標が決まったのでとにかくシンプルな実装を目指していきました。
IRCのプロトコルにも多少興味はあったのでRFC1459を流し読みし、その上でIRC接続ライブラリとしてPython IRC libraryを利用しました。
※ソースは記事の最後に貼り付けてあります。
使い方

$ ./monty.py server[:port] #channel nickname

いじり方

(nakazawa-k) >>>self.v = range(1,101)
(nakazawa-k) >>>print self.v
(monty) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, [...]]]></description>
			<content:encoded><![CDATA[<p>こんばんは、最近なんだかバテ気味の若手ブログ初登場、nakazawa-kです。</p>
<p>よろしくお願いします。</p>
<p>どうやらIRCボットのコンテストをやるらしいと聞いて息巻き、大急ぎでざくっと実装してから実に2ヶ月ほど寝かせてしまったボットを投下してみます。</p>
<p>最初のお題が出た瞬間に思いついたのが「Pythonボット」でした。『Pythonで書かれた』という意味ではなく『Pythonを実行してくれる』ということです。</p>
<pre>
(nakazawa-k) >>>print "hello!"
(bot) hello!
</pre>
<p>こういう風にIRCがPythonコードであふれたら素敵だと思いませんか!? 私は思います。作りましょう。これで機能が決まりました。</p>
<p>次は超重要、名前です。</p>
<p>みなさんパイソンといえば何を思い浮かべるでしょう。<br />
ニシキヘビ? いえいえパイソンといえばモンティ・パイソンです。テリー・ギリアムです。<br />
Pythonを実行してくれるボットの名前にモンティ以上のものはないでしょう。</p>
<p>ということでPythonで書かれたPythonコードを実行してくれるモンティボットを作ってみました。</p>
<p><span id="more-1634"></span></p>
<p>実装を始めるにあたり、まず目標をKLabの社内にある<strong>「どぶろく制度」</strong>を使って『1時間以内にサクッと動くようにする』と設定しました。</p>
<p>ちなみに「どぶろく制度」とは標準業務時間の10%を好きに使い、上司に断ることなく自分の興味が赴くまま研究や開発を行えるというものです。Googleの20%ルールや3Mの15%ルール(こちらは不文律ですね)と似たものです。</p>
<p>標準業務時間の10%とは、だらだらとやっていてはすぐに過ぎ去ってしまう位です。KLab入社間もなかったnakazawa-kにとって、IRCボットは打って付けのネタでした。</p>
<p>閑話休題。目標が決まったのでとにかくシンプルな実装を目指していきました。<br />
IRCのプロトコルにも多少興味はあったのでRFC1459を流し読みし、その上でIRC接続ライブラリとして<a href="http://python-irclib.sourceforge.net/">Python IRC library</a>を利用しました。</p>
<p>※ソースは記事の最後に貼り付けてあります。</p>
<p>使い方</p>
<blockquote><p>
$ ./monty.py server[:port] #channel nickname
</p></blockquote>
<p>いじり方</p>
<blockquote><p>
(nakazawa-k) >>>self.v = range(1,101)<br />
(nakazawa-k) >>>print self.v<br />
(monty) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]
</p></blockquote>
<p>こういう感じで、>>>に続けてPythonの式を書くと評価してくれます。printは結果を取得してIRCへ流してくれます。</p>
<p>「あの実装どうやるんだっけ?誰か教えてー」<br />
という質問にサンプルコードを返信するだけではあまりテンションが上がりませんが、コードの先頭に&#8221;>>>&#8221;を書くだけで実行結果を即確認出来超ハイテンションになれます。これで皆もっとPythonが好きになってくれるはずです!!</p>
<hr />
超簡単な仕組み<br />
SingleServerIRCBotクラスを継承すると、チャンネルでの発言時にon_pubmsg()が呼ばれます。この中に&#8221;>>>&#8221;で始まるものを見つけると、右から左でPythonコードとして実行しています。</p>
<p>これだけでもPythonの実行権限で出来ることは、実質相当色々出来てしまいます。それこそファイルの作成や削除など、結構思いのままなので専用VMを作成して走らせています。</p>
<hr />
応用例<br />
若手ブログ未登場のkさんによる「チャンネル内のユーザからなると(op権限)を奪いまくる」ボットの機能を模倣してみる</p>
<blockquote><p>
>>>self.excp=&#8221;logbot&#8221;<br />
>>>[(nick not in [self.excp,self.nick]) and (c.notice(self.channel, nick + &#8216; is dead&#8217;), c.mode(self.channel, &#8220;-o &#8220;+ nick)) or 1 for nick in [u for u in self.channels.items()[0][1].users()]]
</p></blockquote>
<p>発言が行われたチャンネルで、指定nickと自分以外の全ユーザからop権限を剥奪してくれます。</p>
<p>このように、MontyはPythonの式として書けるものなら、非常に多くの処理を自由に実行することが出来ます。</p>
<hr />
残念なところ<br />
・execでのコード実行は、あまりにもフリーダムすぎる<br />
→PyPyベースのsandbox環境へ持ち込みたい。ただ、既存ライブラリから完全に切り離された環境ではあまり面白いことが出来ないのでほどほどに・・・。<br />
例えば</p>
<blockquote><p>
(nakazawa-k) >>>weather<br />
(monty) =六本木付近の天気予報= 8/19晴れ 8/20曇りのち雨のち晴れのち小雨
</p></blockquote>
<p>こういうことが出来ると段々夢が広がってくるじゃないですか!</p>
<hr />
夢破れて・・・<br />
1) 当初はインタラクティブシェルをそのままIRC上へ持ち込み、謎のIRCペアプログラミング(複数人が1つのインタラクティブシェルを使ってコードを開発するというすさまじい共同作業)などをやりたいなーと夢想しました。まあこれは洒落なので実際にやりたければscreenを使うのが近道でしょう。<br />
2) 当初はpopenして適宜入出力をパイプ取得すれば良いと考えましたが、そうそう素直にstdoutへ各行出力をしてくれるわけではありませんでした。ターミナルの機能を内部でそこそこ使っていたりと結構複雑化するポイントが垣間見えました。それでは手軽に書いてサクッと動かすという主旨に反してしまうので泣く泣く断念。</p>
<hr />
最後に<br />
マルチチャンネル非対応のため、社内の技術雑談チャンネルで放し飼いにしています。</p>
<p>ちょっと残念なコードを食べさせるとinternal exceptionといって実行を放り出してしまうドジッ子(いえ、コードを書いた人のほうがドジッ子なんです)ですがKLab IRCサーバへお立ち寄りの際(!?)は可愛がってあげてください。</p>
<pre class="prettyprint"><code>#! /usr/bin/env python

from ircbot import SingleServerIRCBot
from irclib import nm_to_n, nm_to_h, irc_lower, ip_numstr_to_quad, ip_quad_to_numstr
import random
import re
import os
import sys
import StringIO
import time

class Monty(SingleServerIRCBot):
    def __init__(self, channel, nickname, server, port=6667):
        SingleServerIRCBot.__init__(self, [(server, port)], nickname, nickname)
        self.nick = nickname
        self.channel = channel

    def on_nicknameinuse(self, c, e):
        c.nick(c.get_nickname() + "_")
        self.nick += "_"

    def on_welcome(self, c, e):
        c.join(self.channel)

    def on_pubmsg(self, c, e):
        nick = nm_to_n(e.source())
        matched = re.match(r">>>(.*)", e.arguments()[0])
        if matched != None:
            outputBuffer = StringIO.StringIO()
            sys.stdout = outputBuffer
            exceptionBuf = ''
            try:
                exec matched.group(1)
            except:
                exceptionBuf = sys.exc_info()[0]
                print "internal exception"
            c.notice(self.channel, outputBuffer.getvalue())
            sys.stdout = sys.__stdout__
            print exceptionBuf
        return

def main():
    import sys
    if len(sys.argv) != 4:
        print "Usage: testbot <server[:port]> <channel> <nickname>"
        sys.exit(1)

    s = sys.argv[1].split(":", 1)
    server = s[0]
    if len(s) == 2:
        try:
            port = int(s[1])
        except ValueError:
            print "Error: Erroneous port."
            sys.exit(1)
    else:
        port = 6667
    channel = sys.argv[2]
    nickname = sys.argv[3]

    bot = Monty(channel, nickname, server, port)
    bot.start()

if __name__ == "__main__":
    main()
</code>
</pre>
]]></content:encoded>
			<wfw:commentRss>http://lab.klab.org/young/2010/08/irc%e3%83%9c%e3%83%83%e3%83%88%e3%82%b3%e3%83%b3%e3%83%86%e3%82%b9%e3%83%88%e3%82%a8%e3%83%b3%e3%83%88%e3%83%aa-monty-the-python-bot/feed/</wfw:commentRss>
		</item>
		<item>
		<title>IRCボットコンテストエンリ 最終兵器Haskellによるゲームボット</title>
		<link>http://lab.klab.org/young/2010/08/irc%e3%83%9c%e3%83%83%e3%83%88%e3%82%b3%e3%83%b3%e3%83%86%e3%82%b9%e3%83%88%e3%82%a8%e3%83%b3%e3%83%aa-%e6%9c%80%e7%b5%82%e5%85%b5%e5%99%a8haskell%e3%81%ab%e3%82%88%e3%82%8b%e3%82%b2%e3%83%bc%e3%83%a0/</link>
		<comments>http://lab.klab.org/young/2010/08/irc%e3%83%9c%e3%83%83%e3%83%88%e3%82%b3%e3%83%b3%e3%83%86%e3%82%b9%e3%83%88%e3%82%a8%e3%83%b3%e3%83%aa-%e6%9c%80%e7%b5%82%e5%85%b5%e5%99%a8haskell%e3%81%ab%e3%82%88%e3%82%8b%e3%82%b2%e3%83%bc%e3%83%a0/#comments</comments>
		<pubDate>Tue, 17 Aug 2010 01:56:46 +0000</pubDate>
		<dc:creator>takada-at</dc:creator>
		
		<category><![CDATA[Haskell]]></category>

		<guid isPermaLink="false">https://labmgr.sdc.klab.org/young/?p=1630</guid>
		<description><![CDATA[こんにちは夏です。takada-atです。
夏こそ純粋関数型言語ですね(謎)。
みなさまどうぶつしょうぎというゲームをごぞんじでしょうか？
http://ja.wikipedia.org/wiki/どうぶつしょうぎ
子どもへの将棋普及のために考えられた簡易版の将棋風ゲームです。
以前社内でこのゲームが流行していた時期に、HaskellによるAIを開発しました。
今回はそのAIとIRC上で対戦できるようにしました。

なんとIRC上でどうぶつしょうぎの対戦ができてしまうゲームAIボットです。アスキーアートによるインターフェースがあまりに素朴なので、思わず昔をなつかしんで故郷の両親に電話をかけてしまう、などの効果もあるのではないかと自負しています。
盤面はこういう感じで表示されます。

21:03 (takada-at) !ds-start
21:03 (dshogi) __A__B__C_
21:03 (dshogi) 1-KR-LI-ZO
21:03 (dshogi) 2 * -HY *
21:03 (dshogi) 3 * +HY *
21:03 (dshogi) 4+ZO+LI+KR
21:03 (dshogi) []
21:03 (dshogi) []

!ds-move XXX
という発言で、自分の手を操作します。
たとえば、C4にある駒をC3に動かしてみましょう。

21:04 (takada-at) !ds-move C4C3
21:04 (dshogi) __A__B__C_
21:04 (dshogi) 1-KR-LI-ZO
21:04 (dshogi) 2 * -HY *
21:04 (dshogi) 3 * +HY+KR
21:04 (dshogi) 4+ZO+LI *
21:04 (dshogi) []
21:04 (dshogi) []
21:04 (dshogi) __A__B__C_
21:04 (dshogi) 1 * -LI-ZO
21:04 (dshogi) 2-KR-HY [...]]]></description>
			<content:encoded><![CDATA[<p>こんにちは夏です。takada-atです。<br />
夏こそ純粋関数型言語ですね(謎)。</p>
<p>みなさまどうぶつしょうぎというゲームをごぞんじでしょうか？<br />
<a href="http://ja.wikipedia.org/wiki/どうぶつしょうぎ">http://ja.wikipedia.org/wiki/どうぶつしょうぎ</a></p>
<p>子どもへの将棋普及のために考えられた簡易版の将棋風ゲームです。<br />
以前社内でこのゲームが流行していた時期に、HaskellによるAIを開発しました。<br />
今回はそのAIとIRC上で対戦できるようにしました。<br />
<span id="more-1630"></span></p>
<p>なんとIRC上でどうぶつしょうぎの対戦ができてしまうゲームAIボットです。アスキーアートによるインターフェースがあまりに素朴なので、思わず昔をなつかしんで故郷の両親に電話をかけてしまう、などの効果もあるのではないかと自負しています。<br />
盤面はこういう感じで表示されます。</p>
<pre class="prettyprint">
21:03 (takada-at) !ds-start
21:03 (dshogi) __A__B__C_
21:03 (dshogi) 1-KR-LI-ZO
21:03 (dshogi) 2 * -HY *
21:03 (dshogi) 3 * +HY *
21:03 (dshogi) 4+ZO+LI+KR
21:03 (dshogi) []
21:03 (dshogi) []
</pre>
<p>!ds-move XXX<br />
という発言で、自分の手を操作します。</p>
<p>たとえば、C4にある駒をC3に動かしてみましょう。</p>
<pre class="prettyprint">
21:04 (takada-at) !ds-move C4C3
21:04 (dshogi) __A__B__C_
21:04 (dshogi) 1-KR-LI-ZO
21:04 (dshogi) 2 * -HY *
21:04 (dshogi) 3 * +HY+KR
21:04 (dshogi) 4+ZO+LI *
21:04 (dshogi) []
21:04 (dshogi) []
21:04 (dshogi) __A__B__C_
21:04 (dshogi) 1 * -LI-ZO
21:04 (dshogi) 2-KR-HY *
21:04 (dshogi) 3 * +HY+KR
21:04 (dshogi) 4+ZO+LI *
21:04 (dshogi) []
21:04 (dshogi) []
</pre>
<p>すぐにAIが対抗する手を打ってきます。<br />
すべてのコマンドは、!ds- というプレフィックスではじまります。<br />
!ds-help<br />
と発言すれば、ヘルプを表示します。</p>
<p>実装の詳細には踏み込みませんが、AIは深さ優先探索です。<br />
Haskellをつかったことないと意味不明だと思いますが、盤面の状態、乱数の種などなどをStateモナドで持ち回す感じの実装になってます。</p>
<p>HaskellによるIRCボットの実装については以下を参考にしました。<br />
<a href="http://www.haskell.org/haskellwiki/Roll_your_own_IRC_bot">http://www.haskell.org/haskellwiki/Roll_your_own_IRC_bot</a></p>
<p>あとIRCメッセージのパーサーライブラリを利用しています。<br />
<a href="http://hackage.haskell.org/package/irc">http://hackage.haskell.org/package/irc</a></p>
<h3>■使い方</h3>
<p>dshogi.conf を適当に編集して、サーバーとかチャンネルを指定します。<br />
以下のコマンドで実行します。<br />
念のためWindows環境でビルドしたexeファイルとLinux環境でビルドしたバイナリの両方をつけました。</p>
<p>./DshogiIRC dshogi.conf</p>
<h3>■ビルド方法</h3>
<p>万が一ビルドしてみたい方がいれば以下の方法でお願いします。<br />
ちなみに手元の環境では、ghcのバージョンは6.12.1。ircライブラリだけインストールすればビルドできました。</p>
<pre class="prettyprint">
runhaskell Setup.hs configure
runhaskell Setup.hs build
cp dist/build/DshogiIRC/DshogiIRC .
</pre>
<p>ソースコードは以下にあります。<br />
<a href="http://lab.klab.org/young/wp-content/uploads/data/code/dshogi.tar.gz">http://lab.klab.org/young/wp-content/uploads/data/code/dshogi.tar.gz</a></p>
]]></content:encoded>
			<wfw:commentRss>http://lab.klab.org/young/2010/08/irc%e3%83%9c%e3%83%83%e3%83%88%e3%82%b3%e3%83%b3%e3%83%86%e3%82%b9%e3%83%88%e3%82%a8%e3%83%b3%e3%83%aa-%e6%9c%80%e7%b5%82%e5%85%b5%e5%99%a8haskell%e3%81%ab%e3%82%88%e3%82%8b%e3%82%b2%e3%83%bc%e3%83%a0/feed/</wfw:commentRss>
		</item>
		<item>
		<title>ボットコンテスト twistedを使って常駐型IRC発言プロキシサーバ作ってみました</title>
		<link>http://lab.klab.org/young/2010/08/%e3%83%9c%e3%83%83%e3%83%88%e3%82%b3%e3%83%b3%e3%83%86%e3%82%b9%e3%83%88-twisted%e3%82%92%e4%bd%bf%e3%81%a3%e3%81%a6%e5%b8%b8%e9%a7%90%e5%9e%8birc%e7%99%ba%e8%a8%80%e3%83%97%e3%83%ad%e3%82%ad%e3%82%b7/</link>
		<comments>http://lab.klab.org/young/2010/08/%e3%83%9c%e3%83%83%e3%83%88%e3%82%b3%e3%83%b3%e3%83%86%e3%82%b9%e3%83%88-twisted%e3%82%92%e4%bd%bf%e3%81%a3%e3%81%a6%e5%b8%b8%e9%a7%90%e5%9e%8birc%e7%99%ba%e8%a8%80%e3%83%97%e3%83%ad%e3%82%ad%e3%82%b7/#comments</comments>
		<pubDate>Mon, 16 Aug 2010 23:57:31 +0000</pubDate>
		<dc:creator>sasaki-k</dc:creator>
		
		<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">https://labmgr.sdc.klab.org/young/?p=1627</guid>
		<description><![CDATA[こんにちは!　もう一人の sasaki-k です。
以前からpythonの twisted でなにか作ってみようと思っていたので、技術の無駄遣いネタとして
「常駐型IRCプロキシサーバ」を作ってみました。
バッチプログラムからIRCへ何度も発言させようとすると下のように都度joinとquitしてしまい、
「邪魔だなあ」と言われることが多かったのが背景です。

23:02 *hoge-bot join #channel (~hoge-bot@xxxx.klab.jp)
(発言)
23:02 *hoge-bot quit (Remote host closed the connection)
23:02 *hoge-bot join #channel (~hoge-bot@xxxx.klab.jp)
(次の発言)
23:02 *hoge-bot quit (Remote host closed the connection)

そこで、twistedに付属しているIRCのクライアントを改造して、サーバソケットで
外部プログラムから発言したいメッセージを受け付けられるようにしてみました。ソケットサーバ兼IRCクライアントです。
このpythonスクリプトをバッチの最初で起動しておいて、所定のListenポートに発言したい内容を
Socketクライアントから流しこめば発言してくれる、という流れです。
あるいはdaemontools などでこのスクリプトを常駐させておくのもよさそうです。
使った環境は
Twisted 10.1.0
python 2.5.2
です。


# vim: fileencoding=utf-8

"""
Announce irc bot.
"""

# twisted imports
from twisted.words.protocols import irc
from twisted.internet import reactor, protocol
from twisted.python import log

# system imports
import sys

class IrcBotClient(irc.IRCClient):
    """
  [...]]]></description>
			<content:encoded><![CDATA[<p>こんにちは!　もう一人の sasaki-k です。</p>
<p>以前からpythonの twisted でなにか作ってみようと思っていたので、技術の無駄遣いネタとして<br />
「常駐型IRCプロキシサーバ」を作ってみました。</p>
<p>バッチプログラムからIRCへ何度も発言させようとすると下のように都度joinとquitしてしまい、<br />
「邪魔だなあ」と言われることが多かったのが背景です。</p>
<blockquote><p>
23:02 *hoge-bot join #channel (~hoge-bot@xxxx.klab.jp)<br />
(発言)<br />
23:02 *hoge-bot quit (Remote host closed the connection)<br />
23:02 *hoge-bot join #channel (~hoge-bot@xxxx.klab.jp)<br />
(次の発言)<br />
23:02 *hoge-bot quit (Remote host closed the connection)
</p></blockquote>
<p>そこで、<span id="more-1627"></span>twistedに付属しているIRCのクライアントを改造して、サーバソケットで<br />
外部プログラムから発言したいメッセージを受け付けられるようにしてみました。ソケットサーバ兼IRCクライアントです。</p>
<p>このpythonスクリプトをバッチの最初で起動しておいて、所定のListenポートに発言したい内容を<br />
Socketクライアントから流しこめば発言してくれる、という流れです。<br />
あるいはdaemontools などでこのスクリプトを常駐させておくのもよさそうです。</p>
<p>使った環境は<br />
Twisted 10.1.0<br />
python 2.5.2<br />
です。</p>
<pre class="prettyprint">
<code>
# vim: fileencoding=utf-8

"""
Announce irc bot.
"""

# twisted imports
from twisted.words.protocols import irc
from twisted.internet import reactor, protocol
from twisted.python import log

# system imports
import sys

class IrcBotClient(irc.IRCClient):
    """
    IRCに接続する側のprotocolです。接続するとfactoryのprotocolを差し替えます。
    """
    nickname = "announce-bot"

    def connectionMade(self):
        irc.IRCClient.connectionMade(self)
        self.factory.protocol = self

    def signedOn(self):
        """Called when bot has succesfully signed on to server."""
        self.join(self.factory.channel)

class IrcBotFactory(protocol.ClientFactory):
    """
    IRCに接続する側のprotocolのfactoryです。
    """
    protocol = IrcBotClient

    def __init__(self, channel):
        self.channel = channel

    def clientConnectionLost(self, connector, reason):
        """If we get disconnected, reconnect to server."""
        connector.connect()

    def clientConnectionFailed(self, connector, reason):
        print "connection failed:", reason
        reactor.stop()

class IrcBotCommander(protocol.Protocol):
    """
    メッセージ送信プログラムからの文字列を受け取ります(utf-8固定)
    """
    def dataReceived(self, data):
        channel = "#" + self.factory.botFactory.channel
        data = unicode(data, 'utf-8')
        data = data.rstrip()
        data = data.encode('iso-2022-jp')
        self.factory.botFactory.protocol.msg(channel, data)
        self.transport.loseConnection()

class IrcBotCommanderFactory(protocol.ServerFactory):
    """
    メッセージ送信プログラムのfactoryです。IrcBotへのポインタを持ちます
    """
    protocol = IrcBotCommander

    def __init__(self, botFactory):
        self.botFactory = botFactory

# main
if __name__ == '__main__':
    # initialize logging
    log.startLogging(sys.stdout)

    ircBotFactory = IrcBotFactory(sys.argv[1])
    ircBotCommanderFactory = IrcBotCommanderFactory(ircBotFactory)

    # xxx.xxx.xxxはircサーバ
    reactor.connectTCP("xxx.xxx.xxx", 6667, ircBotFactory)
    reactor.listenTCP(12345, ircBotCommanderFactory)

    # run bot
    reactor.run()

</code>
</pre>
<p>おかげで絶え間なく(?) 発言できるようになりましたー</p>
<blockquote><p>
23:02 *announce-bot join #channel (~hoge-bot@xxxx.klab.jp)<br />
23:02 (announce-bot) ああああ<br />
23:03 (announce-bot) <-------------- テストテスト --------------><br />
23:10 (announce-bot) <-------------- これからよろしくね -------------->
</p></blockquote>
<p>今後複数チャンネル対応とか、他の人の発言に対応した動きをするとか、できたらいいなと思ってます。</p>
]]></content:encoded>
			<wfw:commentRss>http://lab.klab.org/young/2010/08/%e3%83%9c%e3%83%83%e3%83%88%e3%82%b3%e3%83%b3%e3%83%86%e3%82%b9%e3%83%88-twisted%e3%82%92%e4%bd%bf%e3%81%a3%e3%81%a6%e5%b8%b8%e9%a7%90%e5%9e%8birc%e7%99%ba%e8%a8%80%e3%83%97%e3%83%ad%e3%82%ad%e3%82%b7/feed/</wfw:commentRss>
		</item>
		<item>
		<title>iPhoneをアナログゲームコントローラにしてみる(3)</title>
		<link>http://lab.klab.org/young/2010/08/iphone%e3%82%92%e3%82%a2%e3%83%8a%e3%83%ad%e3%82%b0%e3%82%b2%e3%83%bc%e3%83%a0%e3%82%b3%e3%83%b3%e3%83%88%e3%83%ad%e3%83%bc%e3%83%a9%e3%81%ab%e3%81%97%e3%81%a6%e3%81%bf%e3%82%8b3/</link>
		<comments>http://lab.klab.org/young/2010/08/iphone%e3%82%92%e3%82%a2%e3%83%8a%e3%83%ad%e3%82%b0%e3%82%b2%e3%83%bc%e3%83%a0%e3%82%b3%e3%83%b3%e3%83%88%e3%83%ad%e3%83%bc%e3%83%a9%e3%81%ab%e3%81%97%e3%81%a6%e3%81%bf%e3%82%8b3/#comments</comments>
		<pubDate>Tue, 10 Aug 2010 04:46:55 +0000</pubDate>
		<dc:creator>ochi-s</dc:creator>
		
		<category><![CDATA[iPad]]></category>

		<category><![CDATA[iPhone]]></category>

		<category><![CDATA[objective-c]]></category>

		<guid isPermaLink="false">https://labmgr.sdc.klab.org/young/?p=1608</guid>
		<description><![CDATA[暑い日が続いていますが、いかがお過ごしですか。最近複数の人から、「老けた」と言われて若干凹んだponpoko1968です。
「iPhoneをアナログゲームコントローラにしてみる」の最終回、サーバの実装です。
まずはデモ動画からどうぞ。


今回は、前回作成したリモートコントローラの状態を受信して、リアルタイムで表示するサンプルアプリを作成します。
アプリケーションの作成
先ずはプロジェクトの作成です。サーバについても、簡素な画面構成なので、「View-Based Application」を選択します。
主に手を加えるのは、メインの画面を制御するViewControllerクラスです。
Game Kitサーバのセットアップ
ViewControllerがGame Kitからの通知を受信できるよう、GKSessionDelegateプロトコルをサポートさせます。

@interface AnalogRemoteServerViewController : UIViewController {
  // Game Kit
  GKSession* gkSession;
  NSMutableDictionary* peers;
(中略)

「UIViewController」のように継承元のクラスの指定の後ろに、&#8221;&#8220;で括ってサポートするプロトコルを指定します。複数指定する場合はカンマで区切って書きます。
ViewControllerクラスに、接続しているコントローラの状態を保持するメンバ変数を追加します。
リモートコントローラ同様、サーバもGKSessionインスタンスへのポインタを保持します。
さらに、サーバは複数のリモートコントローラからの接続を考慮し、各リモートコントローラの状態を保持する辞書を追加します。
サーバはリモートコントローラからの接続時にPeerIDと呼ばれる文字列を渡されるので、この文字列をキー値として、後述するControllerStateクラスのインスタンスを格納することにします。
クライアントからの接続を受け付ける
リモートコントローラがサーバに接続しに来ると、didReceiveConnectionRequestFromPeerが呼ばれます。

- (void)session:(GKSession *)session didReceiveConnectionRequestFromPeer:(NSString *)peerID {
  NSError* error;
  NSLog(@"session:%p didReceiveConnectionRequestFromPeer: from=\"%@\"", session, peerID );
  if(  [peers count] < 1 ){
    [session acceptConnectionFromPeer:Peerid error:&#038;error];
    [session setDataReceiveHandler:self  withContext:peerID];
  [...]]]></description>
			<content:encoded><![CDATA[<p>暑い日が続いていますが、いかがお過ごしですか。最近複数の人から、「老けた」と言われて若干凹んだponpoko1968です。</p>
<p>「iPhoneをアナログゲームコントローラにしてみる」の最終回、サーバの実装です。<br />
まずはデモ動画からどうぞ。<br />
<object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/sOk_oQlvoTg&#038;hl=ja&#038;fs=1"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/sOk_oQlvoTg&#038;hl=ja&#038;fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"></embed></object><br />
<span id="more-1608"></span><br />
今回は、前回作成したリモートコントローラの状態を受信して、リアルタイムで表示するサンプルアプリを作成します。</p>
<p><H2>アプリケーションの作成</H2></p>
<p>先ずはプロジェクトの作成です。サーバについても、簡素な画面構成なので、「View-Based Application」を選択します。</p>
<p>主に手を加えるのは、メインの画面を制御するViewControllerクラスです。</p>
<p><H2>Game Kitサーバのセットアップ</H2><br />
ViewControllerがGame Kitからの通知を受信できるよう、GKSessionDelegateプロトコルをサポートさせます。</p>
<pre class="prettyprint"><code>
@interface AnalogRemoteServerViewController : UIViewController<GKSessionDelegate> {
  // Game Kit
  GKSession* gkSession;
  NSMutableDictionary* peers;
(中略)
</code></pre>
<p>「UIViewController<GKSessionDelegate>」のように継承元のクラスの指定の後ろに、&#8221;<",">&#8220;で括ってサポートするプロトコルを指定します。複数指定する場合はカンマで区切って書きます。</p>
<p>ViewControllerクラスに、接続しているコントローラの状態を保持するメンバ変数を追加します。<br />
リモートコントローラ同様、サーバもGKSessionインスタンスへのポインタを保持します。<br />
さらに、サーバは複数のリモートコントローラからの接続を考慮し、各リモートコントローラの状態を保持する辞書を追加します。<br />
サーバはリモートコントローラからの接続時にPeerIDと呼ばれる文字列を渡されるので、この文字列をキー値として、後述するControllerStateクラスのインスタンスを格納することにします。</p>
<p><H2>クライアントからの接続を受け付ける</H2><br />
リモートコントローラがサーバに接続しに来ると、didReceiveConnectionRequestFromPeerが呼ばれます。</p>
<pre class="prettyprint"><code>
- (void)session:(GKSession *)session didReceiveConnectionRequestFromPeer:(NSString *)peerID {
  NSError* error;
  NSLog(@"session:%p didReceiveConnectionRequestFromPeer: from=\"%@\"", session, peerID );
  if(  [peers count] < 1 ){
    [session acceptConnectionFromPeer:Peerid error:&#038;error];
    [session setDataReceiveHandler:self  withContext:peerID];
  }else {
    [session denyConnectionFromPeer:peerID ];
  }
}
</code></pre>
<p>今回は、サンプルプログラムの簡単のため、リモートコントローラからの接続を1台のみに制限しています。<br />
具体的にはpeersメンバ変数の要素数が1未満の場合には、セッションにacceptConnectionFromPeerを呼んで接続を許可します。<br />
また、1以上の場合、すなわちリモートコントローラがすでに接続している場合はdenyConnectionFromPeerを呼んでそれ以上の接続を拒否します。<br />
今回のコードも設計上は複数のリモートコントローラからの接続をサポートしているので、ゲームのデザインによって同時に接続できるリモートコントローラのの最大数を変更すると良いでしょう。</p>
<p>このとき、sessionに対し、</p>
<pre class="prettyprint"><code>
    [session setDataReceiveHandler:self  withContext:peerID];
</code></pre>
<p>を呼び出すことで、データが到着したときのハンドラ（コールバック）関数をViewControllerに設定します。ハンドラ関数は名前と引数の型が決められており、セッションからデータを受け取るクラスは</p>
<pre class="prettyprint"><code>
- (void) receiveData:(NSData *)data fromPeer:(NSString *)peer inSession: (GKSession *)session context:(void *)context
</code></pre>
<p>の形式の関数を定義しなければいけません。</p>
<p>このあと、通常であれば、session:peer:didChangeStateが呼ばれます。</p>
<p>アプリケーションがリモートコントローラからの接続が確立したと認識させるのは、このsession:peer:didChangeState:が呼ばれたタイミングで、peerすなわちリモートコントローラの状態を見て行いましょう。以下のコードを参照して下さい。</p>
<pre class="prettyprint"><code>
- (void)session:(GKSession *)session peer:(NSString *)peerID didChangeState:(GKPeerConnectionState)state {
  NSLog(@"session:%p peer:%p\"%@\" didChangeState:%d",session,peerID,peerID,state );
  if( state == GKPeerStateConnected ){
    ControllerState* controller = [[ControllerState alloc] init];
    controller.delegate = self;
    [peers setObject:controller forKey:peerID];
    [controller release];

    NSLog(@"state = GKPeerStateConnected");

  }else if ( state == GKPeerStateDisconnected ){
    NSLog(@"state = GKPeerStateDisconnected");
    [peers removeObjectForKey:peerID];
  }

}
</code></pre>
<p>上記メソッドが呼ばれる際、GameKit側からGKPeerConnectionState定数が渡されます。stateの値がGKPeerStateConnectedであった場合には、peerIDをキーに、後述するControllerStateインスタンスを生成してpeersメンバ変数に登録しておきます。<br />
その後、</p>
<pre class="prettyprint"><code>
    controller.delegate = self;
</code></pre>
<p>として、</p>
<p>ControllerStateクラスがコントローラの状態変化を検知した場合に、ViewControllerクラスが通知を受け取るようにします。</p>
<h2>データの受信</H2></p>
<p>前節で説明したように、Game Kitが受信したデータをビューコントローラクラスに定義したハンドラ関数で処理します。今回はControllerStateという独自のクラスを定義し、受け取ったデータの解釈は、そのクラスにまかせることにします。</p>
<pre class="prettyprint"><code>
- (void) receiveData:(NSData *)data fromPeer:(NSString *)peer inSession: (GKSession *)session context:(void *)context {
  ControllerState* controller = [peers objectForKey:peer];
  if( controller ) {
    [controller updateWithDecodingRawData:data];
  }else {
    NSLog(@"data from unknown peer %@",peer);
  }
}
</code></pre>
<p>Game Kitから渡されたpeer文字列をキーに ControllerStateインスタンスの参照を取り出し、引数で受けとったNSDataのインスタンスを渡します。</p>
<p><H2>データのデコード</H2><br />
今回のサンプルでは、ちょっと大げさですが、リモートコントローラから送信されたコントローラの状態を解釈するクラスControllerStateと、その状態を他のクラスに通知するためのデリゲート通知プロトコルControllerStateDelegateを定義しました。</p>
<p>こういったクラスとプロトコルを定義した意図として、通信セッションの接続や切断などに関するあれこれはシステム内で単一であることが保証されているクラス（＝シングルトン）に担当させるのが適切ですが、ゲーム内の複数のキャラクタを各リモートコントローラを使って動かしたい場合などには、設計上各リモートコントローラからの情報を各キャラクタを表現するクラスが直接受け取ることで、プログラムの見通しが良くなると考えたからです。</p>
<p>また、デリゲートプロトコルを介することで、たとえば、ユーザからの入力をリモートコントローラから外付けキーボードなど別の周辺機器に切り替えたい場合にゲーム側のロジックに関するコードを変更する必要がなくなります。</p>
<blockquote><p>
今回の場合GKSessionのデリゲートを受け取るのはViewControllerなので、CoCoaの設計上は厳密に言うとシングルトンではないのですが、アプリケーション構成上Viewが1つしかないためViewControllerに色々やらせてます^^;
</p></blockquote>
<p>一連の処理をまとめると、下記の図のようになります。<br />
<a href="https://files.me.com/ponpoko1968/qjvllq"><br />
<img src="https://files.me.com/ponpoko1968/qjvllq" alt="データの流れ" width="75%" height="75%" /><br />
</a></p>
<h2>画面表示</H2><br />
最後に、コントローラの状態表示を行います。今回の場合はControllerStateクラスが検知したリモートコントローラの状態変化もViewControllerが受け取ります。</p>
<p>ControllerStateクラスは下記の3つの通知を送ります。</p>
<ul>
<li>buttonPressed - ボタンが押された</li>
<li>buttonReleased - ボタンが離された</li>
<li>controllerTilted: - コントローラが傾いた</li>
</ul>
<p>buttonPressed、 buttonReleasedについては比較的自明なのでサンプルコードを見て頂くとして、 controllerTiltedについては、</p>
<pre class="prettyprint"><code>
- (void) controllerTilted:(float)tilt {
  //NSLog(@"controllerTilted val = %f",tilt);
  [arrowView setTransform:CGAffineTransformMakeRotation(tilt)];
}
</code></pre>
<p>としています。tiltという値はコントローラの傾き角度がラジアンで渡ってきているので、矢印の画像を持つサブビューをInterfaceBuilderで作り、そのビューに対してCGAffineTransformMakeRotation()で作成した回転行列を作用させることで、矢印の画像を回転させています。</p>
<h2>最後に</H2><br />
最終回となりましたこの連載ですが、こうして見直すといくつか課題が残っています。</p>
<ul>
<li>UDPなので、パケットの順番が逆転する可能性があるが対応されていない</li>
<li>→UDPベースの既存のプロトコルを参考にしてみる</li>
<li>ControllerStateを介することでパフォーマンスに影響はないか？あるとしたらどのように改良すべきか
<li>→Objective-Cのメソッド呼び出しを高速化する方法を調べてみる</li>
<li>ボタンを押したときの衝撃でiPhoneが揺れてしまう</li>
<li>→ｎ時点前からのiPhoneの傾きの移動平均値を計算して傾きを緩やかにする（ゲームによるけど）</li>
</ul>
<p>といったところが挙げられます。筆者なりにそれぞれの対応案もあるのですが、もし本稿を読まれて自分のアプリに取り入れてやろうという方がいらっしゃったら、是非このような課題にもチャレンジしていただいて、さらにそのような改良をブログ等で発表して頂ければうれしいです。<br />
サンプルコードは<a href="https://files.me.com/ponpoko1968/bpgv8d">こちら</a>からどうぞ。</p>
]]></content:encoded>
			<wfw:commentRss>http://lab.klab.org/young/2010/08/iphone%e3%82%92%e3%82%a2%e3%83%8a%e3%83%ad%e3%82%b0%e3%82%b2%e3%83%bc%e3%83%a0%e3%82%b3%e3%83%b3%e3%83%88%e3%83%ad%e3%83%bc%e3%83%a9%e3%81%ab%e3%81%97%e3%81%a6%e3%81%bf%e3%82%8b3/feed/</wfw:commentRss>
		</item>
	</channel>
</rss>
