[Cogbot]Microsoft Translator API で Facebook Bot を作ってみた

Cogbot勉強会に参加させていただいたので、復習を兼ねて、自分なりにbotを作ってみました。

# 0. 開発環境

* Windows10
* Node.js (v8.6.0)
* Microsoft Translator API
* Microsoft Bot Framework

# 1. 勉強会資料

[駅すぱあと x Cognitive Services で 画像DE路線当てBOT を作ろう！ [Custom Vision 編]](https://qiita.com/hmaruyama/items/fd28cdee19a55fb510b1)
[駅すぱあと x Cognitive Services で 画像DE路線当てBOT を作ろう！ [BOT実装(Node.js ver) 編]](https://qiita.com/hmaruyama/items/8db9d869e63408c6c444)

# 2. Microsoft Translator API の利用開始

## 2.1. 登録

https://portal.azure.com/#　で 「＋新規」押下

![translate01.png](https://qiita-image-store.s3.amazonaws.com/0/184714/e2303b10-d95e-d465-4a23-429f22e5fe2f.png)

検索文字列に「translator」を入力して、「Translator Text API」を選択

![translate02.png](https://qiita-image-store.s3.amazonaws.com/0/184714/611eb972-b039-a65c-d842-e8cc18efe8f8.png)

「作成」押下

![translate02-2.png](https://qiita-image-store.s3.amazonaws.com/0/184714/187f0771-5421-4e94-547e-166ed7e26b39.png)


各入力項目を設定して「作成」押下

* Name …　API名称
* サブスプリクション　…　作成先（今回は無料）
* 価格レベル　…　利用価格帯（今回はFree）
* Resource Group　…　よく分かんないけど、API名称を小文字にして、新規作成
* Resource Group location …　東日本 or 西日本

![translate03.png](https://qiita-image-store.s3.amazonaws.com/0/184714/8b64ec58-827a-8622-bbb4-55851e8ddbb4.png)

できました！

![translate04.png](https://qiita-image-store.s3.amazonaws.com/0/184714/c82ab3a7-7a2f-bba3-092a-f72decc1e080.png)

## 2.2. 疎通確認

### 2.2.1. アクセストークン取得

参考）[Authentication Token API](https://docs.microsofttranslator.com/oauth-token.html)

「Show access keys ...」押下

![translate05.png](https://qiita-image-store.s3.amazonaws.com/0/184714/8c015192-3ce8-83ea-690a-249ac4f32d6e.png)

「KEY1」の値をコピー

![translate06.png](https://qiita-image-store.s3.amazonaws.com/0/184714/2e234c99-978d-7a68-5337-d663f3f860f3.png)

curl コマンドで、アクセストークン取得

```
curl --data "" "https://api.cognitive.microsoft.com/sts/v1.0/issueToken?Subscription-Key=<KEY1>"
```

### 2.2.2. 翻訳テスト

参考）[Microsoft Translator Text API](http://docs.microsofttranslator.com/text-translate.html#!/default/get_Translate)

ブラウザのURLに下記を指定する

```
https://api.microsofttranslator.com/V2/Http.svc/Translate?appid=Bearer <アクセストークン>&text=おはよう&to=en
```
![translate07.png](https://qiita-image-store.s3.amazonaws.com/0/184714/54576ea3-2a5e-943b-dffc-334ffed91203.png)

翻訳されました！

# 3. Bot開発①

## 3.1. 開発環境

[勉強会資料](https://qiita.com/hmaruyama/items/8db9d869e63408c6c444#%E9%96%8B%E7%99%BA%E7%92%B0%E5%A2%83)参照

## 3.2. ひな形作成

[勉強会資料](https://qiita.com/hmaruyama/items/8db9d869e63408c6c444#%E9%96%8B%E7%99%BA%E7%92%B0%E5%A2%83)参照

### 初期化

```
D:\<path>\source\master>npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
package name: (master) hello-azure-bot
version: (1.0.0) 0.0.1
description:
entry point: (index.js) app.js
test command:
git repository:
keywords:
author:
license: (ISC)
About to write to D:\<path>\source\master\package.json:

{
  "name": "hello-azure-bot",
  "version": "0.0.1",
  "description": "",
  "main": "app.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}


Is this ok? (yes) y

D:\<path>\source\master>
```

### botフレームワークをインストール

```
D:\<path>\source\master>npm install --save botbuilder restify

> dtrace-provider@0.8.5 install D:\<path>\source\master\node_modules\dtrace-provider
> node scripts/install.js

npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN hello-azure-bot@0.0.1 No description
npm WARN hello-azure-bot@0.0.1 No repository field.

+ botbuilder@3.10.1
+ restify@6.0.1
added 138 packages in 16.012s
```

### app.jsを作成

[勉強会資料](https://qiita.com/hmaruyama/items/8db9d869e63408c6c444#%E9%96%8B%E7%99%BA%E7%92%B0%E5%A2%83)参照

### エミュレータ起動

```node app.js```で、nodejsを起動して、エミュレータで動作を確認します。

![capture000970.jpg](https://qiita-image-store.s3.amazonaws.com/0/184714/4bd31595-fab5-2431-99af-41b5c35d8aa5.jpeg)

オウム返しができました！

## 3.3. git

ソースコードの管理とリリースは、gitを介して行います。
ひな形の時点で、一旦githubに登録してしまいます。

```
D:\<path>\source\master>git init

D:\<path>\source\master>git add .

D:\<path>\source\master>git commit -m "initial"

D:\<path>\source\master>git remote add origin https://github.com/<gitアカウント>/hello-azure-bot.git

D:\<path>\source\master>git push origin master
Counting objects: 3774, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (3550/3550), done.
Writing objects: 100% (3774/3774), 4.13 MiB | 211.00 KiB/s, done.
Total 3774 (delta 861), reused 0 (delta 0)
remote: Resolving deltas: 100% (861/861), done.
To https://github.com/<gitアカウント>/hello-azure-bot.git
 * [new branch]      master -> master

D:\<path>\source\master>
 ```

登録できました！

# 4. Bot開発②

「Microsoft Translator API」を使った形に変更していきます。

参考）

* [勉強会資料](https://qiita.com/hmaruyama/items/8db9d869e63408c6c444#custom-vision-%E3%81%AE%E5%91%BC%E3%81%B3%E5%87%BA%E3%81%97)
* [BOT FRAMEWORKとTRANSLATOR TEXT APIを使って簡単な翻訳BOTを作ってみる ② 機能追加編](https://azure-recipe.kc-cloud.jp/2017/09/cognitive-service-translator-text-02/)

## 4.1. モジュール追加インストール

```
D:\<path>\source\master>npm install --save dotenv request
npm WARN hello-azure-bot@0.0.1 No description
npm WARN hello-azure-bot@0.0.1 No repository field.

+ dotenv@4.0.0
+ request@2.82.0
added 1 package and updated 1 package in 2.578s
```

## 4.2. ソースコード
```app.js
var restify = require('restify');
var builder = require('botbuilder');

require('dotenv').config();
var request = require('request');

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

var connector = new builder.ChatConnector({
    appId: process.env.MICROSOFT_APP_ID,
    appPassword: process.env.MICROSOFT_APP_PASSWORD
});

server.post('/api/messages', connector.listen());

// アクセストークン取得処理
function getToken(callback){
    
    //アクセストークン設定
    var options = {
        url: process.env.ACCESSTOKEN_URI,
        headers: {
            'Ocp-Apim-Subscription-Key': process.env.TRANSLATOR_TEXT_KEY
        },
        method: 'POST'
    };
 
    //アクセストークンリクエスト送信
    request(options, function (error, response, body) {
        if(!error && response.statusCode == 200) {
            console.log("accesstoken="+ body);
            
            // トークンをコールバック
            callback(body);
        } else {
            console.error("response.statusCode: " + response.statusCode);
            console.error("error: " + error);
            
            // コールバック(nullでエラー判定させる)
            callback(null);
        }
    });
};

var bot = new builder.UniversalBot(connector, function (session) {
    // 10文字以下のテキストが送付されているか
    // APIの制限があるので、文字数制限を行っておく
    if(session.message.text.length <= 10) {
        
        translate(session.message.text, 'en', function(trans_text){
            // 翻訳後の文字列を返す
            // もしくはエラーメッセージを返す
            session.send("%s", trans_text);
        });
        
    } else {
        session.send("10文字以内で話しかけてください");
    }
});

// 翻訳処理
function translate(text, language, callback){

    getToken(function(token){
        if (!token) {
            // トークンが取れなかったら、認証失敗
            callback("認証に失敗しました。もう一度話しかけてください。");
            return;
        }
        
        //リクエストヘッダ
        var headers = {
            'Authorization': 'Bearer ' + token
        };
        //クエリ
        var query = {
            'to': language,
            'text': text
        };
        //オプションの定義
        var options = {
            url: process.env.TRANSLATOR_TEXT_API_URI,
            qs: query,
            method: 'GET',
            headers: headers,
            json: true
        };
 
        //リクエスト送信
        request(options, function (error, response, body) {
            if(!error && response.statusCode == 200) {

                //タグの除去
                body = body.replace(/<(.+?)>|<\/string>/g, '')
                
                // コールバック
                callback(body);
            } else {
                console.error("response.statusCode: " + response.statusCode);
                console.error("error: " + error);
                
                // コールバック
                callback("失敗しました。もう一度話しかけてください。");
            }
            
        });
    });
};
 
```

.envファイル

```
ACCESSTOKEN_URI=https://api.cognitive.microsoft.com/sts/v1.0/issueToken
TRANSLATOR_TEXT_KEY=<Translator API の KEY1>
TRANSLATOR_TEXT_API_URI=https://api.microsofttranslator.com/V2/Http.svc/Translate
```

![capture000972.jpg](https://qiita-image-store.s3.amazonaws.com/0/184714/551aa240-b54b-9b76-ce05-a5ade09103b3.jpeg)

英語に翻訳したオウム返しができました！

# 5. git commit

翻訳版をgitにコミットします。

```
git add .
git commit -m "翻訳オウム返し機能追加"
git push origin master
```

# 6. botサービス公開

パブリックサービスとして公開していきます。

参考）[Microsoft Bot Framework、Azureを利用したBot開発！Bot作成からWeb上で公開するまで](http://hiiiiiiihikaru.hatenadiary.com/entry/2017/09/01/132921)

## 6.1. Web App として登録

「＋新規」から「Web App」を選択

![translate08.png](https://qiita-image-store.s3.amazonaws.com/0/184714/5541a1a9-8f0c-b492-9218-b9f7392c5b7c.png)

アプリ名を入力して作成
（リソースグループは勝手にアプリ名と同じのが入りました）

![translate09.png](https://qiita-image-store.s3.amazonaws.com/0/184714/f7afde69-9dcf-654e-b33b-de220dc0183d.png)

「デプロイオプション」＞「ソースの選択」＞「GitHub」選択

![translate10.png](https://qiita-image-store.s3.amazonaws.com/0/184714/f9b4552c-bce6-d845-5a8e-3c58d13f858e.png)

「承認」でアクセス許可を承認
GitHubにログイン済みの場合、そのアカウントでの接続が求められます。
別のアカウントで接続したい場合、一旦GitHubをログアウトしてから接続してください。

![translate11.png](https://qiita-image-store.s3.amazonaws.com/0/184714/13761cda-4bdb-8519-83dc-d96e0ab555d4.png)

「プロジェクトの選択」で、コミットしたbotプロジェクトを選択

![translate12.png](https://qiita-image-store.s3.amazonaws.com/0/184714/36252ceb-7071-ae23-41f7-c4a413e7ad87.png)

ブランチは切ってないので、そのまま「master」を選択
パフォーマンステストはそのままで構いません。

![translate13.png](https://qiita-image-store.s3.amazonaws.com/0/184714/1a715f87-7712-3dc8-bd60-dd5eb8f7f2f6.png)

翻訳オウム返しがサーバーを介して返ってきました！

## 6.2. Microsoft Bot Framework への登録

[Microsoft Bot Framework](https://dev.botframework.com/)＞「My bots」＞「Create a bot」を選択

![translate14.png](https://qiita-image-store.s3.amazonaws.com/0/184714/6dc8eb4b-3d6d-b60b-5ec5-7aeb0f006d96.png)

※botがまだ1件も作成されていない場合、この画面になりません。次の画面が表示されます。

Registerのラジオボタンを選択して、「OK」

![translate16.png](https://qiita-image-store.s3.amazonaws.com/0/184714/d93423b3-4472-9d33-b52a-841d30e64d8b.png)

* Display name …　表示名（変更可）
* Bot handle　…　エンドポイントとして使用される名前（変更不可）
* Long description　…　説明
* Messaging endpoint　… **https**から始まるAzure Web App のURL（エミュレータで使用していたURLのhttps版）

「Create Microsoft App ID and password」を押下
※別窓が開きます

![translate17.png](https://qiita-image-store.s3.amazonaws.com/0/184714/4ce2f630-1d4f-01e4-1213-cc7ec27f315e.png)

「アプリパスワードを生成して続行」を押下
**ここで表示されたパスワードは二度と表示されないので、どこかにメモっといてください**

「終了してボットのフレームワークに戻る」押下
※別窓閉じます

自動的にAppIDが入っているので、そのまま登録。
（今回はAnalyticsは設定しません）

## 6.3. Azure Web App と Bot Framework の結合

AzureのWeb App ＞　アプリケーション設定　＞　アプリ設定　に、先ほど取得したIDとパスワードを登録します。

![translate19.png](https://qiita-image-store.s3.amazonaws.com/0/184714/a8f6142e-f99d-e066-863c-059af3ee4ee3.png)

## 6.4. Bot Framework のテスト

![translate20.png](https://qiita-image-store.s3.amazonaws.com/0/184714/615bdaf6-b6a8-cebf-608d-f158d51b2202.png)

Bot Framework に戻って、右上の「Test」で、エミュレータと同じように疎通テストが出来ます。無事翻訳されて返ってきました。

# 7. Facebook bot への登録

[Connect a bot to Facebook Messenger](https://docs.microsoft.com/en-us/bot-framework/channel-connect-facebook)

力尽きました…

bot登録したら、テスターを登録して、テスターにページURLを送って、メッセンジャーで話しかけてもらえれば、レスポンスが返るようになります。

# 8. 感想

botのデバッグとリリース方法がずっと分かんなくて、今回ようやくそれが理解できて、ここまで作る事ができました。
勉強会に参加して本当に良かったです。ありがとうございました！
手順が分かってきたので、結構色んな事ができそうで、ワクワクしています。
後は、Azureの無料プランでどこまでいけるか、とか、運用周りどうするか、とかですかねー。