Twitter で following が増えてくるにつれて、タイムラインに目を通すのが大変になってきた(という程きちんと見ている訳ではないが)。 さっとタイムラインをなめて面白そうな情報をピックアップしたい時は、「おはよう」とか「風呂入った」とか「トイレ」とかは除外して読みたい(そういう書き込み自体は嫌いじゃないのだが、人生はあまりにも短い)。
Twit や P3:PeraPeraPrv では NG ワード指定ができて、それらを含むステータスは表示しないようにできるのだが、Twitter の書き込みは揺らぎが激しすぎて指定しきれないという弱点がる。
ということでベイジアンフィルタでフィルタリングしてみることにした。
自前で Twitter クライアントを作る気はないので、proxy の形でさっと実装してみた。
#!/usr/bin/perl use strict; use warnings; use HTTP::Proxy; use HTTP::Proxy::BodyFilter::complete; my $proxy = HTTP::Proxy->new(port => 8088); $proxy->push_filter(response => HTTP::Proxy::BodyFilter::complete->new, mime => 'application/xml'); $proxy->push_filter(response => Bsfilter->new, mime => 'application/xml'); $proxy->start; { package Bsfilter; use File::Temp qw/tempfile/; use XML::XPath; use base qw(HTTP::Proxy::BodyFilter); sub filter { my ($self, $dataref, $message, $protocol, $buffer) = @_; return unless defined($$dataref) && $$dataref ne ''; eval { my $xml = XML::XPath->new(xml => $$dataref); my @nodes = $xml->findnodes('/statuses/status/text/text()'); return unless @nodes; for my $node (@nodes) { my $text = $node->getNodeValue; if (is_NG($text)) { $node->setNodeValue("[NG] $text"); } } $$dataref = qq(<?xml version="1.0" encoding="UTF-8"?>\n); $$dataref .= $xml->get_context->toString; utf8::encode($$dataref); }; if ($@) { warn $@; } } sub will_modify { 1 } sub is_NG { my ($text) = @_; my ($fh, $filename) = tempfile(); utf8::encode($text); print $fh $text; close($fh); my $result = system( "bsfilter --homedir ~/.twitter-bsfilter --ignore-header --auto-update $filename" ); unlink($filename); return !$result; } }
Perl で HTTP proxy を作ろうとして真っ先に思い浮かんだのは POE だけれど、ちょっとヘビーなので今回は HTTP::Proxy をチョイス。 もともとフィルタリング HTTP proxy を作ることを念頭に置いた Perl モジュールなので今回の目的にぴったり。
1つはまった点といえば、filter の呼び出しがレスポンス全てを取得してからではなく一部分ずつの呼び出しになるところ。その仕様に気がつくのにちょっと時間がかかってしまった。 例えば XML 形式のレスポンスをフィルタしようとしても、普通に HTTP::Proxy を使うと XML の一部ずつがフィルタに渡されるため、XML のパースがうまくいかない。
これについては HTTP::Proxy::BodyFilter::complete を使うことで、まとめてフィルタに渡せるようになった。
Twitter のタイムライン取得については P3:PeraPeraPrv が XML 形式で取得しているので、そのタイプのレスポンスをフィルタするようにした。
XML::XPath でステータス部分を抜き出して NG 判定し、NG であれば先頭に [NG] を追加する。 これで Twitter クライアント側で [NG] を NG ワード指定すれば、表示されないようにすることができる。
NG 判定は普段メールの spam フィルタとして使っている bsfilter を使った。 単純に system 関数で呼び出して結果を取得するだけ。
今回は対象がメールではないので --ignore-header を指定。また自動的に学習するように --auto-update を指定。 それと普段メールのフィルタリングに使っているのとは bsfilter のデータベースを別にしたいので、--homedir も指定しておく。
NG ワードを twitter-NG.txt に、非 NG ワードを twitter-clean.txt に書いて以下のコマンドを実行。
bsfilter --add-clean --ignore-header --homedir ~/.twitter-bsfilter twitter-clean.txt bsfilter --add-spam --ignore-header --homedir ~/.twitter-bsfilter twitter-NG.txt bsfilter --update --homedir ~/.twitter-bsfilter
自分の環境 (Debian GNU/Linux sid)では、UTF-8 で書いておいて問題なかった。
あとは先の proxy を起動し、P3:PeraPeraPrv でプロキシとして localhost:8088 を指定すれば OK。
タイムラインを取得するたびに bsfilter が動いて NG なステータスには [NG] が挿入される。
これについては、まだまだチューンの必要ありかな。
まだ使える精度まで上がってないけれど、教師データを増やせばそれなりにいけるかもしれない。
proxy の枠組ができたので、(@〜は抜いてから bsfilter に渡すとか、前後の文脈も含めるとか)いろいろ試して遊べそうではある。 別に bsfilter にこだわらず、正規表現による判定などをしてもよいし。
パケ・ホーダイを契約してから、MovaTwitter・RTM・モバイル Gmail などで携帯電話を活用するようになった。そんななか、決定打がないのが、ノートアプリケーション。電車の中などの隙間時間に、この nDiki の 下書きなどはケータイでできるようにしたい。
Google ドキュメントが使えればいいが、前年ながらまだiモードでは使えない。 メールベースでやる手もあるが、メモには良いものの再編集を繰り返したいようなものに難がある。
ということで自前でプライベート Wiki を立てそこに書き込んでみることにした。
使う WikiEngine はいつも通り自作の WiKicker。
書き込んだテキスト内のキーワードを nDiki へ自動リンクさせることができるので、パーソナルナレッジベースとして自分にとっては一番便利。書式も同じなので、Wiki に書いた下書きを、そのまま nDiki で使える。
肝心のケータイからの書き込みだが Ajax 等凝った技術を使っていないおかげで、問題なく FOMA 端末(D703i)からiモードで読み書きできた。WiKicker は UTF-8 でページを出力しているが、網側か端末側の処理かは知らないが今のところ問題なし。
なお認証は簡単に Basic 認証で済ますことにした。 安全とは言えないがそれほど重要なデータを置くわけではないしいいかな。 cookie は必要ないし WikiEngine に手を入れなくてもよいので、すぐできるのはコレ。
ユーザ名とパスワード付きのトップページ URL を端末でブックマークしておけば1発でアクセスできる。
これでケータイ(と PC)から使えるプライベート Wiki を設置できたわけだが、なにぶんもともとケータイをサポートしている WikiEngine ではないため、長いページの分割機能などはないのがちょっと不安。PageName で生成される URL が長くなった時の振る舞いもちょっと不安。
そこで Google Mobile Proxy (http://www.google.co.jp/gwt/n) 経由で Wiki を使うことにした。 ページを携帯端末向けに変換してくれる proxy で、Basic 認証もできるしフォーム の POST もできる。
Google Mobile Proxy 経由で見たページ内のリンク先も全て自動的に proxy 経由になるので、 PC 向け Web ページの URL を書いておけばそのまま携帯電話で見ることができる。
安全のためか、比較的短い一定時間立つと認証の再確認画面が表示されてしまうが、ユーザ名とパスワードを入力すれば、セッションは継続される。 テキスト編集に時間がかかってしまうと POST する時にひっかかってしまい認証の再入力がちょっと面倒だが、再認証が通れば POST リクエスト自体は有効で書き込みがロストすることはないようだ。
しばらくはこれで読み書きしてみよう。
昨日、Notify My Android (NMA) をプレミアムアカウントにしたので、さっそく IRC の通知設定をしてみた。
IRC proxy の Tiarra を使っているのでチェックしたところ、Auto::Notify というそれっぽいモジュールがあり Prowl に流すことができる模様。これを使うことにした。
module/Auto/Notify.pm の config_prowl() と send_prowl() をそれぞれ同じファイル内で config_nma() と send_nma() としてコピー。 config_nma() はとりあえずあればいいので空のサブルーチンにしておく。send_nma() の方は、API の URL のところを変更するのと、$url->query_form(@data); を削除して、かわりに GET($url->as_string()) を POST($url->as_string(), \@data) にすれば OK。Prowl も NMA も通知 API のパラメータは一緒。
conf の方も Auto::Notify とほぼ同じで OK。
+ Auto::Notify { mask: * *!*@* regex-keyword: (Naney|反応したいキーワード1|反応したいキーワード2) blocks: nma nma { type: format: #(date:%H:%M:%S) <#(raw_channel)> (#(nick.now)) #(text) apikey: NMA で発行したキー priority: 0 application: tiarra event: IRC }
とかこんな感じ。これで IRC でニックネーム呼ばれたりすると Xperia GX がブルブルいってくれる。
メッセージ本文を外に流したくない時には format を適宜調整。
IRC でニックネームで呼び出されたり緊急系のアナウンスがあった時に気がつけるようにするために組んでいる構成について紹介。
「集中」と「注意を向ける(記事)」のバランスを考えて以下でレベル分けしてる。
構成は以下のような感じ。
iPhone ユーザーなら NMA ではなくて Boxcar / Prowl / Pushover などが使えるのかな。
あとは ikachan を立ち上げてあって、チームのチャネルではメンバが ITS のチケットの情報を流してくれている。他チャネルでは Jenkins 他からの各種情報も流れているので必要に応じて、通知キーワードを設定してく感じ。
Naney (なにい) です。株式会社MIXIで SNS 事業の部長をしています。
※本サイトの内容は個人的見解であり所属組織とは関係ありません。