tutorial

### チュートリアル

言葉で説明するより、実際に簡単なモジュールを使ってみるのが わかりやすいと思います。 plumのモジュールを作る手順はだいたい以下のようになります。

1. どんなことをするモジュールかを決める。
2. モジュール名とそれをおくディレクトリを決める。
3. モジュールで指定するプロパティを決める。
4. デフォルトの動作などを考えて初期化部分を作る。
5. 実際に処理を行うサブルーチンを作る。

それでは実際に簡単なモジュールを作ってみます。

### モジュールの動作

まずどんな動作を行うかを考えます。 今回は簡単な例として、ある特定の言葉が発言されたときに、 それに対応する発言を返すというモジュールにします。 また、処理を簡単にするために、発言は完全に一致した場合にのみ 反応するようにします。

### モジュール名

次に、モジュールの名前を考えます。 自動的に返事を返すという意味で、「answer.plm」という名前にします。 これをどこのディレクトリにおくかですが、 自動的に処理を行うという意味でautoディレクトリにおくことにします。 これでモジュール名は「auto/answer.plm」ということになります

### プロパティ

次はモジュールにどんなプロパティが指定できるかを決めます。 ある発言に対して、対応する発言を返すわけなので、 それぞれの発言をプロパティで設定できるようにします。 実際にどのような処理を行うかによってプロパティの数も内容も変わってきます。 例えば、反応する発言を「auto.answer.message」で指定し それに対する発言を「auto.answer.reply」で指定するという方法があります。 しかしこれでは反応する発言が1種類しか指定できません。 そこで、プロパティは「auto.answer.reply」だけにし、 反応する発言と、それに対する返事を空白で区切って指定できるようにします。 この場合、空白を含む発言に反応することができませんが、 そのようなことはそれほどないと考えて無視します。

### 初期化部分

次にモジュールの初期化部分を作ります。 まず、モジュールのパッケージ名は「auto_answer」になります。 次にプロパティを指定しなかったときのデフォルトの動作を考えて、 グローバル変数を設定しますが、 このモジュールではプロパティを設定しなかったときは何もしないことにします。 これを実際に作ってみると以下のようになります。

        package auto_answer;
        $_ = 'auto_answer';

これで初期化部分は完成です。これだけでは何もしませんが、 plumにモジュールとして組み込むことも可能です。

### サブルーチン

それでは実際の処理を行うサブルーチンを作ります。 まず、どのイベント対する処理を行えばいいかを考えます。 このモジュールが実行されるのは、ある特定の言葉が発言されたときです。 とりあえず、plumが実行するサブルーチンの中から適当なものを選びます。 発言というのは実際にはPRIVMSGというコマンドがサーバから 送られてくることを言います。 そこで、「ss_privmsg」というサブルーチンを作ればいいことがわかります。 とりあえずサブルーチンだけを作ります。

        package auto_answer;
        $_ = 'auto_answer';

        sub ss_privmsg {
          local($serverno, $prefix, $cmd, $chan, $msg) = @_;
          # まだ作ってない部分
          return ($prefix, $cmd, $chan, $msg);
        }

サブルーチンの引数として渡されるものは、サーバのファイルNO、 発言者のプレフィクス、コマンド、発言したチャンネル、メッセージの5つです。 戻り値として返すものは後ろの4つです。
次に、発言がプロパティで指定されたものかどうかを調べる部分を作ります。 プロパティ名は「auto.answer.reply」にしたので、 その値と発言が等しいかどうかを調べます。 また、プロパティは複数設定できるようにしたので、 1つの発言についてすべてのプロパティとの比較が必要になります。

        package auto_answer;
        $_ = 'auto_answer';

        sub ss_privmsg {
          local($serverno, $prefix, $cmd, $chan, $msg) = @_;
          local($userno);
          $userno = $'userno[$serverno];
          foreach $reply (&'property($userno, 'reply')) {
            # まだ作ってない部分
          }
          return ($prefix, $cmd, $chan, $msg);
        }

&'propertyは引数としてユーザNOが必要なので、$'userno[]という配列変数で この発言がどのユーザに属するものかを調べています。 また、サブルーチン内で使用する変数は必ずlocalを作って、 他のサブルーチンから見えないようにして下さい。
プロパティは空白で区切って、発言とそれに対する返事を書くようにしました。 そこで、それぞれのプロパティを空白で区切って、 発言と一致するかどうかを比較します。

        package auto_answer;
        $_ = 'auto_answer';

        sub ss_privmsg {
          local($serverno, $prefix, $cmd, $chan, $msg) = @_;
          local($userno, $str, $answer);
          $userno = $'userno[$serverno];
          foreach $reply (&'property($userno, 'reply')) {
            ($str, $answer) = split(/\s+/, $reply, 2);
            if ($msg eq $str) {
              # まだ作ってない部分
            }
          }
          return ($prefix, $cmd, $chan, $msg);
        }

ここまで来ればあと少しです。あとは対応する返事をするだけです。 返事は発言のあったチャンネルにすればいいのですが、 少し問題があって、自分あてのプライベートな発言の場合は、 $chanにはチャンネル名の変わりに自分のニックネームが入っています。 そこで、発言がチャンネルに対してのものなのか、 自分に対するものなのかを区別する必要があります。 チャンネル名は頭に必ず「#」、「&」、「+」、「-」のどれかがつくので、 そうでないものは無視します。

        package auto_answer;
        $_ = 'auto_answer';

        sub ss_privmsg {
          local($serverno, $prefix, $cmd, $chan, $msg) = @_;
          local($userno, $str, $answer);
          $userno = $'userno[$serverno];
          if ($chan =~ /^[\#\&\+\-]/) {
            foreach $reply (&'property($userno, 'reply')) {
              ($str, $answer) = split(/\s+/, $reply, 2);
              if ($msg eq $str) {
                &'s_print($serverno, '', 'PRIVMSG', $chan, $answer);
                last;
              }
            }
          }
          return ($prefix, $cmd, $chan, $msg);
        }

返事はサーバに対して行わなければなりません。 この場合は発言がされたサーバがわかっているので、 返事もそのサーバに対して行います。
これでとりあえず完成です。このモジュールを使用するためには plum.confに以下のように設定します。

    + auto/answer.plm
    auto.answer.reply: こんにちは こんにちは〜☆

これで誰かが「こんにちは」と発言すると、「こんにちは〜☆」と返事をします。
auto/answer.plmというファイルはすでに存在していますが、 このモジュールは実際にこのチュートリアルを書きながら作ったものです。 ここで作ったものより多少機能が増えていますが、 ある程度わかってしまえばモジュールの改造も簡単です。