Azure Bot Service + Emotion API + LUIS

※Cognitive ServicesのURLが変わったため、index.jsと気づいた一部スクリーンショットを更新。
　他の画面も変わってるかも。

#1. Emotion API

Azure管理ポータル
https://portal.azure.com/
の左のメニューの一番上にある「＋」をクリック、
「Intelligence + analytics」
　-> 「Cognitive Services APIs (プレビュー)」

![emotion00](http://www.hpi-portal.com/azure/images/emotion00.png)

「Account Name」を半角英字で入力、
「サブスクリプション」を選択、
「API Type」は「Emotion API (Preview)」を選択、

![emotion01](http://www.hpi-portal.com/azure/images/emotion01.png)

「Pricing tier」は「F0 Free」を選択、

![emotion02](http://www.hpi-portal.com/azure/images/emotion02.png)

「Legal terms」は「I Agree」をクリック、
最後に「作成」ボタンをクリック。

作成されたら、Azure管理ポータルのサービスメニューから「Cognitive Services accounts」を選択、
アカウント名をクリック、
「Keys」をクリック、
「KEY 1」の文字列をコピーして控える。


#2. Bot Service

https://docs.botframework.com/en-us/azure-bots/build/first-bot/

基本的に上記URLのページ記載のとおり。

![botservice01](http://www.hpi-portal.com/azure/images/botservice01.png)

「3. Select the programming language that you want to use to develop your bot.」では「NodeJS」を選択、
「4. Select the template to use as the starting point for writing your bot.」では「Language understanding」を選択。

Bot Serviceは、「App Service」に作成される。
「App Service」で作成されたアプリ名をクリック、
画面上部の「Settings」をクリック、
「Application settings」の「Open」をクリック、
「アプリ設定」の下のほうにある「LuisAPIKey」をコピーして控える、

![botservice02](http://www.hpi-portal.com/azure/images/botservice02.png)

また、キーが「EmotionAPIKey」で値が1.で控えたキーの文字列でる設定を追加する。

LUIS(Language Understanding Intelligent Service)の設定もされている。
だが、LUISのサイト
https://www.luis.ai/
で設定内容を確認すると、言語がEnglishになっていて、日本語をうまく認識してくれない。

![luis01](http://www.hpi-portal.com/azure/images/luis01.png)

そこで、自動作成されたLUISのアプリを削除し、新たにJapaneseでアプリを作成する。

![luis02](http://www.hpi-portal.com/azure/images/luis02.png)

作成されたら、アプリケーション名をクリック、
画面左上の「App Settings」をクリック、
「App Id」をコピーして控え、
「App Name」をわかりやすいものに変更、
「Save a new endpoint key:」に先程控えた「LuisAPIKey」の文字列をペーストし、「Add New Key」をクリック、
最後に「Close」をクリック。

Azure管理ポータルの先程の「ボットサービス」の画面に戻り、
「アプリケーション設定」の「LuisAppId」にLUISの画面で控えた「App Id」をペースト、
画面上部の「保存」をクリック。

![botservice02](http://www.hpi-portal.com/azure/images/botservice02.png)

なお、実際にサービスを提供する場合は、「ボットサービス」の「Channels」「Publish」の設定を行う。

![botservice04](http://www.hpi-portal.com/azure/images/botservice04.png)

![botservice05](http://www.hpi-portal.com/azure/images/botservice05.png)

#3. LUIS

https://www.luis.ai/
にアクセス、
アプリケーション名をクリック、
画面左側の「Intents」の右の「＋」をクリック、
「Intent name:」に半角英字で入力、
「Enter an example of a command that triggers this intent:」に日本語を入力、

![luis03](http://www.hpi-portal.com/azure/images/luis03.png)

「New utterances」タブで「Submit」をクリック、
同じく「New utterances」タブで別の日本語を入力し、先程入力した「Intent name:」を選択し、「Submit」をクリック、

![luis04](http://www.hpi-portal.com/azure/images/luis04.png)

登録したいIntentの分だけ上記の作業を繰り返す。

画面左下の「Train」をクリック、
画面左上の「Publish」をクリック、
「Update published application」をクリック。

![luis05](http://dev-web.hpi-portal.com/azure/images/luis05.png)

#4. index.jsのサンプル

「ボットサービス」の「Develop」の画面で編集。
動作確認は、同じ画面の右側のWebチャットのボックスで行う。

![botservice03](http://www.hpi-portal.com/azure/images/botservice03.png)

```javascript:index.js
"use strict";
var request = require('request');
var builder = require("botbuilder");
var botbuilder_azure = require("botbuilder-azure");

var useEmulator = (process.env.NODE_ENV == 'development');

var connector = useEmulator ? new builder.ChatConnector() : new botbuilder_azure.BotServiceConnector({
  appId: process.env['MicrosoftAppId'],
  appPassword: process.env['MicrosoftAppPassword'],
  stateEndpoint: process.env['BotStateEndpoint'],
  openIdMetadata: process.env['BotOpenIdMetadata']
});

var bot = new builder.UniversalBot(connector);

var emotionAPIKey = process.env.EmotionAPIKey;
var luisAppId = process.env.LuisAppId;
var luisAPIKey = process.env.LuisAPIKey;
var luisAPIHostName = process.env.LuisAPIHostName || 'westus.api.cognitive.microsoft.com';

const LuisModelUrl = 'https://' + luisAPIHostName + '/luis/v2.0/apps/' + luisAppId + '?subscription-key=' + luisAPIKey + '&timezoneOffset=0&verbose=true';

var recognizer = new builder.LuisRecognizer(LuisModelUrl);
var intents = new builder.IntentDialog({ recognizers: [recognizer] });

bot.dialog('/', intents);

/*
 * /get_version ダイアログ
 *
 * 「バージョン」の入力で表示。
 */
intents.matches('getVersion', '/get_version');
bot.dialog('/get_version', (session) => {
  session.send('Bot version 1.0b');
  session.endDialog();
});

/*
 * /start ダイアログ
 *
 * 「やあ」「ハイ」「こんにちは」「スタート」のいずれかの入力で表示。
 * 回答された選択肢に応じて表示ダイアログを切り替える。
 */
intents.matches('start', '/start');
bot.dialog('/start', [
  (session) => {
    session.send('こんにちは');
    builder.Prompts.choice(session, "どちらを確認しますか？", "選択肢1|選択肢2");
  },
  (session, results) => {
    if (results.response.entity === '選択肢1') {
      session.replaceDialog('/select1');
    }
    else if (results.response.entity === '選択肢2') {
      session.replaceDialog('/select2');
    }
    session.endDialog();
  }
]);

/*
 * /select1 ダイアログ
 *
 * 「選択肢1」「選択1」のいずれかの入力、
 * または、startダイアログで「選択肢1」が選ばれたときに表示。
 */
intents.matches('select1', '/select1');
bot.dialog('/select1', [
  (session) => {
    session.send('こちらが選択肢1の内容です。');
    session.endDialog();
  }
]);

/*
 * /select2 ダイアログ
 *
 * 「選択肢2」「選択2」のいずれかの入力、
 * または、startダイアログで「選択肢2」が選ばれたときに表示。
 */
intents.matches('select2', '/select2');
bot.dialog('/select2', [
  (session) => {
    session.send('こちらが選択肢2の内容です。');
    session.endDialog();
  }
]);

// LUISがNoneを返した場合
intents.matches('None', (session) => {
  session.send('こんにちは。<br />該当するintentがありませんでした。<br />あなたはつぎのように書きました：<br /> \'%s\'.', session.message.text);
});

// デフォルトのハンドラー
// ここで選択された画像の処理を行う
intents.onDefault((session) => {
  if (hasImageAttachment(session)) {
    const options = {
      url: 'https://westus.api.cognitive.microsoft.com/emotion/v1.0/recognize',
      headers: {
        'Content-Type': 'application/json',
        'Ocp-Apim-Subscription-Key': emotionAPIKey
      },
      method: 'POST',
      json: { "url": session.message.attachments[0].contentUrl }
    };

    request(options, (error, response, body) => {
      if (!error && response.statusCode == 200) {
        let resultList = [];
        for (let i in body) {
          resultList.push({
            left : body[i].faceRectangle.left,
            anger : (body[i].scores.anger * 100).toFixed(2),
            contempt : (body[i].scores.contempt * 100).toFixed(2),
            disgust : (body[i].scores.disgust * 100).toFixed(2),
            fear : (body[i].scores.fear * 100).toFixed(2),
            happiness : (body[i].scores.happiness * 100).toFixed(2),
            neutral : (body[i].scores.neutral * 100).toFixed(2),
            sadness : (body[i].scores.sadness * 100).toFixed(2),
            surprise : (body[i].scores.surprise * 100).toFixed(2)
          });
        }

        resultList.sort((a, b) => {
          return (a.left < b.left) ? -1 : 1;
        });

        let result = "";
        if (resultList.length > 1) {
          result = '左から<br />-------------------------------<br />';
        }
        resultList.some((val, index) => {
          result
            += 'Anger (怒り)：' + val.anger + '<br />'
             + 'Contempt (軽蔑)：' + val.contempt + '<br />'
             + 'Disgust (嫌悪)：' + val.disgust + '<br />'
             + 'Fear (恐怖)：' + val.fear + '<br />'
             + 'Happiness (幸福)：' + val.happiness + '<br />'
             + 'Neutral (ニュートラル)：' + val.neutral + '<br />'
             + 'Sadness (悲しみ)：' + val.sadness + '<br />'
             + 'Surprise (驚き)：' + val.surprise + '<br />'
             + '-------------------------------<br />';
        });

        session.send(result);
      }
      else {
        console.log('error: '+ response.statusCode);
      }
    });
  }
  else {
    session.send('ごめんなさい。理解できませんでした。 \'%s\'.', session.message.text);
  }
});


//=========================================================
// Utilities
//=========================================================
const hasImageAttachment = (session) => {
  return ((session.message.attachments.length > 0) && (session.message.attachments[0].contentType.indexOf("image") !== -1));
}


// 
if (useEmulator) {
  var restify = require('restify');
  var server = restify.createServer();
  server.listen(3978, function() {
    console.log('test bot endpont at http://localhost:3978/api/messages');
  });
  server.post('/api/messages', connector.listen());
}
else {
  module.exports = { default: connector.listen() }
}
```


#5. 参照URL

https://docs.botframework.com/en-us/node/builder/chat-reference/modules/_botbuilder_d_.html