LINE WORKSで初めてのBot開発！(後編)

この記事は、LINE WORKSでBotを開発してみたい！けど、今までBotを開発したことがない方向けに、手順を紹介しています。
前編は、こちらからご覧ください！
> **[LINE WORKSで初めてのBot開発！(前編)](https://qiita.com/tokotan/items/f615f4a62219d655436f)**

#Botを開発する
##Bot用プログラミング
先ほど作成したindex.jsを書き換えて、ユーザーが送ったメッセージを返信するBotのプログラムを書いていきます。
ここでは、５つの要素に分けて説明したいと思います。

i.追加モジュールのインストール
下記モジュールを追加でインストールします。

```
$ npm install body-parser --save
$ npm install jsonwebtoken --save
$ npm install https --save
$ npm install request --save
$ npm install querystring --save
```

index.jsにて、モジュールをインポートしておきます。

```index.js
//モジュールインポート
const bodyParser = require("body-parser");
const jwt = require('jsonwebtoken');
const https = require("https");
const request = require("request");
const qs = require("querystring");
```


ii.環境変数設定
Server TokenやConsumer Keyは、Herokuの環境変数へ設定します。
Herokuの環境変数への設定はHerokuのダッシュボードから設定することができます。手順は[こちら](https://devcenter.heroku.com/articles/config-vars#using-the-heroku-dashboard)を参照してください。
今回は、下記５つの値をHerokuの環境変数として設定しましょう。

|環境変数名|Developer Consoleの表示箇所|
|--------|-------------------------|
|APIID|[API]メニューのAPI IDに表示されている英数字|
|CONSUMERKEY|[API]メニューのServer API Consumer KeyのKey欄に表示されている英数字|
|SERVERID|[API]メニューのServer List(ID登録タイプ)のID欄に表示されている英数字|
|PRIVATEKEY|[API]メニューのServer List(ID登録タイプ)の[認証キー再発行]ボタンからダウンロードします。<br> ダウンロードしたファイルに表示されている英数字
|BOTNO|[Bot]メニューに表示されているBotをクリックし、<br>[Bot No.]に表示されている４桁の数字|

全て設定するとこんな感じになります。

設定したら、プログラムから利用できるよう変数として定義しておきましょう。

```index.js
const APIID = process.env.APIID;
const SERVERID = process.env.SERVERID;
const CONSUMERKEY = process.env.CONSUMERKEY;
const PRIVATEKEY = process.env.PRIVATEKEY;
const BOTNO = process.env.BOTNO;
```

iii.ユーザーのメッセージを受け取る
メッセージはPOSTリクエストのbody 部分に含まれて通知されます。サンプルは下記となります。（メッセージのプロパティについて、詳細は[こちら](https://developers.worksmobile.com/jp/document/100500901?lang=ja)を参照してください。）

```
{
  "type": "message",
  "source": {
    "accountId": "admin@example.com",
    "roomId": "12345"
  },
  "createdTime": 1470902041851,
  "content": {
    "type": "text",
    "text": "hello"
  }
}
```

このオブジェクトから、メッセージを送信する時に利用する値を、変数として格納しておきます。

```index.js
//POSTのパラメータをJSONで取得
server.use(bodyParser.json());

// Botからメッセージに応答
server.post('/callback', (req, res) => {
    res.sendStatus(200);
    const message = req.body.content.text;
    const roomId = req.body.source.roomId;
    const accountId = req.body.source.accountId;

});
```

>**[注意]**
受信したメッセージが、本当にLINE WORKSのサーバーから送信されたメッセージであり、そのメッセージが改ざんされていないかどうか、を確認するようにしてください。
確認のプロセスは、こちらを参考にしてください。

iv.メッセージをユーザーへ送信するためのトークンを取得する
トークンは、下記の流れで取得します。

1.JWTを取得
2.JWTを使ってServer Tokenを取得

それぞれ実装のサンプルコードは下記となります。詳細な仕様についてはこちらを参照してください。

```index.js
    getJWT((jwttoken) => {
        getServerToken(jwttoken, (newtoken) => {
            //取得したnewtokenを使ってメッセージ送信
        });
    });

//1. JWTを取得 
function getJWT(callback){
    const iss = SERVER_ID;
    const iat = Math.floor(Date.now() / 1000);
    const exp = iat + (60 * 60);　//JWTの有効期間は1時間
    const cert = PRIVATEKEY;
    const token = [];
    const jwttoken = jwt.sign({"iss":iss, "iat":iat, "exp":exp}, cert, {algorithm:"RS256"}, (err, jwttoken) => {
        if (!err) {
            callback(jwttoken);
        } else {
            console.log(err);
        }
    });
}
//2.JWTを使ってServer Tokenを取得
function getServerToken(jwttoken, callback) {
    const postdata = {
        url: 'https://authapi.worksmobile.com/b/' + API_ID + '/server/token',
        headers : {
            'Content-Type' : 'application/x-www-form-urlencoded; charset=UTF-8',
        },
        form: {
            "grant_type" : encodeURIComponent("urn:ietf:params:oauth:grant-type:jwt-bearer"),
            "assertion" : jwttoken
        }
    };
    request.post(postdata, (error, response, body) => {
        if (error) {
            console.log(error);
            callback(error);
        } else {
            const jsonobj = JSON.parse(body);
            const AccessToken = jsonobj.access_token;
            callback(AccessToken);
        }
    });
}
```

>**[注意]**
Server Token（access_token）は24時間以内に利用されない場合には自動的に失効します。access_token発行時に、発行時間とaccess_tokenを保存し、Server APIを実行する際には有効期限を確認するようにしてください。有効期限内であれば保存したaccess_tokenを利用することができます。

v.メッセージを送信する
取得したServerTokenを使って、ユーザーへメッセージを送信します。
メッセージ送信APIの詳細な仕様は[こちら](https://developers.worksmobile.com/jp/document/100500801?lang=ja)を参照してください。
実装サンプルコードは下記となります。

```index.js
    getJWT((jwttoken) => {
        getServerToken(jwttoken, (newtoken) => {
            sendMessage(newtoken, accountId, message);
        });
    });
function sendMessage(token, accountId, message) {
    const postdata = {
        url: 'https://apis.worksmobile.com/' + API_ID + '/message/sendMessage/v2',
        headers : {
          'Content-Type' : 'application/json;charset=UTF-8',
          'consumerKey' : CONSUMERKEY,
          'Authorization' : "Bearer " + token
        },
        json: {
            "botNo" : Number(BOTNO),
            "accountId" : accountId,
            "content" : {
                "type" : "text",
                "text" : message
            }
        }
    };
    request.post(postdata, (error, response, body) => {
        if (error) {
          console.log(error);
        }
        console.log(body);
    });
}
```

>**　[注意]**
Botから１人のユーザーに対しメッセージを送信する場合は、上記のようになります。
トークルームに対しメッセージを送信する場合は、下記のようにaccountIdをnullにして、その代わりにroomIdを送信してください。

サンプルのJSONデータ

```
{
            "botNo" : Number(BOTNO),
            "accountId" : null,
            "roomId" : roomId,
            "content" : {
                "type" : "text",
                "text" : message
}
```

###最終的なサンプルコード
完成したindex.jsのサンプルコードは下記のようになります。

>［注意］
今回は、わかりやすいようにindex.jsに全てのコードを記述しています。必要に応じて、モジュール化を検討してください。

```index.js
"use strict";

// モジュールインポート
const express = require("express");
const server = express();
const bodyParser = require("body-parser");
const jwt = require('jsonwebtoken');
const https = require("https");
const request = require("request");
const qs = require("querystring");

//変数
const APIID = process.env.APIID;
const SERVERID = process.env.SERVERID;
const CONSUMERKEY = process.env.CONSUMERKEY;
const PRIVATEKEY = process.env.PRIVATEKEY;
const BOTNO = process.env.BOTNO;

server.use(bodyParser.json());

// Webアプリケーション起動
server.listen(process.env.PORT || 3000);

// サーバー起動確認
server.get('/', (req, res) => {
    res.send('Hello World!');
});

// Botからメッセージに応答
server.post('/callback', (req, res) => {
    res.sendStatus(200);

    const message = req.body.content.text;
    const roomId = req.body.source.roomId;
    const accountId = req.body.source.accountId;

    getJWT((jwttoken) => {
        getServerToken(jwttoken, (newtoken) => {
            sendMessage(newtoken, accountId, message);
        });
    });
});

//サーバーAPI用JWT取得
function getJWT(callback){
    const iss = SERVERID;
    const iat = Math.floor(Date.now() / 1000);
    const exp = iat + (60 * 60);　//JWTの有効期間は1時間
    const cert = PRIVATEKEY;
    const token = [];
    const jwttoken = jwt.sign({"iss":iss, "iat":iat, "exp":exp}, cert, {algorithm:"RS256"}, (err, jwttoken) => {
        if (!err) {
            callback(jwttoken);
        } else {
            console.log(err);
        }
    });
}

function getServerToken(jwttoken, callback) {
    const postdata = {
        url: 'https://authapi.worksmobile.com/b/' + APIID + '/server/token',
        headers : {
            'Content-Type' : 'application/x-www-form-urlencoded; charset=UTF-8',
        },
        form: {
            "grant_type" : encodeURIComponent("urn:ietf:params:oauth:grant-type:jwt-bearer"),
            "assertion" : jwttoken
        }
    };
    request.post(postdata, (error, response, body) => {
        if (error) {
            console.log(error);
            callback(error);
        } else {
            const jsonobj = JSON.parse(body);
            const AccessToken = jsonobj.access_token;
            callback(AccessToken);
        }
    });
}

function sendMessage(token, accountId, message) {
    const postdata = {
        url: 'https://apis.worksmobile.com/' + APIID + '/message/sendMessage/v2',
        headers : {
          'Content-Type' : 'application/json;charset=UTF-8',
          'consumerKey' : CONSUMERKEY,
          'Authorization' : "Bearer " + token
        },
        json: {
            "botNo" : Number(BOTNO),
            "accountId" : accountId,
            "content" : {
                "type" : "text",
                "text" : message
            }
        }
    };
    request.post(postdata, (error, response, body) => {
        if (error) {
          console.log(error);
        }
        console.log(body);
    });
}
```

##Herokuへデプロイ
プログラムがすべて用意できたら、Herokuへデプロイしましょう。
コマンドは、下記となります。

```
$ git add .
$ git commit –m ‘commit message’
$ git push heroku master
```

#Botを公開し利用する
##Bot利用設定
Admin Consoleにて、Developer Consoleで登録したBotの利用設定を行います。
①[サービス]の[Bot]メニューにて、[+追加]ボタンをクリックします。
②追加できるBotが表示されますので、選択して[追加]をクリックします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/135983/9eebf917-dd53-ce1e-68ba-c3aebca64e17.png)

③Botが追加されました！
![image.png](https://qiita-image-store.s3.amazonaws.com/0/135983/502fc1d1-d4a5-767f-6561-facb588383b6.png)
④追加されたBotを選択し詳細ページにて、[使用権限]と[公開設定]の設定をします。 今回Botは誰でも利用できるように設定するので、[使用権限] は[すべて]に設定し、 [使用権限]の[公開]にチェックを入れます。
⑤設定が終わったら、[保存]をクリックします。

##Botに話しかけてみましょう！
トークにて、Botを追加し話しかけると、同じメッセージの返信がきたら、成功です！

#最後に
最後まで読んでくださってありがとうございます。この記事は現時点での最新情報をもとに作っていますが、LINE WORKSのAPIが更新された場合は動かなくなる可能性もありますので、そういった場合はコメントでお知らせください。
またそれ以外にも、おかしなところがあればご指摘頂けると嬉しいです！（プログラム初心者なものでおかしなところあると思います。。。）