Errbot: Python製chatbotの基礎


## 概要

ErrbotはPython製のchatbotです。

このページにはErrbotのプラグイン（自作bot）の作り方で学んだことをまとめます。
内容は公式ドキュメントの[Plugin Development](http://errbot.io/en/latest/user_guide/plugin_development)に概ね沿っていて
Pythonの基礎的な知識があれば容易に理解できるはずです。

環境構築については[以前の記事](http://qiita.com/nanakenashi/items/2f932bb84f05e84df954)をご参照ください。

## 作成前の基礎知識

### ローカルディレクトリの読み込み

基本的にErrbotにおけるpluginは
組み込みの`!repos`コマンドによりgitを経由して管理されます。

ただし、開発中はいちいちgitリポジトリにソースをあげるのも手間ですので
ローカルディレクトリからpluginを読み込みます。

`errbot --init`によって生成された`config.py`の中に
`BOT_EXTRA_PLUGIN_DIR`という設定項目があります。
ここにローカルディレクトリとしたいディレクトリのパスを記載します。

とはいえ、デフォルトで`plugins`ディレクトリが指定されているはずですので
そのまま使って問題ないはずです。

### テストモードでの実行

他のサーバとの接続なしでbotのデバッグをするため
起動時に`--text`か`-T`を指定することで、テストモードでの実行が行えます。
`test`ではなく`text`です。

```bash:テストモードでの起動
$ errbot --text
```

### pluginの構成ファイル

* `.plug`ファイル: pluginのメタデータをINIファイル形式で記載
* `.py`ファイル: pluginで実際に処理されるPythonコードを記載

詳細は後述します。

## Hello, World!

### ファイルの作成

`BOT_EXTRA_PLUGIN_DIR`で指定されているディレクトリ内に
`HelloWorld`ディレクトリとその構成ファイルを作成します。

```text
plugins/           
|~err-example/     
| |-example.plug   
| `-example.py     
`~HelloWorld/      
  |-helloworld.plug
  `-helloworld.py  
```

helloworld.pyには、例に従って以下のように記載します。

```py:helloworld.py
from errbot import BotPlugin, botcmd

class HelloWorld(BotPlugin):
    """Example 'Hello, world!' plugin for Errbot"""

    @botcmd
    def hello(self, msg, args):
        """Say hello to the world"""
        return "Hello, world!"
```

### コードの解説

まず、pluginの継承元になるクラスと
コマンドに対応する関数を作成するためのデコレータをimportします。

```py:helloworld.py
from errbot import BotPlugin, botcmd
```

BotPluginクラスを継承し、plugin用のクラスを生成します。
docstringの部分は`!help`コマンドを使った際に表示されます。

```py:helloworld.py
class HelloWorld(BotPlugin):
    """Example 'Hello, world!' plugin for Errbot"""
```

次に関数を記載し、`@botcmd`でデコレートします。
関数名がコマンド名になり、botは`!hello`コマンドを受け付けるようになります。
関数内のdocstringも、`!help`コマンドを使った際に表示されます。

```py:helloworld.py
@botcmd
def hello(self, msg, args):
    """Say hello to the world"""
    return "Hello, world!"
```

`hello`関数は`msg`と`args`という複数の引数を受け取ります。

* `msg`: ユーザが入力した文章全体
* `args`: ユーザが入力した文章からコマンド部分を除いたもの

例えば、`!hello hoge moge`と入力した際は以下の値を格納しています。

* `msg`: `'!hello hoge moge'`
* `args`: `'hoge moge'`

両方ただの文字列っぽく書いてますが、`msg`は
`errbot.backends.base.Message`クラスのインスタンスのようです。

## pluginのメタデータ

helloworld.plugには、例に従って以下のように記載します。

```
[Core]
Name = HelloWorld
Module = helloworld

[Python]
Version = 2+

[Documentation]
Description = Example "Hello, world!" plugin
```

* `Name`: 実行ファイルの名前から`.py`を除いたもの
* `Module`: botクラスとなるモジュール（`Name`で指定したファイル内）
* `Version`: 対応するPythonのバージョン（詳細不明）
* `Description`: プラグインの詳細（詳細不明）

## 作成したbotの実行

ここまで記載すれば、botの実行が可能になります。

```bash:helloコマンドの実行
 >>> !hello
Hello, World!
```

## コマンド作成における応用

### 引数の自動分割

`botcmd`デコレータには引数が指定可能です。
キーワード引数として`split_args_with`を指定することで
前述の`args`変数をリストとして受け取ることができます。

```py:引数の分割
@botcmd(split_args_with=None)
def action(self, mess, args):
    # !action one two three と入力したときに
    # argsは ['one', 'two', 'three']を受け取る
```

`split_args_with`の指定は`str.split()`のように振る舞い
`None`はホワイトスペースを示しています。

### サブコマンド

関数名の中で`_`を使用している場合、`_`以降はサブコマンドとして扱われます。
これはコマンドのグルーピングに役立ちます。

```py:サブコマンド
@botcmd
def basket_add(self, mess, args):
    # !basket add という入力に対応
    pass

@botcmd
def basket_remove(self, mess, args):
    # !basket remove という入力に対応
    pass
```

このとき、`add`と`remove`はあくまでもコマンドの一部ですので
`args`の中に引数として格納されることはないようです。

### argparseによる引数の分割

`botcmd`の代わりに`arg_botcmd`デコレータを使用することで
[argparse](https://docs.python.org/3/library/argparse.html)のフォーマットで引数を定義することができます。
デコレータは重ねて記載することで複数の指定が可能です。

```py:argparseによる引数の指定
@arg_botcmd('first_name', type=str)
@arg_botcmd('--last-name', dest='last_name', type=str)
@arg_botcmd('--favorite', dest='favorite_number', type=int, default=42)
def hello(self, mess, first_name=None, last_name=None, favorite_number=None):
    # !hello Err --last-name Bot と入力したとき
    # first_name は 'Err'
    # last_name は 'Bot'
    # favorite_number は 42（デフォルト値）
```

## まとめ

pluginの作成の基本的な部分について書きました。
引数はある程度自由に受け取れるようになったため
あとはPythonで実行させたい処理を好き勝手に処理を書くだけです。

他にも正規表現を用いたコマンドの使用やテンプレートの活用など
シンプルながら多くの機能があるようです。
興味を持たれたら、[ドキュメント](http://errbot.io/en/latest/user_guide/plugin_development/)をご一読ください。