さて、bogofilter 使用環境での spam 登録/ham 登録/判定のためのスクリプトの改訂版です。 このスクリプトは元々 Mew のマーク付けに呼び出して使うために書いています。 何故このスクリプトが必要かというと、nkf では本文が base64 等でエンコードされたメールをきちんとデコードできないからです。 Ruby で書かれた前処理のフィルタ (その1、その2) は見つかったのですが、perl のものが見当たらないので自分で書いてみたのです。外部コマンド実行のオーバーヘッド削減を目的として Text::Kakasi や NKF モジュールを用いています。
前回から何が変わったかというと、MIME の階層をきちんと辿るようにしました。前回のものはこれが不十分でした。 また、コンテンツタイプがテキスト以外のものは Content-Type フィールドを表示して、学習に生かせるようにしました。実行時の各種オプションも新設しました。
#!/usr/bin/perl use Email::MIME; use NKF; use Text::Kakasi; use Getopt::Std; my $nkfopt = "-e"; my $bogoopt = ""; my $printmode = 0; sub mime_body { my $o = shift; my @p = $o->subparts; my $m = ""; if (@p > 0) { foreach my $so (@p) { $m = $m . mime_body($so); } } elsif (!defined($o->content_type) || $o->content_type =~ /text/i) { $m = $m . nkf($nkfopt, $o->body); } else { $m = $m . $o->content_type . "\n"; } return $m; } local $/; my $k = Text::Kakasi->new; $k->set("-w"); getopt('snp'); if (defined($opt_s)) { $bogoopt = "-s"; } if (defined($opt_n)) { $bogoopt = "-n"; } if (defined($opt_p)) { $printmode = 1; } LOOP: foreach $filename (@ARGV) { open(I, $filename) || next LOOP; my $message = <I>; my $parsed = Email::MIME->new($message); $message = nkf($nkfopt, $parsed->header_obj->as_string . "\n") . mime_body($parsed); my $tokenized = $k->get($message); if ($printmode) { print $tokenized; } else { open(O, "| bogofilter $bogoopt") || die("can't execute bogofilter"); print O $tokenized; close(O) && print "$filename\n"; } close(I); }
このスクリプトを bogo として、使い方を簡単に説明します。 そのフォルダのメールをスパムとしてデータベースに登録するには次のように呼び出します。
bogo -s -- *
そのフォルダのメールをハムとしてデータベースに登録するには次のように呼び出します。
bogo -n -- *
"-p" オプションは bogofilter に喰わせる前のメールデータを標準出力に出力します。 kakai による分かち書きの結果も確認できます。
bogo -p -- *
引数でオプションをつけずファイル名のみ渡すと、指定したファイルのうち spam と判定されたメールのファイル名のみ標準出力に出力します。 これを Mew のマークに使います。
bogo *
利用している perl のメール関連パッケージは以下の通りです。この順で "perl Makefile.PL; make; make install" を実行しインストールしました。
- MIME-Types-1.20
- MIME-Base64-3.07
- Email-MIME-ContentType-1.014
- Email-MIME-Encoding-1.311
- Email-Simple-1.999
- Email-MIME-1.859
この他に Text::Kakasi と NKF を用いています。
メール関連のこの辺りのモジュールは使いやすいと思います。 日本語のサイトで取り上げている例は少ないようですが、このコードが処理の参考になればと思います。