MicrosoftBotFramework v1とv3の罠

結論

  • 使用していたクラスはv1時代のものでv3では非推奨となっている(使えるけど)
  • v1時代はSkypeのグループ会話に対応していなかった
  • v1時代のクラスでBotを開発していたのでSkypeグループでのMentionに反応できなかった
  • v3のクラスに置き換えましょう

事の発端

12/18に横浜でMicrosoftBotFrameworkのハッカソンを開いたとき、参加者の方からこのような質問をいただきました。

BotFrameworkで作成したBotが、普通の1対1の会話には反応してくれるけどグループ会話に追加したときに反応してくれない

 

そもそもBotFrameworkはSkypeのグループチャットを有効にしたとき、「@botname こんにちは」などのMentionを飛ばすことで反応ができるはずなのに実際に動きを見せてもらったら見事に反応してませんでした。

その後ソースコードをいただき、その原因を探ってみました。

ソースコードは以下となります。(掲載許可はいただきました)

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

var port = process.env.PORT || 8080;


// Create bot and add dialogs
var bot = new builder.BotConnectorBot({ appId: process.env.MICROSOFT_APP_ID||'', appSecret: process.env.MICROSOFT_APP_PASSWORD||'' });
var dialog = new builder.CommandDialog();

dialog.matches(["help"],function(session){
  session.send("これはゲームしよbot(仮)です。");
  session.send("現在応答する単語には Hi,hi,Hello,hello,こんにちは,シージしよ,シージして? があります。")
})
dialog.matches(["Hi","hi","hello","Hello","こんにちは"],function(session){
  session.send("こんにちは");
});
dialog.matches(["ゲームしよ"],function(session){
  session.send("いいよ");
});
dialog.matches(["ゲームして?"],function(session){
  session.send("やだよぉ");
});
dialog.onDefault(function(session){
    session.send("ゲームしよ");
})
bot.add('/',dialog);

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

 

原因調査

原因調査するにはデバッグするのが一番ということでローカルに実行環境を作成しデバッグしてみるとコンソールに以下のようなメッセージが表示されていました。

 

BotConnectorBot class is deprecated. Use UniversalBot with a ChatConnector class.
CommandDialog class is deprecated. Use IntentDialog class instead.

 

どうやらBotConnectorBotクラスと、CommandDialogクラスは現在のバージョンでは非推奨のようです。

それぞれ、UniversalBotとIntentDialogに置き換えましょうとのことでした。

ソースコードの場所を見ても、deprecatedフォルダ以下にあるので完全に非推奨クラスのようです。

 

BotFrameworkはv1から始まり、現在v3なのですが、v3で大きく仕様が変化しました。

また、v1のドキュメントを見ても、Skypeのグループ会話機能の説明がないことから、Skypeのグループ会話機能はv3からということが推測できます。

 

つまり、v3の環境で、v1のクラスを呼び出していたため、Skypeのグループ会話に対応していないv1では、グループ会話に応答できなかったということです。

 

対処方法

v3のコードに置き換えましょう。

実際に置き換えたサンプルを以下に示します。

//BotBuilder v3テンプレートコードは公式のドキュメントを見るとよい
//https://docs.botframework.com/en-us/node/builder/overview/#navtitle
var restify = require('restify');
var builder = require('botbuilder');

//restifyでhttpサーバーを作る
var server = restify.createServer();
server.listen(process.env.port || process.env.PORT || 3978, function () {
   console.log('%s listening to %s', server.name, server.url); 
});

// Connectorを作る
// AppIDとPasswordで認証する
var connector = new builder.ChatConnector({
    appId: process.env.MICROSOFT_APP_ID,
    appPassword: process.env.MICROSOFT_APP_PASSWORD
});

//Botを作成する
var bot = new builder.UniversalBot(connector);
//{your bot url}/api/messages で通信を待つ
server.post('/api/messages', connector.listen());

//v3のIntentDialogを作成する
//https://docs.botframework.com/en-us/node/builder/chat/IntentDialog/#navtitle
var intents = new builder.IntentDialog();
bot.dialog('/', intents);

//複数の条件を指定する場合はmatchesAnyに配列で渡す
//正規表現で .*hoge.* は「hogeが入っているなら」にマッチする
// 「/  /i」  正規表現のiフラグは大文字小文字を区別しないようにできる
// http://kyu-mu.net/coffeescript/regexp/
intents.matchesAny([/.*hi.*/i,/.*hello.*/i,/.*こんにちは.*/i],function(session){
  session.send('こんにちは');
});

// 1つの条件でいい場合はmatches
// 複数チェインすることも可能
intents.matches(/.*ゲームしよ.*/i,function(session){
  session.send('いいよ');
}).matches(/.*ゲームして?.*/i,function(session){
  session.send('やだよぉ');
});

// どの条件にもマッチしない場合はonDefault
intents.onDefault(function(session){
  session.send("ゲームしよう");
});

 

これで正しく応答することができます。

1対1の場合

グループ会話の場合

Mention以外には反応しない

Mentionにだけ反応する

対策方法

BotFrameworkは最近出たということもあり、仕様がどんどん進化していきます。(v3でだいぶ安定してるとは思いますが)

このようなところに引っかからないためには以下のことに注意すると良いです。

デバッグメッセージを見る

Microsoftの製品はとくに、広報互換性を重視しているので古いコードでも難なく動いてしまいます。

そういう時は、必ずデバッグメッセージとして表示されますのでそれを注意深く読んでみるのがいいかと思います。

デバッグメッセージはたいてい英語ですが頑張れば読めます

公式ドキュメントを見る

公式ドキュメントは常に最新の情報が上がっているので仕様が安定していないようなプラットフォームであれば、公式を見るのがよいでしょう。

英語ですが頑張れば読めます。

Botの応答でprintfデバッグ

Bot系は特に、ローカル環境でのデバッグが難しいプラットフォームになります。printfのような出力環境も難しいので、Botの応答に変数の値を出力させちゃうのが良いでしょう。

node.jsの場合であればJSON.stringifyでJSON化して応答させるとみやすくて良いです。

//メッセージオブジェクトの中身をjson化して応答
intents.onDefault(function(session){
  session.send(JSON.stringify(session.message));
});

 

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください