Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

この記事は、LINE WORKSでBotを開発してみたい!けど、今までBotを開発したことがない方向けに、手順を紹介しています。
前編は、こちらからご覧ください!

LINE WORKSで初めてのBot開発!(前編)

Botを開発する

Bot用プログラミング

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

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のダッシュボードから設定することができます。手順はこちらを参照してください。
今回は、下記5つの値を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登録タイプ)の[認証キー再発行]ボタンからダウンロードします。

ダウンロードしたファイルに表示されている英数字
BOTNO [Bot]メニューに表示されているBotをクリックし、
[Bot No.]に表示されている4桁の数字

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

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

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 部分に含まれて通知されます。サンプルは下記となります。(メッセージのプロパティについて、詳細はこちらを参照してください。)

{
  "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の詳細な仕様はこちらを参照してください。
実装サンプルコードは下記となります。

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から1人のユーザーに対しメッセージを送信する場合は、上記のようになります。
トークルームに対しメッセージを送信する場合は、下記のように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

③Botが追加されました!
image.png
④追加されたBotを選択し詳細ページにて、[使用権限]と[公開設定]の設定をします。
今回Botは誰でも利用できるように設定するので、[使用権限] は[すべて]に設定し、 [使用権限]の[公開]にチェックを入れます。
⑤設定が終わったら、[保存]をクリックします。

Botに話しかけてみましょう!

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

最後に

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

tokotan
LINE WORKS→Box。 Qiitaの記事は個人の見解であり、所属する組織の公式見解ではありません。 趣味は、電子工作です。夫婦で「SHK」と言う名称でMakerFaireTokyoに出展したりしました。 http://makezine.jp/event/makers2018/m0265/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away