AizuHack LINEBot勉強会 Vol.1
資料一覧
- LINEBotとは
- AizuHack LINEBot勉強会 Vol.1(本記事)
- AizuHack LINEBot勉強会 Vol.2
- AizuHack LINEBot勉強会 Vol.3
- AizuHack LINEBot勉強会 Vol.4
はじめに
こんにちは、会津大学学部二年のしんぶんぶんです。
本記事はAizuHackのLINEBot勉強会 Vol.1の資料になります。
本記事のサンプルコードは個人利用で一般的に使われるLINE Messaging APIの機能をだいたいカバーしており、サンプルコードを書き換えるだけでLINE Messaging APIのだいたいの機能が使えるようになっています。
LINEBotの入門記事やサンプルはたくさんありますが、その多くがオウム返しbotを作るだけやngrokで動かすだけの拡張性が低いものになっています。
AWSなどにデプロイする記事などもありますが、初心者には若干ハードルが高くとっつきにくいかと思います。
今回の資料では、初心者でも理解できるということを念頭におきつつ、例えば、Vercelでデプロイして永続化する、ソースコードはできるだけ改造しやすいように書くなど、できるだけ拡張性が高いように作成しました。
コピペだけで簡単に機能拡張できるようなサンプルコードになっています。
【追記】
最初からクラウドにデプロイすると手間がかかるため、最初はngrokで試して後からGCPのCloud Runへデプロイする形に変更しました。
GCPへのデプロイ手順はAizuHack LINEBot勉強会 Vol.4に掲載しています。
また、本記事に掲載している解説の多くは公式リファレンスを参考に作成しています。
このリファレンスは非常にわかりやすいため、作っていて何かわからないことがあったらリファレンスを参考にしましょう。
できるようになること
- ngrokを使ってLINEBotを動かすことができる
- 一般的に使われるイベントの種類を理解できる
- LINE Messaging APIで返信できるメッセージの種類が理解できる
サンプルコードについて
本サンプルコードで試せることは以下の通りです
- メッセージイベント
- テキスト
- 画像
- 動画
- 音声
- ファイル
- 位置情報
- スタンプ
- 送信取消イベント
- フォローイベント
- フォロー解除イベント
- 参加イベント
- 退出イベント
- メンバー参加イベント
- メンバー退出イベント
- ポストバックイベント
LINEBotについて
LINEBotについてはこちらのGoogle Slidesで解説しています。
事前準備
この記事を読む前に以下の事前準備を行ってください。
- JavaScriptについて勉強する
-
LINE Developersのアカウントを作成して、プロバイダを1つ作成する
- 普段使っているLINEアカウントでログインしてください
- ngrokのアカウントを作成する
- (ローカルで開発したい人は)ローカルにNode.jsの環境を導入する
簡単な流れ
- LINEBotのアカウントを作成する(Messaging APIのチャネルを作成する)
- GitHubのリポジトリからソースコードをcloneして、モジュールをインストールする
- LINE DevelopersからLINE Botの設定をする
- ngrokで試してみる
- 完成!
作ってみよう!
初回準備
-
LINE DevelopersにログインしてMessaging APIのチャネルを作成する
- チャネル名とかは適当にいれる(LINEという単語が入っていると作成できないので注意)
- GitHubリポジトリからコードをcloneする
- ソースコードがあるリポジトリに移動して
npm i
を実行する(この先の手順でもterminalでコマンドを実行する操作がいくつか出てきますが、全てソースコードのリポジトリで実行してください) -
LINE Developersに移動して先ほど作成したチャネルを開き、以下の設定をする
- "Messaging API設定"→"LINE公式アカウント機能"から"グループ・複数人チャットへの参加を許可する"を有効化、"応答メッセージ"を無効化、"あいさつメッセージ"を無効化する
- チャネルアクセストークン(長期)を発行し、コピーしてどこかにメモしておく
- "チャネル基本設定"→"基本情報"からチャネルシークレットを発行し、コピーしてどこかにメモしておく
- ソースコードがあるリポジトリに
.env
というファイルを作成して、以下のコードを記述する
channelSecret=先ほどコピーしたチャネルシークレット
channelAccessToken=先ほどコピーしたチャネルアクセストークン
6.ngrokにログインする
7. 左側のメニューにあるYour Authtoken
を選択する
8. Tokenが表示されるのでコピーする。
9. terminalで以下のコマンドを入力する
npx ngrok authtoken 先ほどコピーしたngrokのtoken
以上で初回の準備は完了です!
上記の手順はサーバ初回起動時のみ必要となるため、2回目以降サーバを起動したい場合は以下のサーバ起動の手順を行えば大丈夫です。
サーバ起動
-
npm start
を実行する(これでNode.jsのサーバが3000番ポートで起動されます) - terminalをもう1つ開いて、
npx ngrok http 3000
を実行する(これでローカルPCの3000番ポートが外部に公開されます) -
Forwarding
という項目が2つありそれぞれURLが記述されているので、https
から始まる方をコピーする -
LINE Developersに移動して先ほど作成したチャネルを開き、以下の設定をする
- "Messaging API設定"→"Webhook URL"に、先ほどコピーしたURLの後ろに
/webhook
を追加したものを貼り付ける - "Webhookの利用"をオンにする(2回目以降この手順は必要ありません)
- "Messaging API設定"→"Webhook URL"に、先ほどコピーしたURLの後ろに
- 完成!!!
動かしてみよう
- LINE Developersに移動して先ほど作成したチャネルを開き、"Messaging API設定"→"ボット情報"→"QRコード"に掲載されているQRコードを読み取って友達追加する
- 挨拶メッセージが返ってきたら設定成功です!
※サーバのログは先ほどnpm start
を実行したterminalに表示されるので、メッセージを送ったらどんなログが表示されるか確認してみましょう
コード解説
eventの種類
LINE Messaging APIのWebhookには様々なイベントがあります。
今回のサンプルコードで使用しているイベントを以下で簡単に解説します。
※途中で挿入しているイベント例は公式リファレンスから引用したものになります
共通プロパティ
すべてのイベントに共通するプロパティについて解説します。イベントは以下のような形になっています。
{
"destination": "xxxxxxxxxx",
"events": [
{
"replyToken": "0f3779fba3b349968c5d07db31eab56f",
"type": "message",
"mode": "active",
"timestamp": 1462629479859,
"source": {
"type": "user",
"userId": "U4af4980629..."
},
・・・
}
]
}
- destination
- botのuserId
- userIdとはLINEのアカウントを識別するIDで、ユーザー✖️プロバイダで一意になっています
- つまり、プロバイダが変わると同じユーザーでもuserIdが変わります
- events
- Webhookイベントが入っています
- replyToken
- イベントへ応答する際に使用するトークンです
- これが含まれているイベントには返信できます
- type
- イベントのタイプです
- イベントの種類についてはeventの種類で解説しています
- mode
- チャネルの状態です
- botがアクティブかどうかの情報が入っています
- timestamp
- イベントが発生した時刻のタイムスタンプです
- source
- イベントの送信元情報が入っています
-
type
にどこから送られてきたかが入っています(user
やgroup
など) - 送信元のuserIdやgroupIdも含まれています
- replyToken
- Webhookイベントが入っています
メッセージイベント
- メッセージが送られてきた時に発生するイベントです
- メッセージには以下の種類があります
- テキスト
- イメージ
- ビデオ
- オーディオ
- ファイル
- 位置情報
- スタンプ
それぞれのメッセージのjsonはリファレンスで確認できます。
送信取消イベント
メッセージが送信取り消しされた時に発生するイベントです。
中身は以下のような形になっています。
{
"destination": "xxxxxxxxxx",
"events": [
{
"type": "unsend",
"mode": "active",
"timestamp": 1462629479859,
"source": {
"type": "group",
"groupId": "Ca56f94637c...",
"userId": "U4af4980629..."
},
"unsend": {
"messageId": "325708"
}
}
]
}
- unsend.messageIdに送信取り消しされたmessageIdが格納されています。
- replyTokenが含まれていないため、このイベントに対して返信することはできません
フォローイベント
Botのアカウントが友達追加されるかブロック解除された時に飛んでくるイベントです。
イベントの中身は以下のような形になっています。
{
"destination": "xxxxxxxxxx",
"events": [
{
"replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
"type": "follow",
"mode": "active",
"timestamp": 1462629479859,
"source": {
"type": "user",
"userId": "U4af4980629..."
}
}
]
}
- このイベントにはreplyTokenが含まれているため返信できます
フォロー解除イベント
Botがブロックされた時に飛んでくるイベントです
{
"destination": "xxxxxxxxxx",
"events": [
{
"type": "unfollow",
"mode": "active",
"timestamp": 1462629479859,
"source": {
"type": "user",
"userId": "U4af4980629..."
}
}
]
}
- このイベントにはリプライトークンがないため返信はできません
参加イベント
- botがグループまたはトークルームへ参加した時に飛んでくるイベントです
{
"destination": "xxxxxxxxxx",
"events": [
{
"replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
"type": "join",
"mode": "active",
"timestamp": 1462629479859,
"source": {
"type": "group",
"groupId": "C4af4980629..."
}
}
]
}
- このイベントにはreplyTokenが含まれているため返信できます
退出イベント
- botがトークルームやグループから退出したさいにとんでくるイベントです
{
"destination": "xxxxxxxxxx",
"events": [
{
"type": "leave",
"mode": "active",
"timestamp": 1462629479859,
"source": {
"type": "group",
"groupId": "C4af4980629..."
}
}
]
}
- このイベントにはreplyTokenが含まれていないため返信はできません
メンバー参加イベント
Botが参加しているグループにメンバーが参加した時にとんでくるイベントです
{
"destination": "xxxxxxxxxx",
"events": [
{
"replyToken": "0f3779fba3b349968c5d07db31eabf65",
"type": "memberJoined",
"mode": "active",
"timestamp": 1462629479859,
"source": {
"type": "group",
"groupId": "C4af4980629..."
},
"joined": {
"members": [
{
"type": "user",
"userId": "U4af4980629..."
},
{
"type": "user",
"userId": "U91eeaf62d9..."
}
]
}
}
]
}
- 参加したユーザーの情報が配列形式で
joined.members
に格納されています - このイベントにはreplyTokenが含まれているため返信できます
メンバー退出イベント
- Botが参加しているトークルームやグループからメンバーが退出した時にとんでくるイベントです
{
"destination": "xxxxxxxxxx",
"events": [
{
"type": "memberLeft",
"mode": "active",
"timestamp": 1462629479960,
"source": {
"type": "group",
"groupId": "C4af4980629..."
},
"left": {
"members": [
{
"type": "user",
"userId": "U4af4980629..."
},
{
"type": "user",
"userId": "U91eeaf62d9..."
}
]
}
}
]
}
-
left.members
に配列形式で退出したメンバーのリストが格納されています - このイベントにはreplyTokenが含まれていないため返信はできません
ポストバックイベント
- ポストバックとはトーク上に表示されない、「見えないメッセージ」のようなものです
- トーク上には何も表示されませんが、サーバにデータは飛んできます
{
"destination": "xxxxxxxxxx",
"events": [
{
"replyToken": "b60d432864f44d079f6d8efe86cf404b",
"type": "postback",
"mode": "active",
"source": {
"userId": "U91eeaf62d...",
"type": "user"
},
"timestamp": 1513669370317,
"postback": {
"data": "storeId=12345",
"params": {
"datetime": "2017-12-25T01:00"
}
}
}
]
}
-
postback.data
にdataが格納されています - 日時選択アクションの場合は
postback.params
に日時データが含まれています
メッセージオブジェクトについて
LINE Messaging APIで返信できるメッセージには以下の種類があります
クイックリプライ
クイックリプライは以下の画像のようなメッセージです。
次のメッセージがトークに送信されると選択肢が消えます。
テキストメッセージ
スタンプメッセージ
画像メッセージ
動画メッセージ
音声メッセージ
位置情報メッセージ
イメージマップメッセージ
1つの画像の中に複数のタップ領域を設定できるタイプのメッセージです
ボタンテンプレート
画像、タイトル、テキスト、ボタンで構成されたメッセージです
確認テンプレート
テキストに加えて、2つのボタンが平行に表示されます
カルーセルテンプレート
複数のカラムを横にスクロールする形で表示できます。
画像カルーセルテンプレート
カルーセルの全面画像版です
Flex Message
CSS Flexible Boxの記法をベースに作れるメッセージで、とても自由度が高いです。
Flex Messageに関しては第二回勉強会で解説します。
ディレクトリ構成
本サンプルコードのディレクトリ構成は以下のようになっています
- /
- api/
- server.js
- event/
- follow.js
- join.js
- leave.js
- memberJoined.js
- memberLeft.js
- postback.js
- unfollow.js
- unsend.js
- message.js
- message
- audio.js
- file.js
- image.js
- location.js
- sticker.js
- text.js
- video.js
- bot.js
- config.js
- vercel.json
実行の流れ
LINEBotで何かイベントが発生した際、今回のサンプルコードでは以下の流れで処理をします。
- たとえば「こんにちは」というメッセージを送った場合
ユーザーがメッセージを送信する → LINEのサーバに送られる → LINEのサーバからngrokの/webhook
にリクエストが飛んでくる → 自分のPCにたてたNode.jsサーバにリクエストが飛んでくる → server.jsが呼び出される → server.jsがbot.jsを呼び出す → bot.jsがmessage.jsを呼び出す → message.jsがtext.jsを呼び出す → text.jsが「Hello, World」というメッセージをmessage.jsに返す → message.jsが返信するメッセージをbot.jsに返す → bot.js返信するメッセージをLINEのサーバに送る → LINEのサーバからユーザーにメッセージが送られる
各ファイルのコード解説
/api/server.js
サーバを起動するコードが書いてあるファイルです。/
にGETのアクセスがきたらDeploy succeeded
とレスポンスし、/webhook
にPOSTのアクセスがきたら/bot.jsのindexを呼び出してその実行結果をレスポンスするというコードになっています。
/bot.js
LINEから送られてきたリクエストを処理するコードが書いてあるファイルです。/event
にそれぞれのイベントを処理するファイルが格納されているため、イベントに合わせてそれぞれのファイルを呼び出す処理が書かれています。
/event/follow.js
Botのアカウントが友達追加された時に呼び出されるファイルです。
友達追加ありがとうございます!
と返信する処理が書いてあります。
/event/join.js
Botがトークルームやグループへ招待された時に呼び出されるファイルです。
招待ありがと!!
と返信する処理が書いてあります。
/event/leave.js
Botがトークルームやグループから退出した時に呼び出されるファイルです。
botがグループから退出しました
とログに出力する処理が書いてあります。
/event/memberJoined.js
Botが入っているグループやトークルームにユーザーが参加した時に呼び出されるファイルです。
ユーザーが参加しました! 参加したユーザー: userId
と返信される処理が書かれています。
/event/memberLeft.js
Botが入っているグループやトークルームからユーザーが退出した時に呼び出されるファイルです。
ユーザーが退出しました... 退出したユーザー: userId
と返信される処理が書かれています。
/event/message.js
メッセージが送られてきた時に呼び出されるファイルです。
テキストが送られてきた場合は/event/message/text.js
, イメージは/event/message/image.js
, ビデオは/event/message/video.js
, オーディオは/event/message/audio.js
, ファイルは/event/message/file.js
, 位置情報は/event/message/location.js
, スタンプは/event/message/sticker.js
がそれぞれ呼び出されます。
/event/message/text.js
では送られてきたメッセージで条件分岐して返信するメッセージを決める処理が書かれています。/event/message/image.js
, /event/message/video.js
,/event/message/audio.js
, /event/message/file.js
では/download
ディレクトリにファイルを保存したのち、メッセージを返信する処理が書かれています。
/event/message/location.js
では受け取った住所を返信する処理、/event/message/sticker.js
には受け取ったスタンプのIDを返す処理が書かれています(特定のスタンプを受け取った時のみあるスタンプを返信する処理も書かれています)。
返信に対応しているメッセージは以下の通りです
以下のメッセージを送信して何が返ってくるか試してみましょう!
- テキスト
- こんにちは
- 複数メッセージ
- クイックリプライ
- スタンプメッセージ
- 画像メッセージ
- 位置情報メッセージ
- イメージマップメッセージ
- ボタンテンプレート
- 確認テンプレート
- カルーセルテンプレート
- 画像カルーセルテンプレート
- Flex Message
- プロフィール
- ここはどこ
- 個チャ、グループ、トークルームで返信されるメッセージが変わります
- イメージ
- ビデオ
- オーディオ
- ファイル
- 位置情報
- スタンプ
/event/postback.js
ポストバックイベントが飛んできた時に呼び出されるイベントです。
- ポストバックイベントとは
ポストバックイベントとは、表に表示されないメッセージのようなものです。トーク画面には表示されませんが、サーバへデータが飛んできます。
テンプレートメッセージなどでポストバックアクションが実装されているボタンを押すとポストバックイベントが飛んできます。
/event/unfollow.js
botがunfollow(ブロック)された時に飛んでくるイベントです。
unfollowされました...\nuserId: ${userId}
とログに表示される処理が書かれています。
/event/unsend.js
メッセージが送信取り消しされた時に飛んでくるイベントです。
メッセージが取り消されました!\n取り消されたmessageId: ${messageId}
と返信される処理が書かれています
config.js
アクセストークンやチャネルシークレットなどのコンフィグ情報を読むこむ処理が書かれています。
アクションオブジェクトについて
テンプレートメッセージのボタンなどを押したときに実行される操作を、アクションオブジェクトというものを用いて記述することができます。
詳しくはこちらのリファレンスに記載されています。
ソースコードを更新する方法
nodemonを使ってホットリロードできるようになっているので、変更を加えたらファイルを保存するだけでサーバが勝手に再起動されて自動で適用されます。
PCをスリープ/シャットダウンしたりVSCodeを閉じるとNode.jsのサーバ、ngrokのセッションが終了してしまうため、その場合はサーバ起動の手順を確認しながらサーバ起動、ngrokの実行、WebhookURLの更新を行ってください。
演習課題1
「こんにちは」と送信したときに返ってくるメッセージを「こんにちは世界」に変えてみましょう。
演習課題2
「おはよう」と送信したら「Good Morning!」と返信するコードを追加してみましょう。
演習課題3
自分のQ2の時間割を確認するbotを作ってみましょう。要件は以下のとおりです。
- 「時間割」と送信すると、曜日を選択するボタンが出てくる(クイックリプライを使用すると良さげ)
- ボタンを押すと、その曜日の時間割が送られてくる
演習課題4(宿題)
勉強会の評価アンケートをLINEBotで実装してみましょう(アンケート形式だけを実装すれば良くて、アンケート結果を保存する必要はありません)。
選択肢の実装はボタンテンプレートやクイックリプライを使うと良いかもしれません。
自由記述の実装方法はいくつか考えられますが、思いつかなかったら選択肢の項目を実装するだけでも大丈夫です。
演習課題5(宿題)
今回の勉強会で、LINEBotにできることはだいたいわかったのではないでしょうか。次回までに自分が作ってみたいLINEBotをいくつか考えてみましょう(次回ちょこっと発表してもらうかもです)。
おわりに
皆さんお疲れ様でした。このソースコードを全て理解する必要はないので、まずは「どこをいじったらどう動くのか」を知るためにたくさんコードをいじくりまわしてみましょう。
第2回はLINE Bot Designerの使い方、Flex Messageの作り方、リッチメニューの作り方、PUSHメッセージについて解説する予定です。
第二回はこちら: AizuHack LINEBot勉強会 Vol.2