特集
» 2016年04月26日 05時00分 UPDATE

特集: 新たなアプリ「ボット」の時代:Slackでボットと対話してみよう (1/4)

Microsoft Bot Frameworkを使用して作成したボットをAzureにデプロイ、Bot Connectorに登録し、最終的にSlackで対話を行う。

[かわさきしんじ,Insider.NET編集部]
特集: 新たなアプリ「ボット」の時代
Insider.NET

               

「特集: 新たなアプリ「ボット」の時代」のインデックス

連載目次

 前回は、Microsoft Bot Framework(以下、Bot Framework)を使用して、シンプルなボットを作成しながら、ダイアログ、コマンド、ウオーターフォールなどの基本要素について見た。今回は少し複雑なボットを作成して、Azureにデプロイ、これをSlackで使えるようにするまでを見てみよう。

今回作成するボット

 今回は対話的に名前と電話番号のセットを登録し、そのデータを検索、一覧表示できるようなボットを作成する。ボットに「add」「find」などと呼びかけると、対話が始まり、名前と電話番号を入力したり、電話番号を検索したりできる。以下にSlackでボットと対話している様子を示す。

Slackでボットと対話しているところ Slackでボットと対話しているところ

 このボットとSlackで対話できるようにするには、おおよそ次のような段階を踏んでいく。

  1. ボットを作成する
  2. Azureにデプロイする
  3. Bot Connectorに登録する
  4. Slackに接続するための設定を行う

 それではそれぞれのステップを順に見ていくことにしよう。

ボットを作成する

 ボットの作成に当たっては前回と同様にbotbuilder/restifyの2つのパッケージをnpmからインストールしている(「npm init」コマンドと「npm install botbuilder restify --save」コマンドを実行している)。

 実際のコードは次のようになる。

var restify = require('restify');
var builder = require('botbuilder');
var tmpdata = {};

var bot = new builder.BotConnectorBot(
  { appId: 'process.env.appid',
    appSecret: 'process.env.appsecret' });

bot.use(function(session, next) {
  if (!session.userData.addrbook)
    session.userData.addrbook = [];
  next();
});

bot.add('/', new builder.CommandDialog()
  .matches('^(regist|add)',  builder.DialogAction.beginDialog('/regist'))
  .matches('^(find|search)', builder.DialogAction.beginDialog('/find'))
  .matches('^list', showList)
  .onDefault(function (session) {
    var msg = 'you have ' + session.userData.addrbook.length + ' data.';
    session.send('Hello, I am address book bot. ' + msg);
  }));

function showList(session) {
  var tmp = session.userData.addrbook.map(
    current => 'name: ' + current.name + ' tel: ' + current.tel);
  session.send(tmp.join(', '));
}

bot.add('/regist', [
  function(session) { 
    builder.Prompts.text(session, 'what name do you want to add ?');
  },
  function(session, results) {
    tmpdata.name = results.response;
    builder.Prompts.text(session, 'telephone number? ');
  },
  function(session, results) {
    tmpdata.tel = results.response;
    session.userData.addrbook.push(tmpdata);
    session.send('registerd!');
    session.endDialog();
  }
]);

bot.add('/find', [
  function(session) {
    builder.Prompts.text(session, 'find? ');
  },
  function(session, results) {
    var target = results.response;
    var res = session.userData.addrbook.filter(
      item => item.name == target ? true : false
    );
    if (res[0]) {
      session.send("name: " + res[0].name + " tel: " + res[0].tel);
    } else {
      session.send('no such data');
    }
    session.endDialog();
  }, 
]);

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

名前と電話番号を登録/検索/表示するボット(server.jsファイル)

 コマンド、ウオーターフォールなど、コードを構成する要素は前回とほぼ変わらないので、前回と異なる点のみを簡単に説明しておこう。ただし、その前に1点。今回はボットのコードを記述するファイルの名前が「server.js」になっている。これは、Azure側の都合であり、「server.js」ファイルがある場合にはそのWebアプリがNode.jsを使って記述されていると判断するようになっているためだ。

ボットの概要

 このボットは以下の3種類のコマンドを受け付ける。サンプルなのでデータの修正と削除は実装していない。

  • add/regist: 名前と電話番号を登録する
  • find/search: 電話番号を検索する
  • list: 名前と電話番号を一覧表示する

 コマンドのディスパッチを行っているのが以下のコードだ。

bot.add('/', new builder.CommandDialog()
  .matches('^(regist|add)',  builder.DialogAction.beginDialog('/regist'))
  .matches('^(find|search)', builder.DialogAction.beginDialog('/find'))
  .matches('^list', showList)
  .onDefault(function (session) {
    …… 省略 ……
  }));

コマンドのディスパッチ

 matchesメソッドでのコマンドのマッチ処理ではadd/registなど、同じ処理を行うコマンドを正規表現でひとまとめに記述している。登録と検索については前回と同様にウオーターフォールを使ってボットと対話しながら登録/検索を行う。一覧表示については、対話の必要がないので、showList関数を実行するように指定している(強調表示の部分)。シンプルな処理については、このように関数一発で処理を終わらせても構わない。showList関数のコードを以下に示す。特に説明の必要はないだろう。

function showList(session) {
  var tmp = session.userData.addrbook.map(
    current => 'name: ' + current.name + ' tel: ' + current.tel);
  session.send(tmp.join(', '));
}

showList関数
mapメソッドで「name: ... tel: ...」形式の文字列を要素とする配列を作成し、joinメソッドでそれらをカンマ区切りの文字列に変換して返送している。

App IdとApp Secret

 前回は特に気にしていなかったApp IdとApp Secretだが、今回はBot Connector(Bot Frameworkの構成要素の1つで、実際に対話を行う際に使われるWebページやSNSなどのチャンネルと、作成したボットとを接続するコミュニケーションサービス)にボットを登録して、設定を行う際にこれが必要になる。今回はAzureの[アプリケーション設定]で環境変数の形でこれを設定する(後述)。実行時にこれを取得するためのコードは次のようになる。

var bot = new builder.BotConnectorBot(
  { appId: 'process.env.appid',
    appSecret: 'process.env.appsecret' });

App IdとApp Secretを環境変数から取得するコード

 プログラムに直接これらを記述するよりも、上のコードのように環境変数経由で取得した方がApp IdとApp Secretが他者にのぞかれる可能性が低く、安全な方法だろう。なお、Bot Emulatorでテストする場合には、これらの値はundefinedのままでも構わないようだ。

アプリの初期設定

 このボットではセッションごとに固有のユーザーデータ(session.userDataプロパティ)にaddrbookプロパティを追加し、そこに名前と電話番号のセットを保存している。が、ここで注意点が1つある。ボットとの対話がルート(ID)で分岐するので、そのままではボット全体で使い回すデータ(ここではaddrbookプロパティ)がきちんと初期化されているかをルートごとにチェックする必要があるのだ(少なくとも本稿のボットでは、どのコマンドが最初に実行されるかをプログラマーの側が強制できないため)。

 さらにセッションデータはコールバック関数の引数として渡されるため、パッと見ではaddrbookプロパティを初期化する場所がない。そこでここでは以下のようにして、ミドルウェアを定義して、そこで初期化を行うようにしている。

bot.use(function(session, next) {
  if (!session.userData.addrbook)
    session.userData.addrbook = [];
  next();
});

初期化を行うミドルウェア

 Bot Builder SDKではDialogCollectionクラス(前回見たTextBotクラスやBotConnectorBotクラスなどのボットクラスの基底クラス)でuseメソッドが定義されており、これを使ってミドルウェアを登録できる。ミドルウェアとして登録されたコードは、ボットがメッセージを受信するたびに呼び出され、そこで何か特別な処理を加えることができる。ここでは、「session.userData.addrbook」プロパティの有無を判定し、なければ初期化するようにしている。

 毎回呼び出されるのでパフォーマンス的には気になるところだが、今回はこのようにしている(実際、Bot Builder SDKのサンプル「basics-firstRun」でもこの方法が例示されているので、これが一般的な初期化方法だと考えられる)。

 これにより、コードの他の箇所ではsession.userData.addrbookプロパティが存在していることを前提としたコードを記述できるようになっている。

bot.add('/', new builder.CommandDialog()
  …… 省略 ……
  .onDefault(function (session) {
    var msg = 'you have ' + session.userData.addrbook.length + ' data.';
    session.send('Hello, I am address book bot. ' + msg);
  }));

session.userData.addrbookプロパティは常に存在するので、その有無を確認せずに使える

 コードの説明はここまでとして、次ページではAzureへのデプロイ手順を見てみよう。

       1|2|3|4 次のページへ

Copyright© 1999-2017 Digital Advantage Corp. All Rights Reserved.

@IT Special

- PR -

TechTargetジャパン

この記事に関連するホワイトペーパー

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。