見出し画像

素人が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に格納しています。

※pathモジュール
Node.jsに組み込まれている標準モジュール。
ファイルパスを扱う際に必要になります。

・3行目はPORTを指定して定数に格納している記述です。この記事がわかりやすかったです。process.envはNode.jsの環境変数を入れているオブジェクトのようです。

※PORT
http://127.0.0.1:1234/みたいなやつの1234とかを指定するやつ。
ポート(port)とは、オペレーティングシステムがデータ通信を行うためのエンドポイントである。

※環境変数
環境変数とはコンピュータさんが持っている、値を入れておく箱(変数)。もう少し具体的に書くと
コンピュータの人格に相当するソフト(OS)が動いているときに使える変数であり
すべてのプログラムで一緒に使える「みんなで仲良く使ってね~」な変数です。
https://wa3.i-3-i.info/word11027.html

・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は同じ意味です。

※ルーティング
ルーティング とは、アプリケーションが特定のエンドポイントに対するクライアント要求に応答する方法として、URI (またはパス) と特定の HTTP 要求メソッド (GET、POST など) を決定することです。
各ルートには、1 つ以上のハンドラー関数があり、それらはルートが一致したときに実行されます。

ルート定義では、次の構造を使用します。
app.METHOD(PATH, HANDLER)

各部分の意味は次のとおりです。
・app は、express のインスタンスです。
・METHOD は、HTTP 要求メソッド です。
・PATH は、サーバー上のパスです。
・HANDLER は、ルートが一致したときに実行される関数です。

このチュートリアルでは、app という名前の express のインスタンスが作成されていて、サーバーが稼働中であることを想定しています。アプリケーションの作成と開始に慣れていない場合は、Hello World の例 を参照してください。

※インスタンス
インスタンスとは、英語において「事実」「実例」「実態」といった意味を示す名詞である。 プログラミングの分野では、オブジェクト指向言語における具体的な(クラスが生成する)オブジェクトを指す語として用いられる。

■自分なりのまとめを入れていくと、

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'))

※app.get("/", function(req, res){……略……});
これは、HTTPのGETによるアクセスの登録です。第1引数にはアクセスしたパスを、第2引数には実行する関数をそれぞれ指定します。これで、第1引数のパスにアクセスがあったとき、第2引数の関数を呼び出し実行するようになります。

第2引数のコールバック関数では、requestとresponseのオブジェクトがそれぞれ引数として渡されます。これは、既にお馴染みのリクエストとレスポンスのオブジェクトが渡されます。

このgetメソッドで必要なだけアクセス先のパスに関数を登録していけば、いくつでもページを追加していけるわけです。面倒なルーティング処理などは必要なくなるのです。

https://www.tuyano.com/index3?id=1147003&page=5

※ルート
C:\Users\sample\ みたいなフォルダ階層の中にindex.jsがあるとした時に、sampleというフォルダ階層がルートと呼ばれる。一番上の階層ってことですね。'\'は言うなればどの階層にも行かないよっていう意味ですね。

何をするのか?の記述が、(req, res) => res.render('pages/index'))です。

これはアロー関数( =>という記号を使って簡単に無名関数を作れるやつ)を使って、res.renderというビュー・テンプレート(後で出てくるejsなどのテンプレートエンジンと呼ばれるレンダリングされる対象のファイル)をレンダリングするメソッドを関数として呼び出しています。

※レンダリングはここではejsというファイルをユーザーに見える形に書き出している作業だと思えば大丈夫だと思います。

このメソッドで呼び出しているのが、pagesというフォルダの中にあるindex.ejsというファイルです。
(具体的な階層で言うと、node-js-getting-started\views\pages\index.ejsになります。viewsというフォルダがこういったビュー・テンプレートを入れておく場所っぽいです。)

ejsはテンプレートエンジンと呼ばれるもので、これを使うとhtmlのなかで変数やfor文を使えるようになります。テンプレートエンジンは他にjade,ectなど複数あり、好みに応じて変更可能です。
例えば、ユーザーの情報によって、表示したい内容を変えたい場合がある。そういうときは、変えたい部分のみを変更して変数などを記述したhtmlファイルをこのviewsフォルダにぶち込めば良い。
ユーザーからのリクエストがあったときに、ユーザーの情報とこのテンプレートファイルを合体させて、htmlファイルをその場で作ってユーザーに送り返すことができる。

上記の行でindexとだけ書いて、拡張子を書いていないのは、1つ上の行でsetというメソッドを使って、viewの拡張子にはejsを使いますよっていうのを環境変数に設定しているからです。

 .set('view engine', 'ejs')

※view engine
テンプレートエンジンの種類を示す。具体的には第二引数で’ejs’をテンプレートエンジンに使用してねという司令になる。

※setメソッド
アプリケーションを必要とする各種の設定情報をセットするためのメソッド。
.set(name, value)という感じでnameにvalueをセットしてねって感じです。
例えば、あるディレクトリをカレントディレクトリにしたりとか、拡張子の省略を行えるようにするなど。

また、ここは同じくsetメソッドを使って、viewsディレクトリのパスを設定しています。

 .set('views', path.join(__dirname, 'views'))

※path.join関数
返り値として第1引数以降のパスを順番に結合したパスが得られる。

※__dirname
現在実行中のソースコードが格納されているディレクトリパスが格納されている。
ex) ~/hoge/Foo.js/a.js → ~/hoge/Foo.js

http://cidermitaina.hatenablog.com/entry/2018/04/19/023433

ということなので、今のディレクトリの中のviewsというフォルダをviewsとして利用するから、後でビュー・テンプレートを取り出す時はここからやってね。という司令を出している感じですね。

次はこの行です。

 .use(express.static(path.join(__dirname, 'public')))

useメソッドはミドルウェア関数を使用できるようにするメソッドです。

app.use( [パス]ミドルウェア関数

という感じで記述するのですが、[パス]は省略可能です。
省略すると、クライアントからのリクエストがあったときに、すべてのリクエストでミドルウェア関数が必ず実行されます。
[パス]に'/about'などが記述されていると、'/about'のリクエストが合った際に実行されるミドルウェア関数を設定できる。
※注意点として、app.useで設定した順でミドルウェア関数が実行される。

express.staticはクライアントにアクセスさせたい静的ファイルが格納されているフォルダを設定しています。

publicにはcssなどが格納されていました。読み込んどいて欲しい静的ファイルはここに全部入れとけばいいって感じですね。先程のejsは動的ファイルなのでここではないってことかもです。

画像1

次はこの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を叩いた時にどういう動作をするのか?ということでした。

ちなみに、POSTやGETはHTTPというプロトコルのメソッドらしいです。

GET
・HTTP通信で、サーバから情報を取得してくる時に使用する
・他人に見られたくない情報は、GETでは送らない(後述する)
・送信できるデータ量に制限がある
・ブックマークに保存する場合
・テキストデータのみ送信できる(バイナリデータは送信できない)

POST
・HTTP通信で、サーバへ情報を登録する時に使用する(データベースへの格納など)
・データ量が多い場合(GETでのデータ送信量制限を超えてしまう場合)
・バイナリデータを送信したい場合
・他の人に見られたくない情報を送る場合(パスワードなど)

https://qiita.com/Sekky0905/items/dff3d0da059d6f5bfabf

この2行はこの記事の最後に入れたものです。

画像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というリクエストがあった時にそれぞれ文字列を返すようなもののようです。

画像3

次はこの行。

 .listen(PORT, () => console.log(`Listening on ${ PORT }`))

listenというメソッドは、指定したPATHをサーバーが読み込んで待機しといてねっていう感じみたいです。その後に付いているのは、このポートを読み込んでますよっていうのをconsoleというログを出す所に出しといてねという処理です。多分何回か出てきているのでイメージつくと思います。

この後の行と、飛ばした2段落目と4段落目以降はLINEBOTの機能を作っている部分なので、分量も多くなったので分解して明日の記事に書きます。

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