SlackとWeb上のチャットをつなぐ / MS謹製のbot作成フレームワーク「Bot Builder」で遊んでみた！


先日、Bot Builderを触ってみてどんな感じかな？という記事をQiitaに上げましたが
この「Microsoft Bot Framework」、公式ドキュメントを見るとこんな風に書かれているわけです。

![](http://docs.botframework.com/images/bot_framework_wht_bgrnd.png)
http://docs.botframework.com/

つまり、「botを簡単に作れる(SDK)」というのはこのフレームワーク全体のごくごく一部でしかなく、「複数チャンネルにシームレスに接続できる」というのも強みなわけです。
これがあることで「botをプログラミングする」ことに対する見返りが格段に大きくなりそうですね・・・！

今回は、この「Bot Connector」で遊んでみたいと思います。

## アプリケーションの登録
最初に、Botframeworkにアプリケーションの登録を行う(app id, secretを取得しておく)必要があります。

1. https://dev.botframework.com/#/bots を開きます。必要であればココからMSアカウントのサインインが可能です
2. サインインしたらbotの登録が出来ますので、諸々の項目を埋めます
3. Endpoint, app id, secretを確認して下さい。  
  * Endpointは「チャットを受け取った時にその内容を送信する(hookする)」先になり、グローバルにアクセスする必要があります。
  * ドキュメントを見ると「Azureからアクセスできること」という要件が記載されています  
  * ※ 画像は`localhost` にしていますが、これだと動かないです。。
![Kobito.OgsBes.png](https://qiita-image-store.s3.amazonaws.com/0/941/61366411-7290-1cfc-f0a1-f4a27bcf70c6.png "Kobito.OgsBes.png")

## Hello World

早速、botを書いて接続してみましょう！
試しに、「Webチャット」「Slack」を接続してみました。
Webチャットは、 Bot Connector側でiframeを用意してくれているのでインターフェイスを作成する必要もなしです・・・！

### チャンネルの接続・有効化

#### Webチャットの有効化
botの設定画面でワンクリックで接続できるチャンネルの一覧が表示されているので、そこからWebを選択します。
![botconnect-web.gif](https://qiita-image-store.s3.amazonaws.com/0/941/28ed518a-5768-2508-05b5-052864db9c55.gif "botconnect-web.gif")

これだけでWebチャットを利用する用意が完了しました(!)
あとは、表示されている「Embed templateを任意のWebページに設置して下さい
※ iframeのsrcにある`YOUR_SECRET_HERE`を書き換えて下さい

#### Slackの有効化

Slackも同様にAddして完了ですw
(Slack側の)token,secretの取得が必要な場合は、 「Add To Slack」ボタンが表示されるので利用して下さい。

### コード

http://docs.botframework.com/connector/libraries/node にサンプルがありますので、これをベースにオウム返し系botを作ってみます。

```js
var restify = require('restify');
var msRest = require('ms-rest');
var connector = require('botconnector');

var server = restify.createServer();
server.use(restify.authorizationParser());
server.use(restify.bodyParser());

/*
 * AppSecretには「Primary app secret」の値を入れればOKです！
 */
var appId = process.env.appId || 'your appId';
var appSecret = process.env.appSecret || 'your appSecret';
var credentials = new msRest.BasicAuthenticationCredentials(appId, appSecret);

/*
 * サンプル中とのコードと異なる部分。
 * 第2引数に「リクエストの認証」を入れていますが、EndpointがHTTPの場合は認証が渡ってきません。
 */
server.post('/v1/messages', function (req, res) {
    var msg = req.body;
    if (/^delay/i.test(msg.text)) {
        setTimeout(function () {
            var reply = { 
                replyToMessageId: msg.id,
                to: msg.from,
                from: msg.to,
                text: 'I heard "' + msg.text.substring(6) + '"'
            };
            sendMessage(reply);
        }, 5000);
        res.send({ text: 'ok... sending reply in 5 seconds.' })
    } else {
        res.send({ text: 'I heard "' + msg.text + '". Say "delay {msg}" to send with a 5 second delay.' })
    }
});

server.listen(process.env.PORT || 8080, function () {
    console.log('%s listening to %s', server.name, server.url); 
});

function sendMessage(msg, cb) {
    var client = new connector(credentials);
    var options = { customHeaders: {'Ocp-Apim-Subscription-Key': credentials.password}};
    client.messages.sendMessage(msg, options, function (err, result, request, response) {
        if (!err && response && response.statusCode >= 400) {
            err = new Error('Message rejected with "' + response.statusMessage + '"');
        }
        if (cb) {
            cb(err);
        }
    });
}
```

大きく変更した箇所はありませんが、受け取った内容を承認する処理を省略しています。
エンドポイントをHTTPSにしている場合は、App-ID:AppSecretでBasic認証をかけることができますが(※未検証です><)、そうでない場合はrequest.autorizationに値が入ってきません。そのため、全ての認証に失敗する状況になってしまいます。

 * 参考1・・公式: http://docs.botframework.com/connector/authorization/#navtitle
 * 参考2・・[StackOverflow: http://stackoverflow.com/questions/36338995/microsoft-bot-frameworks-verifybotframework-always-returning-forbidden/36339365

### 実際の動作を確認する
スクリプト中でappId/appSecretを環境変数から呼び出しているので、それらを付けて実行してあげてください
```sh
appId=hogehoge appSecret=fugafuga node bot.js
```

無事に起動すると、次のようなイメージで動かすことが出来ます。
1つのソースで、簡単に複数のチャンネルにbotを組み込むことが出来る・・・というのを確認できると思います
#### Webチャット デモ
![botconnector-webchat.gif](https://qiita-image-store.s3.amazonaws.com/0/941/05f0715a-b529-35f4-9030-7a6e305dee0f.gif "botconnector-webchat.gif")

#### Slackチャット デモ
![botconnector-slack.gif](https://qiita-image-store.s3.amazonaws.com/0/941/0312e1c0-11c6-8c58-a7bd-c5377c8ef149.gif "botconnector-slack.gif")

### Bot Builderを利用して
先の記事で触れているBot Builder([その1](http://qiita.com/o0h/items/1a51cd0bbd9c38027388)・[その2](http://qiita.com/o0h/items/6e0f7a705f7162a49394))ですが、
実はこの「Bot Connector」をベースにしたClientが存在します。([公式:BotConnectorBot](http://docs.botframework.com/builder/node/bots/BotConnectorBot/))
なので、Dialogを使ってゴニョゴニョして・・という実装も、簡単に複数チャンネルに繋ぎこむことが可能です。
例えば、以下のコードで「挨拶をしたらこんにちはと返す」botが動作します。

```js
var restify = require('restify');
var builder = require('botbuilder');

var bot = new builder.BotConnectorBot({ appId: process.env.appId, appSecret: process.env.appSecret});
var dialog   = new builder.CommandDialog();

dialog.matches(['Hi', 'Hello', 'こんにちは'], function (session) {
  session.send('こんにちは');
});
bot.add('/', dialog);

var server = restify.createServer();
server.post('/v1/messages', bot.listen());
server.listen(process.env.port || 8080, function () {
    console.log('%s listening to %s', server.name, server.url);
});
```

## 感想
また「非常に簡単にbotが動くぞ」という感想です・・・
ただ、現状だとドキュメントがまともにないような印象があって結構辛い。
まだまだ触りきれていない実感があるので、ちょいちょい掘り下げて遊ぶぞ〜〜というのは暫く継続していくつもりです！
botは楽しいー