素人が2020年までの1ヶ月でLINE BOTに挑戦する毎日note. 【Day 23:ここまでに作ったサンプルコードの解説 part 1】
こんにちは!
12月1日から2019年残り1ヶ月でスケジュール調整BOTの開発に挑戦している"くろ"です。
BOTの概要:Day 2の記事
BOTの仕様:機能一覧やフローチャート Day 10の記事
昨日はJavascriptとnode.js(+Express)のインプットをしつつ、Day17で作ったサンプルコード(herokuにデプロイしているやつ)を解読していました。
今日はサンプルコードを解読した内容を書いていきます。
サンプルコードはこちらです。
index.jsというファイルですね。
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)) // 変更、middlewareを追加
.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}」って言いました?`
})
}
まず最初の段落。
const express = require('express')
const path = require('path')
const PORT = process.env.PORT || 5000
const line = require("@line/bot-sdk");
ここはconst XX = YYってなっているので、定数を宣言しているやつですね。
宣言やら定数やらはJavascriptの本に書いてあったのですが、このサイトを辞書代わりに使ってもらうと楽かもしれません。ctrl+Fで単語検索できるし
・1行目から解説していくと、ここはrequire('express')でexpressフレームワークを呼び出して使えるようにしている所です。
・2行目は、同じくpathというモジュールを呼び出してpathに格納しています。
・3行目はPORTを指定して定数に格納している記述です。この記事がわかりやすかったです。process.envはNode.jsの環境変数を入れているオブジェクトのようです。
・4行目はLINEのSDKを呼び出してlineという定数に格納しています。
LINEのSDKについては、Day17とかに書いているはずです。
次は段落1つ飛ばして3段落目です。
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)) // 変更、middlewareを追加
.listen(PORT, () => console.log(`Listening on ${ PORT }`))
この記事にこの辺の話が結構書いてます!
※記事の中ではappにexpressのインスタンスを格納している形で書いているので、記事の中のapp.getとサンプルコードのexpress().getは同じ意味です。
■自分なりのまとめを入れていくと、
expressオブジェクトの中にget, set, use, post, listenといったメソッドが格納されていてそれを使っている感じです。
express()
.METHOD(PATH, HANDLER)
っていう感じで、METHODがgetやuseとかで、PATHはサーバ上のパス、HANDLER はルートが一致したときに実行される関数です。
下記の所は、getというメソッドを使っていて、PATHの'/'はルートという意味です。ルートで何を返すのか?がget('/', HANDLER)の意味で、ホームページで何をするのか?に近いと思います。
.get('/', (req, res) => res.render('pages/index'))
何をするのか?の記述が、(req, res) => res.render('pages/index'))です。
これはアロー関数( =>という記号を使って簡単に無名関数を作れるやつ)を使って、res.renderというビュー・テンプレート(後で出てくるejsなどのテンプレートエンジンと呼ばれるレンダリングされる対象のファイル)をレンダリングするメソッドを関数として呼び出しています。
このメソッドで呼び出しているのが、pagesというフォルダの中にあるindex.ejsというファイルです。
(具体的な階層で言うと、node-js-getting-started\views\pages\index.ejsになります。viewsというフォルダがこういったビュー・テンプレートを入れておく場所っぽいです。)
上記の行でindexとだけ書いて、拡張子を書いていないのは、1つ上の行でsetというメソッドを使って、viewの拡張子にはejsを使いますよっていうのを環境変数に設定しているからです。
.set('view engine', 'ejs')
また、ここは同じくsetメソッドを使って、viewsディレクトリのパスを設定しています。
.set('views', path.join(__dirname, 'views'))
ということなので、今のディレクトリの中のviewsというフォルダをviewsとして利用するから、後でビュー・テンプレートを取り出す時はここからやってね。という司令を出している感じですね。
次はこの行です。
.use(express.static(path.join(__dirname, 'public')))
useメソッドはミドルウェア関数を使用できるようにするメソッドです。
app.use( [パス]ミドルウェア関数
という感じで記述するのですが、[パス]は省略可能です。
省略すると、クライアントからのリクエストがあったときに、すべてのリクエストでミドルウェア関数が必ず実行されます。
[パス]に'/about'などが記述されていると、'/about'のリクエストが合った際に実行されるミドルウェア関数を設定できる。
※注意点として、app.useで設定した順でミドルウェア関数が実行される。
express.staticはクライアントにアクセスさせたい静的ファイルが格納されているフォルダを設定しています。
publicにはcssなどが格納されていました。読み込んどいて欲しい静的ファイルはここに全部入れとけばいいって感じですね。先程のejsは動的ファイルなのでここではないってことかもです。
次はこの2行
.get('/g/', (req, res) => res.json({method: "こんにちは、getさん"})) // 追加
.post('/p/', (req, res) => res.json({method: "こんにちは、postさん"})) // 追加
1つ目はgetメソッドなので、GET形式で、/g/というパスを要求したときに何を返すのか?です。
2つ目はpostメソッドなので、POST形式で、/p/というパスを要求したときに何を返すのか?です。
つまり、http://hogehoge.com/gとかhttp://hogehoge.com/p
っていうURLを叩いた時にどういう動作をするのか?ということでした。
この2行はこの記事の最後に入れたものです。
@コマンドプロンプト
$ chcp 65001
// コンソールがクリーンされる
$ curl -H "Content-Type: application/json" https://<heroku createのとき出てきたアプリ名>.herokuapp.com/g/
{"method":"こんにちは、getさん"}
$ curl -X POST -H "Content-Type: application/json" https://<heroku createのとき出てきたアプリ名>.herokuapp.com/p/
{"method":"こんにちは、postさん"}
これcurlっていうもので確かめていますが、ブラウザでも確認できました。
処理としてはどちらもpがgというリクエストがあった時にそれぞれ文字列を返すようなもののようです。
次はこの行。
.listen(PORT, () => console.log(`Listening on ${ PORT }`))
listenというメソッドは、指定したPATHをサーバーが読み込んで待機しといてねっていう感じみたいです。その後に付いているのは、このポートを読み込んでますよっていうのをconsoleというログを出す所に出しといてねという処理です。多分何回か出てきているのでイメージつくと思います。
この後の行と、飛ばした2段落目と4段落目以降はLINEBOTの機能を作っている部分なので、分量も多くなったので分解して明日の記事に書きます。
よろしければサポートお願いします! 頂いたサポートはクリエイター活動に活用させて頂きます。