micro.js（zeit）でAzure Bot serviceのチャットのリクエストを受け付ける

## Azure Bot Service + micro.jsでボットアプリ作成
Azure Bot Serviceを利用してbotアプリを作ってみました。
環境構築は慣れてなくても一瞬で完了します。 
Azure Bot serviceをnode.jsで実装する際にサンプルがrestifyで実装されていましたが
micro.js（zeit）での実装に変更してみました。
環境は以下のようになります。

開発言語 :typescript v3.1.6
実行環境 :Node.js v8.9.4
アプリケーションフレームワーク :[Asynchronous HTTP microservices zeit/micro](https://github.com/zeit/micro)
BOtフレームワーク :[Azure Bot Framework | Microsoft Azure](https://dev.botframework.com/)

### 環境構築(Azure側)

Azure　bot frameworkで検索すると以下のサイトに行きつきこれを手掛かりに
Azure Bot serviceに足を踏み入れることにしました。

![Art237.jpg](https://qiita-image-store.s3.amazonaws.com/0/151893/0e8d6864-73f6-345f-a7cb-68de09c71391.jpeg)

[Azure Bot Framework | Microsoft Azure](https://dev.botframework.com/)

まずは環境を整えるためFrameworkを入手するところから始めようと思いましたが
上記URLにモノを落とすようなURLがない。
他にこのページでやることがないようなので
MyBot → createでBot アプリケーションを作成してみる。

![Art240.jpg](https://qiita-image-store.s3.amazonaws.com/0/151893/832a9e3a-89d6-db2d-9041-a10a02fe6e9f.jpeg)

Azureにサインインします。
![Art238.jpg](https://qiita-image-store.s3.amazonaws.com/0/151893/cc210f2e-affc-9474-1700-51bb11d220ce.jpeg)

そして結局portalへ飛ばされます。
![Art239.jpg](https://qiita-image-store.s3.amazonaws.com/0/151893/4243d948-c0dc-7856-a31a-84d2135d1a0f.jpeg)


今回はfunctionsでなくweb appを選択。
![Art241.jpg](https://qiita-image-store.s3.amazonaws.com/0/151893/34d347c9-eef3-ad73-2570-9a05b7d6bc63.jpeg)

app serviceが作成されます。
設定→メッセージングエンドポイントで受け付けるURLを設定
![Art244.jpg](https://qiita-image-store.s3.amazonaws.com/0/151893/e97ef449-9f3c-8428-0aca-d447c3403146.jpeg)

今回は
https://xxxxxxxxxxx.azurewebsites.net/api/messages
としました。

サンプルのソースコードを入手します。
[Microsoft/BotBuilder-Samples](https://github.com/Microsoft/BotBuilder-Samples/tree/master/samples/javascript_typescript/02.a.echobot)

### 必要なモジュールのインストール
入手したpackage.jsonを以下のように編集しnpm installを実行

package.json

```json
{
  "name": "natural-bot",
  "version": "1.0.0",
  "description": "",
  "main": "./dist/index.js",

  "dependencies": {
    "@types/micro": "^7.3.3",
    "botbuilder": "^4.2.0",
    "botframework-config": "^4.2.0",
    "config": "^1.31.0",
    "dotenv": "^6.1.0",
    "js-yaml": "^3.8.4",
    "micro": "^9.3.3",
    "micro-http-router": "^1.3.0"
  },
  "devDependencies": {
    "@types/dotenv": "6.1.0",
    "@types/micro": "7.3.3",
    "nodemon": "^1.18.6",
    "tslint": "^5.11.0",
    "typescript": "^3.1.6"
  }
}


```


### ソースを編集してデプロイ
サンプルはrestifyで実装されているためmicroに入れ替えます。
tsconfig.json,echobot.botは変更点なしでそのまま流用します。
今回は取り合えず動けば良しとします（編集中）

#### index.ts
microを導入します。ルーティングは[micro-http-router](https://github.com/protocol114/micro-http-router)を利用して以下のRouter.tsにて実装しました。

```ts

const micro = require('micro')
const server = micro(async (req:any, res:any) => {
        // リクエスト受付（各ルーティング実行）
        let router:any
        let Router = require('./gateway/Router')
        router = await Router(req,res)
        return await router.handle(req, res);
})
server.listen(process.env.port || process.env.PORT || 3978, () => {
    console.log(`\n${server.name} listening to ${server.url}`);
    console.log(`\nGet Bot Framework Emulator: https://aka.ms/botframework-emulator`);
    console.log(`\nTo talk to your bot, open echobot.bot file in the Emulator.`);
});


```
#### gateway/Router.ts

```ts

const Router = require('micro-http-router')
import { config } from 'dotenv';
import * as path from 'path';
import { BotConfiguration, IEndpointService } from 'botframework-config';
import { BotFrameworkAdapter } from 'botbuilder';
import { MyBot } from '../bot';

const ENV_FILE = path.join(__dirname, '../..', '.env');
config({ path: ENV_FILE });
const BOT_CONFIGURATION = (process.env.NODE_ENV || DEV_ENVIRONMENT);

module.exports = async (req:any, res:any) => {

    const router = new Router()
    // bot設定ファイルの読み込（サンプルではプロジェクトルートのechobot.botが読み込まれます。）
    // パスは上記.envファイル内で指定 
    const BOT_FILE = path.join(__dirname, '../..', (process.env.botFilePath || ''));
    let botConfig;
    try {
        botConfig = BotConfiguration.loadSync(BOT_FILE, process.env.botFileSecret);
        const endpointConfig = botConfig.findServiceByNameOrId(BOT_CONFIGURATION) as IEndpointService;
        const adapter = new BotFrameworkAdapter({
            appId: endpointConfig.appId || process.env.microsoftAppID,
            appPassword: endpointConfig.appPassword || process.env.microsoftAppPassword,
        });

        // on error
        adapter.onTurnError = async (context, error) => {
            console.error(`\n [onTurnError]: ${ error }`);
            await context.sendActivity(`Oops. Something went wrong!`);
        };

        // app service の『設定』で指定したエンドポイント
        router.post('/api/messages', (req:any, res:any) => {
            adapter.processActivity(req, res, async (context) => {
                // Route to main dialog.
                let myBot = new MyBot();
                await myBot.onTurn(context);
            });
        });
    } catch (err) {
        console.error(`\nError reading bot file. Please ensure you have valid botFilePath and botFileSecret set for your environment.`);
        console.error(`\n - The botFileSecret is available under appsettings for your Azure Bot Service bot.`);
        console.error(`\n - If you are running this bot locally, consider adding a .env file with botFilePath and botFileSecret.\n\n`);
        process.exit();
    }
    return router;
};

```

#### bot.ts
適当な返事をしてくれるメソッドを用意。util.jsは以下を使用。
[eustia - A Tool for Generating JavaScript Utility Libraries:](https://eustia.liriliri.io/module.html)

turnContext.activity.textが受信されたテキストメッセージを保持、
turnContext.sendActivity()でテキストメッセージの送信が可能です。


```ts

import {
    ActivityTypes,
    TurnContext
} from 'botbuilder';
const natural = require('natural');
let util = require("./util/Util.js");

export class MyBot {
    /**
     * Use onTurn to handle an incoming activity
     *
     * @param {TurnContext} context on turn context object.
     */
    async onTurn(turnContext: TurnContext) {
        if (turnContext.activity.type === ActivityTypes.Message) {
            let message = turnContext.activity.text
            // 結果の返却
            await turnContext.sendActivity(`${  this.greet(message)}`);
        } else {
            Generic handler for all other activity types.
            await turnContext.sendActivity(`[${ turnContext.activity.type } event detected]`);
        }
    }

    greet(text:string):string{

        const goodMorning = "おはようございます、"
        const hello = "こんにちは、"
        const goodEveinning = "こんばんは、"

        if(util.isStr(text)){
            if(text.indexOf("おはよう") > -1){
                return goodMorning
            }else if(text.indexOf("こんにちは") > -1){
                return hello
            }else if(text.indexOf("こんちは") > -1){
                return hello
            }else if(text.indexOf("こんばんは") > -1){            
                return goodEveinning
            }
        }
        return ""
    }
}

```

あとはgit等でデプロイします。
デフォルトでsdk等が大量に用意されていて若干戸惑いますが、デプロイは通常のApp Serviceと同様になります。