Tech 4 Mine :-) 忘れぬ先のテックメモ

ゆるエンジニアな私が、多少なり役に立ったひらめきやらTipsを忘れる前に書いていくブログです。

robot.hear/respondなどのマッチ条件を動的に定義する

仕組み?

DBにマッチ条件の文字を登録->スクリプト内で読み込み->ループ処理
やあ、やぁ、こんにちは、おはよう的なサムシングに対して、こんにちは!ぼくはぼっと!と返す処理を作ります。
そして最終的にいい感じにします。

ファイル構成

  scripts(Hubotのscriptsディレクトリ)  
  ├ index.js  
  ├ register_api.js  
  └db  
    ├ files  
    │  └ definition.db  
    ├ nedb_wrapper.js  
    ├ file_db_controller.js  
    └ mem_db_controller.js  

多分こうなります。気持ちいらないものもありますがご愛嬌ってことで。。。

HowTo

NeDBを利用してデータ操作を行うので、こちらの記事でNeDBの実装をしておきます。
Hubotと上記のNeDBを前提にしています。

データの準備

ひとまずJson形式で反応するキーワードリストを作りましょう。

const json_data = {
    key: "keywords",
    value: ["やあ","やぁ","こんにちは","おはよう"]
}

これをDBにinsertします。今回はファイル、メモリどちらでもOKですが、登録内容の確認にも便利なので最初はファイルDBとして初期化することをオススメします。
ログ出力やエラー処理は省略。

const db = require('./db/file_db_controller');

db.insertQuery(json_data, db.GetConnection);

データの準備ができました。scriptフォルダ以下に早速待受処理を書いていきます。

value正規表現にして判定に利用する

現状は1レコードのみなので、適当にDBからレコードを抽出、Valueの配列をfor文で回します。

const query = {
    key: "keywords"
}
db.findAllQuery(query, db.GetConnection)
.then(param => {
    for(let word of param.value){
        let regexp = new RegExp(word, 'i');

        robot.hear(regexp, (res) => {
            res.send("こんにちは!ぼくはぼっと!");
        });

    }
})

はい、できました。
挨拶は大事ですが、これでは応答が固定で、ひたすら挨拶する残念な子になってしまいますね。

応答メッセージもセットにする

せっかくJSON形式を使うので応答メッセージもおなじレコードに入れ込みましょう。
json_dataに要素を追加します。

const json_data = {
    key: "keywords",
    value: ["やあ","やぁ","こんにちは","おはよう"],
    response: "こんにちは!ぼくはぼっと!"  // responseとして回答テキストを追加
}

読み込み~応答部分も変更しましょう。

const query = {
    key: "keywords"
}
db.findAllQuery(query, db.GetConnection)
//.then(param => {
.then(params => {
    for(let param of params){
        for(let word of param.value){
            let regexp = new RegExp(word, 'i');

            robot.hear(regexp, (res) => {
                //res.send("こんにちは!ぼくはぼっと!");
                res.send(param.response);
            });

        }
    }
})

複数のレコードがあってもいいようにfindクエリの結果をfor文で回すように変更しました。
あとはres.send部分が変数化されたくらいです。

API経由でJsonパラメータを受け取ってみる

最後にレコードの取り込み用のAPIを追加しましょう。
ServerURL/api/v1/registerへJson形式でPOSTすることにします。
Json形式のデータを受け付けるので、BodyParserを使います。
※送るデータは{value:["よいではないか"], response:"あーれぇー"}のような形にします。valueは配列にするのを忘れずに。

// register_api.js
const db = require('./db/file_db_controller');
const path = require('path');
const bodyParser = require('body-parser');

module.exports = (robot) => {
    robot.router.use(bodyParser.urlencoded({ extended: true }));
    robot.router.use(bodyParser.json());

    robot.router.post('/api/v1/register', async (req, res) => {
        if (!req.body) return;

        const json_data = {
            key: "keywords",
            value: req.body.value,
            response: req.body.response
        }

        await db.insertQuery(json_data, db.GetConnection);
        robot.loadFile(path.resolve(__dirname, "./"), 'index.js');
        res.send(200);
    })
}

DBへ受け付けたレコードを流し込んだら、robot.loadFileコマンドでjsファイルを再読み込み、先程追加したレコードを有効化します。
すべて終わったら、レスポンスコード200でも返しておきましょう。

ついでに最終的なindex.jsはこんな感じ。

// index.js
const db = require('./db/file_db_controller');

module.exports = (robot) => {
    const query = { key: "keywords" }

    db.findAllQuery(query, db.GetConnection)
    .then(params => {
        for(let param of params){
            for(let word of param.value){
                let regexp = new RegExp(word, 'i');
                console.log("set hearing word: " + word);

                robot.hear(regexp, (res) => {
                    res.send(param.response);
                });
            }
        }
    })
}

ということで完成です。
やあ、というキーワードで、ややあんとにお とかでも反応するのが嫌な場合は、wordの部分を、"^" + word にするなど、先頭一致等の条件をいい具合煮付けてください。
あとは煮るなり焼くなり、認証つけるなり入力フォーム作るなりご自由にドウゾ。