はじめてのircbot

まったりとしたチャット

ircdを動かして日々の何気ないチャットをやっていたある日,

url送ったらそのページのタイトルを返してくれるボットが欲しいなぁ

という話がでてきました.これはPOEを使ってみるチャンス,ってことでircbot作りに挑戦することにしました.

サンプル

POE::Compoennt::IRCにはいくつかサンプルがついていたり,podにもサンプルがついているのですが,一番簡単なのがPOE Cookbookだったので,これをベースにしてみました.やることは簡単で,元のスクリプトの

    if ( my ($rot13) = $msg =~ /^rot13 (.+)/ ) {
        $rot13 =~ tr[a-zA-Z][n-za-mN-ZA-M];

となっている部分を,urlを受け取ったらそのタイトルを返すように書き直せばいいわけです.これを実現するために見つけたのがHTML::TagParserモジュールでした.これを使うと,

    my $html = HTML::TagParser->new( "index-j.html" );
    my $elem = $html->getElementsByTagName( "title" );
    print "<title>", $elem->innerText(), "</title>\n" if ref $elem;

とするだけで,指定したhttp urlからタイトルを抜き出すことができます.さらにうれしいことには,

This module natively understands the character encoding used in document by parsing its meta element.The parsed document's encoding is converted as this class's fixed internal encoding "UTF-8".

ということなので文字コード変換までおまかせでやってくれています.後はサーバへの書き込み時点で,サーバ指定のiso-2022-jpに変換してやればOK.そんなこんなでスクリプトはこんな感じになりました.

title.pl

#!/usr/local/bin/perl

use POE;
use POE::Component::IRC;
use HTML::TagParser;
use Encode qw(from_to);
use strict;

my ( $url, $channel, $irc );

$url     = q{s?https?://[-_.!~*'()a-zA-Z0-9;/?:@&=+$,%#]+};
$channel = '#test';

$irc = POE::Component::IRC->spawn();

POE::Session->create(
    inline_states => {
        _start     => \&bot_start,
        irc_001    => \&on_connect,
        irc_public => \&on_public,
    },
);

sub bot_start {
    my $kernel  = $_[KERNEL];
    my $heap    = $_[HEAP];
    my $session = $_[SESSION];

    $irc->yield( register => 'all' );

    my $nick = 'poe0';
    $irc->yield(
        connect => {
            Nick     => $nick,
            Username => 'cookbot',
            Ircname  => 'POE::Component::IRC cookbook bot',
            Server   => 'irc.hoge.co.jp',
            Port     => '6667',
        }
    );
}

sub on_connect {
    $irc->yield( join => $channel );
}

sub on_public {
    my ($msg) = @_[ARG2];

    if ( my ($http) = $msg =~ m/^title ($url)/ ) {
        my $title;
        eval {
            my $html = HTML::TagParser->new($http);
            my $elem = $html->getElementsByTagName('title');
            $title = $elem->innerText();

            from_to( $title, 'utf-8', 'iso-2022-jp' );
        };
        $title = 'Error to get Title.' if ($@);

        $irc->yield( privmsg => $channel, $title );
    }
}

$poe_kernel->run();
exit 0;

ちょっとした解説

http URLを正規表現で書いてみる方法はPerlメモが詳しいです.ここでは,単純な場合を使っています.

$url     = q{s?https?://[-_.!~*'()a-zA-Z0-9;/?:@&=+$,%#]+};

っていうか,複雑なケースを使う気力はあんまりないですね(笑

それから,タイトルを抜き出す部分のスクリプトが以下のようになっています.文字コードはHTML::TagParser中でutf8になっているので,それをfrom_toを使ってjisコードに変換しています.

eval {
        my $html = HTML::TagParser->new($http);
        my $elem = $html->getElementsByTagName('title');
        $title = $elem->innerText();

        from_to( $title, 'utf-8', 'iso-2022-jp' );
};
$title = 'Error to get Title.' if ($@);

おわりに

PlaggerからIRCにしたりしていたら,ircbotがどんどん増えてしまいました.少しまとめる方法を考えないといけなさそうです.