見出し画像

素人が2020年までの1ヶ月でLINE BOTに挑戦する毎日note. 【Day 17:開発環境を整える part 3】

こんにちは!
12月1日から2019年残り1ヶ月でスケジュール調整BOTの開発に挑戦している"くろ"です。

BOTの概要:Day 2の記事

BOTの仕様:機能一覧やフローチャート Day 10の記事

今日は、開発環境を整えるpart 3、BOTサーバの開発からスタートです。

今日で下記の記事内容は終わらせます。

では早速見ていきます。

ボットサーバとして使うサーバは、Node.jsとHerokuの準備で準備したHerokuのインスタンスを使用します。
これをボットサーバとして機能するように修正していきます。
必要なことは、LINE Developersからチャネルの開設してChannel Secretとアクセストークンを取得
Heroku上のREST APIをLINE BOTのWebhookに反応できるように修正
LINE DevelopersでREST APIのURLをWebhookとして設定
です。Webhookとは、LINEプラットフォームからボットサーバへ送信されるリクエストとそれに対するレスポンスをさします。ボットサーバのURLをWebhook URLとしてLINE Developersで設定することで、接続が可能となります。

LINE Developersからチャネルの開設してChannel Secretとアクセストークンを取得。

手順にしたがって、下記サイトから取得しておきました。

次は、Heroku上のREST APIをLINE BOTのWebhookに反応できるように修正です。

手順に従ってやってみると。

図1

いきなりエラー。

どうやらexpressが無いって言われているようです。

確かにexpressまだこれには入れてなかったので、入れてみます。

図2

いけました!!

Expressここで入れられて逆に良かったです。

そして、ここまでできました!

画像3

LINE DevelopersでWebhookを設定

できました!モザイクの中はherokuアプリケーションのURLです。

画像4

受信する内容

だんだんサイトに飛んで戻ってくるのが読者的にめんどくさそうだなとおもってきたので、引用してきます。

図3

ここは読み物ですね。

メッセージが送られてきたり、友だち追加されるとwebhookとして上記のような配列と呼ばれる形のテキストが送られてきて、それには型があるので、そこをいい感じに抑えれば返信とか色々できるよってことだと思います。

返信する

画像6

Herokuの環境変数にトークン情報をセットする

画像7

環境変数の設定はクリア!

画像8

シークレットキーはdevelopersのここにあるやつですね。

画像9

チャネルアクセストークンも同じくdevelopersのここです。

画像10

次は署名を検証する

画像11

line-bot-sdkは問題なく入りました!

図4

上段部分のconfig云々の所はこれですね。飛んできたwebhookが対象のBOTからのものなのか?を認証してくれるようです。

画像13

ちゃんとエラーメッセージが返ってくる所までできました!

画像14

※ちなみに、

また、heroku logsを実行すると、コンソールにpassが表示されていることが分かるはずです。

この記述の所がエラーになってのですが

» Error: Missing required flag:
» -a, --app APP app to run command against
» See more help with --help

こういうやつ。

heroku open --app herokuアプリケーション名ってやればいけました。

ただ、毎回やるのめんどくさすぎる、、、

さっと200番を返す

画像15

できた

応答メッセージを送る

画像16

const express = require("express");
const path = require("path");
const PORT = process.env.PORT || 5000;
const line = require("@line/bot-sdk");
const config = {
 channelAccessToken: process.env.ACCESS_TOKEN,
 channelSecret: process.env.SECRET_KEY
};
const client = new line.Client(config); // 追加

express()
 .use(express.static(path.join(__dirname, "public")))
 .set("views", path.join(__dirname, "views"))
 .set("view engine", "ejs")
 .get("/", (req, res) => res.render("pages/index"))
 .get("/g/", (req, res) => res.json({ method: "こんにちは、getさん" }))
 .post("/p/", (req, res) => res.json({ method: "こんにちは、postさん" }))
 .post("/hook/", line.middleware(config), (req, res) => lineBot(req, res))
 .listen(PORT, () => console.log(`Listening on ${PORT}`));

function lineBot(req, res) {
 res.status(200).end();
 // ここから追加
 const events = req.body.events;
 const promises = [];
 for (let i = 0, l = events.length; i < l; i++) {
   const ev = events[i];
   promises.push(
     echoman(ev)
   );
 }
 Promise.all(promises).then(console.log("pass"));
}

// 追加
async function echoman(ev) {
 const pro =  await client.getProfile(ev.source.userId);
 return client.replyMessage(ev.replyToken, {
   type: "text",
   text: `${pro.displayName}さん、今「${ev.message.text}」って言いました?`
 })
}
解説していきます。大まかな流れとしては、
受信したbodyからeventを解析
client.getProfile()を使ってユーザー名を取得
client.replyMessage()を使って返事を送信
一通り終わったらログだけ出しておく
です。
受信したbodyからeventを解析
まずreq.bodyですが、すでにline.middlewareがパースしてくれているので普通に使えます。ありがたい。逆に、自前でbody-parser使ったりしてると注意が必要です。以下公式のmiddleware Usageより抜粋です。

You do not need to use body-parser to parse webhook events, as middleware() embeds body-parser and parses them to objects. Please keep in mind that it will not process requests without X-Line-Signature header. If you have a reason to use body-parser for other routes, please do not use it before the LINE middleware. body-parser parses the request body up and the LINE middleware cannot parse it afterwards.

bodyの内容は受信する内容で述べた通りです。
ここではclient.getProfile()で使うevent.source.userIdと、client.replyMessage()で使うevent.message.textを使います。
client.getProfile()を使ってユーザー名を取得
ユーザー情報を取得します。
LINEプラットフォームから非同期通信でデータを取得しています。その情報を後続の処理で利用するので、async/awaitを使って同期化します。
index.js(echoman()より抜粋)

 const pro =  await client.getProfile(ev.source.userId);
client.replyMessage()を使って返事を送信
ここでメッセージを返します。
先ほど取得したユーザー情報から名前と、ユーザーが送信したtextを返信bodyに詰め込んで返信を送ります。
client.replyMessageは非同期通信で、Promiseを返します。これをechoman()の呼び出し元で取得して、完了後の処理を行います。
index.js(echoman()より抜粋)

return client.replyMessage(ev.replyToken, {
   type: "text",
   text: `${pro.displayName}さん、今「${ev.message.text}」って言いました?`
 })
一通り終わったらログだけ出しておく
最後に、処理の完了だけ検知してログを出しておきます。これはついでなので、別にやる必要はないですが。
echoman()で返却されるPromiseを配列に格納して、全部の完了をPromise.all()で検知し、then()の中でconsole.log()を呼び出してログを表示します。
Promise周りの仕様を確認したい方は、NDMをご参照ください。
さて、長くなりましたがここで本記事で作るものは完成しました。
index.js

 const events = req.body.events;
 const promises = [];
 for (let i = 0, l = events.length; i < l; i++) {
   const ev = events[i];
   promises.push(
     echoman(ev)
   );
 }
 Promise.all(promises).then(console.log("pass"));

全部やってみます。

画像17

できました!!

これで、下記記事の内容は終わりです!お世話になりました。

明日以降は下記3つを進めていきます。

・DBスキーマの定義の続き。

・Expressやnode.jsの勉強を本格的に。

・BOTアプリケーションだけでなく、WEBアプリケーション(LIFF)も必要になるので、そこのチュートリアル的なものをしていく。

以上。

いつも読んでいただいている方ありがとうございます!!


この記事が参加している募集

よろしければサポートお願いします! 頂いたサポートはクリエイター活動に活用させて頂きます。