LINEボットを立ち上げるまで。LINEビーコンも。

Node.jsでLINEボット用のユーティリティクラスを作成したので、立ち上げるまでをまとめておきます。（時間がたつと忘れてしまうので）
ついでに、ESP32でLINEビーコンを作成し、LINEボットで連携します。

（とは言っても、今まで複数投稿してきた内容をまとめただけです。ベースとなった投稿は、本投稿の最後に示しておきました。）

以下のAWS Lambdaエミュレーション環境をベースにします。

poruruba/amplify_template
　https://github.com/poruruba/amplify_template

先に順番を示しておきます。

・LINEチャネルの作成
・Node.jsサーバ立ち上げ
・Node.jsサーバにエンドポイントの作成
・LINEボットWebhook設定
・友達登録
・メッセージ応答のカスタマイズ
・(必要に応じて)ESP32でLINEビーコンを作成

# LINEチャネルの作成

LINEデベロッパーコンソールから登録します。

LINEデベロッパーコンソール
https://developers.line.biz/console/

プロバイダを選択または新規作成したのち、チャネル設定の新規チャネルを選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261826/0807ec25-6a11-e304-3ad3-4010dc0ba14c.png)

チャネルの種類は、Messaging APIです。
 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261826/e4672a7b-eb00-6a38-9f53-a1e7eea5c7a6.png)

適当に入力し、利用規約に同意して、「作成」ボタンを押下します。
 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261826/80424313-cbbd-6981-cb6f-6fab06137a2f.png)

作成後に表示されるチャネル基本設定にあるチャネルシークレットとMessaging API設定にあるチャネルアクセストークン(長期)の発行ボタンで生成されるチャネルアクセストークン(長期)を覚えておきます。

また、Messaging API設定の応答メッセージは無効に変更しておきます。グループ・複数人チャットへの参加を許可するも有効にしておきます。

# Node.jsサーバ立ち上げ

前提
　Node.jsがインストールされていること

以下から、ZIPダウンロードします。

poruruba/express_template
　https://github.com/poruruba/express_template

以下の通りに展開し、セットアップします。

```
> unzip express_template-master.zip
> cd express_template-master
> mkdir cert
> touch .env
> npm install
> node app.js
```

HTTPSである必要があるため、上記の途中でcertにSSL証明書を格納します。ファイル名は、app.jpを参照してください。
特に何もしなければ、HTTPSがポート10443で立ち上がるかと思います。

# Node.jsサーバにエンドポイントの作成

LINEボット用のフォルダを作成します。

```
> mkdir api\controllers\linebot
```

上記フォルダに以下の2つのファイルを作成します。
・index.js
・swagger.yaml

LINEボットとして応答できるようにするためには、最低限以下の記載があればよいです。

```js:api/controllers/linebot/index.js
'use strict';

const config = {
  channelAccessToken: process.env.LINE_CHANNEL_ACCESS_TOKEN || '【LINEチャネルアクセストークン(長期)】',
  channelSecret: process.env.LINE_CHANNEL_SECRET || '【LINEチャネルシークレット】',
};

const HELPER_BASE = process.env.HELPER_BASE || '../../helpers/';
const LineUtils = require(HELPER_BASE + 'line-utils');
const line = require('@line/bot-sdk');
const app = new LineUtils(line, config);

app.message(async (event, client) =>{
  console.log(event);

  var message = { type: 'text', text: event.message.text + ' ですね' };
  return client.replyMessage(event.replyToken, message);
});

exports.fulfillment = app.lambda();
```

【LINEチャネルアクセストークン(長期)】と【LINEチャネルシークレット】のところに、先ほど覚えておいた値を付記しておきます。
LINEアプリからチャットメッセージを入力すると、``` app.message()```が呼び出されます。
event.message.textに、受信したチャットメッセージがありますので、とりあえず、単純に、チャットでエコーバックするようにしておきました。

```yaml:api/controllers/linebot/swagger.yaml
swagger: '2.0'
info:
  version: 'first version'
  title: Lambda Laboratory Server
  
paths:
  /linebot:
    post:
      x-handler: fulfillment
      parameters:
        - in: body
          name: body
          schema:
            $ref: "#/definitions/CommonRequest"
      responses:
        200:
          description: Success
          schema:
            $ref: "#/definitions/CommonResponse"
```

Node.jsサーバを再起動します。

```
> node app.js
```

細かな処理は、api/helpers/line-utils.js で処理させています。以下のnpmモジュールを使っています。

line/line-bot-sdk-nodejs
　https://github.com/line/line-bot-sdk-nodejs

# LINEボットのWebhook設定

LINEデベロッパーのMessaging API設定に、Webhook設定があります。
ここに、立ち上げたNode.jsサーバのURLを指定します。以下のような感じです。

　```https://【Node.jsサーバのホスト名】:10443/linebot```

これで、Webhookの利用をOKにして、検証ボタンを押下すると、「成功」と表示されるかと思います。

# 友達登録

Messaging API設定のQRコードに表示されているQRコードから、ご自身のLINEアプリでこのLINEボットを友達として登録しておきましょう。
チャットしてみましょう。エコーバックされてくるかと思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261826/9953e9c7-db19-f6bb-96f8-425fe35eef75.png)

※タイトルがちょっと違いますが気にしないでください。。。

# メッセージ応答のカスタマイズ

応答できるメッセージオブジェトの種類は以下に定義されています。

メッセージオブジェクト
　https://developers.line.biz/ja/reference/messaging-api/#message-objects

・テキストメッセージ
・スタンプメッセージ
・画像メッセージ
・動画メッセージ
・音声メッセージ
・位置情報メッセージ
・イメージマップメッセージ
・テンプレートメッセージ
・Flex Message

このうち、テンプレートメッセージとFlex Message以外は名前の示す通りの単機能のメッセージなのですが、テンプレートメッセージとFlex Messageは単機能を組み合わせて定義済みのテンプレートだったり、自由に定義可能なレイアウトのメッセージです。

今回は、上記を組み合わせて、定型レイアウトを作っておきました。

・SimpleResponse
・BasicCard
・List
・Carousel
・QuickReply

以下に利用する際のソースコードを示しておきます。

```js:api/controllers/linebot/index.js
'use strict';

const config = {
  channelAccessToken: process.env.LINE_CHANNEL_ACCESS_TOKEN || '【LINEチャネルアクセストークン(長期)】',
  channelSecret: process.env.LINE_CHANNEL_SECRET || '【LINEチャネルシークレット】',
};

const HELPER_BASE = process.env.HELPER_BASE || '../../helpers/';

const line = require('@line/bot-sdk');
const LineUtils = require(HELPER_BASE + 'line-utils');
const app = new LineUtils(line, config);

app.message(async (event, client) =>{
  console.log(event);

	if( event.message.text == '/' ){
		var message = app.createSimpleResponse(event.message.text);
		var list = [
			{
				title: "SimpleResponse",
				action: {
					type: "postback",
					data: "SimpleResponse"
				}
			},
			{
				title: "BasicCard",
				action: {
					type: "postback",
					data: "BasicCard"
				}
			},
			{
				title: "List",
				action: {
					type: "postback",
					data: "List"
				}
			},
			{
				title: "Carousel",
				action: {
					type: "postback",
					data: "Carousel"
				}
			},
		];
		var quickReply = app.createQuickReply(list);
		message.quickReply = quickReply;

		return client.replyMessage(event.replyToken, message);
	}else{
		var message = app.createSimpleResponse(event.message.text + ' ですね');
		return client.replyMessage(event.replyToken, message);
	}
});

app.postback(async (event, client) =>{
	console.log(event);

	if(event.postback.data == 'SimpleResponse' ){
		var message = app.createSimpleResponse('SimpleResponseです');
		return client.replyMessage(event.replyToken, message);
	}else
	if(event.postback.data == 'BasicCard' ){
		var message = app.createBasicCard("BasicCard", "linebot sample", "https://scdn.line-apps.com/n/channel_devcenter/img/fx/01_1_cafe.png", 
																			"BasicCardです", "ボタンテキスト", "ボタンを押下");
		return client.replyMessage(event.replyToken, message);
	}else
	if(event.postback.data == 'List' ){
		var list = [
			{
				title: "Brown Cafe",
				desc: "Brown Cafeです",
				image_url: "https://scdn.line-apps.com/n/channel_devcenter/img/flexsnapshot/clip/clip10.jpg",
				action_text: "選択",
				action: {
					type: "message",
					text: "Brown Cafeを選択"
				}
			},
			{
				title: "Brow&Cony's Restaurant",
				desc: "Brow&Cony's Restaurantです",
				image_url: "https://scdn.line-apps.com/n/channel_devcenter/img/flexsnapshot/clip/clip11.jpg",
				action_text: "選択",
				action: {
					type: "message",
					text: "Brow&Cony's Restaurantを選択"
				}
			},
			{
				title: "Tata",
				desc: "Tataです",
				image_url: "https://scdn.line-apps.com/n/channel_devcenter/img/flexsnapshot/clip/clip12.jpg",
				action_text: "選択",
				action: {
					type: "message",
					text: "Tataが選択されました"
				}
			},
		];
		var message = app.createList("List", list);
		return client.replyMessage(event.replyToken, message);
	}else
	if(event.postback.data == 'Carousel' ){
		var list = [
			{
				title: "Brown Cafe",
				desc: "Brown Cafeです",
				image_url: "https://scdn.line-apps.com/n/channel_devcenter/img/flexsnapshot/clip/clip10.jpg",
				action_text: "選択",
				action: {
					type: "message",
					text: "Brown Cafeを選択"
				}
			},
			{
				title: "Brow&Cony's Restaurant",
				desc: "Brow&Cony's Restaurantです",
				image_url: "https://scdn.line-apps.com/n/channel_devcenter/img/flexsnapshot/clip/clip11.jpg",
				action_text: "選択",
				action: {
					type: "message",
					text: "Brow&Cony's Restaurantを選択"
				}
			},
			{
				title: "Tata",
				desc: "Tataです",
				image_url: "https://scdn.line-apps.com/n/channel_devcenter/img/flexsnapshot/clip/clip12.jpg",
				action_text: "選択",
				action: {
					type: "message",
					text: "Tataが選択されました"
				}
			},
		];
		var message = app.createCarousel('Carousel', list);
		return client.replyMessage(event.replyToken, message);
	}

});

exports.fulfillment = app.lambda();
```

チャットメッセージで「/」(スラッシュ)を送ると、定型レイアウト名のクイックリプライが表示され、選択するとpostbackが返り、それに応じた定型レイアウトを表示する、というものです。

```app.postback()``` の部分がポストバックを受け取る部分です。

簡単に補足すると、以下の関数で、定型メッセージを作成し、

・SimpleResponse：app.createSimpleResponse()
・BasicCard：app.createBasicCard()
・List：app.createList()
・Carousel：app.createCarousel()

出来上がったmessageを、以下のように渡します。

```
return client.replyMessage(event.replyToken, message);
```

クイックリプライを追加する場合には、以下の関数でQuickReplyを作成し

・QuickReply：app.createQuickReply()

出来上がったquickReplyを前述のmessageに追加します。送信方法は同じです。
```
message.quickReply = quickReply;
```

SimpleResponseの例です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261826/f7568721-3f9d-2afd-e902-f6809c095bd9.png)

BasicCardの例です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261826/04d1cab2-7da5-29e0-fac7-ea432f0f4947.png)

Listの例です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261826/d791b8e1-68ad-fdfe-260a-c8c5d1707095.png)

Carouselの例です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261826/64801a21-4750-d814-df26-00af8da23839.png)

上記は、LINEアプリからのチャットの応答としてメッセージを返信しました。一方でこちらの好きなタイミングでメッセージを送りたい場合は、replyMessageではなくpushMessageを使います。送信の際には、messageに加えて、送信先を示すLINEユーザIDが必要です。

```
return app.client.pushMessage('【LINEユーザID】', message);
```

今回は、いくつかの定型メッセージを作成しましたが、他にもLINEメッセージの仕様を参考にしたり、以下のツールを利用すれば、簡単に作成できるかと思います。

Flex Message Simulator：Flex Messageを視覚的に確認しながらメッセージを作れます。
　https://developers.line.biz/flex-simulator/

LINE Bot Designer：単純なパラメータの指定でメッセージを作成できます。
　https://developers.line.biz/ja/services/bot-designer/

メッセージオブジェクト：LINEの仕様です。
　https://developers.line.biz/ja/reference/messaging-api/#message-objects

# ESP32でLINEビーコンを作成

次は、ESP32でLINEビーコンを作成します。

まず以下のページから、LINE Simple BeaconのハードウェアIDを発行します。

LINE Official Account Manager：LINE Beacon
　https://manager.line.biz/beacon/register
 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261826/047b217e-0056-b53f-dc6e-247619e5b5a2.png)

アカウントリストの中に、先ほど作成したMessaging APIのチャネルがあるかと思います。
それを選択し、その次の画面で表示される「ハードウェアIDを発行」ボタンを押下します。
そすると、16進数5桁のハードウェアIDが払い出されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261826/3522a3dc-0cf3-470a-305f-d4ce186e9b77.png)

Arduino IDEまたはPlatformIOのESP32のソースコードを以下に示します。

```c:LineBot.ino
#include <M5StickC.h>
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
#include <BLE2902.h>

const uint8_t LINE_BEACON_HWID[] = { 0x01, 0x02, 0x03, 0x04, 0x05 }; // LINE BEACON HWID
const uint8_t LINE_BEACON_DEVICE_MESSAGE[] = { 'h', 'e', 'l', 'l', 'o' }; // LINE BEACON DEVICE MESSAGE(1-13)

#define UUID_SERVICE_SHORT  0xFE6F
#define UUID_SERVICE BLEUUID((uint16_t)UUID_SERVICE_SHORT)

BLEAdvertising *pAdvertising;

void taskServer(void*) {
  BLEDevice::init("M5StickC");

  BLEServer *pServer = BLEDevice::createServer();

  BLEService *pService = pServer->createService(UUID_SERVICE);

  pService->start();

  pAdvertising = pServer->getAdvertising();
  pAdvertising->addServiceUUID(UUID_SERVICE);
  BLEAdvertisementData advertisementData = BLEAdvertisementData();
  advertisementData.setFlags(0x06);

  std::string strServiceData = "";

  strServiceData += (char)0x03;
  strServiceData += (char)0x03;
  strServiceData += (char)0x6f;
  strServiceData += (char)0xfe;

  strServiceData += (char)(10 + sizeof(LINE_BEACON_DEVICE_MESSAGE));
  strServiceData += (char)0x16;
  strServiceData += (char)(UUID_SERVICE_SHORT & 0xff);
  strServiceData += (char)((UUID_SERVICE_SHORT >> 8) & 0xff);
  strServiceData += (char)0x02; // LINE Simple Beacon FrameのFrame Type
  strServiceData += LINE_BEACON_HWID[0]; // LINE Simple Beacon FrameのHWID(5B)
  strServiceData += LINE_BEACON_HWID[1];
  strServiceData += LINE_BEACON_HWID[2];
  strServiceData += LINE_BEACON_HWID[3];
  strServiceData += LINE_BEACON_HWID[4];
  strServiceData += (char)0x7f; // LINE Simple Beacon FrameのMeasured TxPowe

  // LINE Simple Beacon FrameのDevice Message(1-13)
  int i = 0;
  for( i = 0 ; i < sizeof(LINE_BEACON_DEVICE_MESSAGE) ; i++ )
    strServiceData += LINE_BEACON_DEVICE_MESSAGE[i];

  advertisementData.addData(strServiceData);
  pAdvertising->setAdvertisementData(advertisementData);
  pAdvertising->start();

  vTaskDelay(portMAX_DELAY); //delay(portMAX_DELAY);
}

void setup() {
  M5.begin();

  Serial.begin(9600);
  
  M5.Lcd.fillScreen(BLACK);
  M5.Lcd.setRotation(3);
  M5.Lcd.setTextColor(WHITE);
  M5.Lcd.setCursor(0, 0);
  M5.Lcd.setTextSize(1);
  M5.Lcd.println("[LineBeacon]");
  Serial.println("LineBeacon");

  xTaskCreate(taskServer, "server", 40000, NULL, 5, NULL);
}

void loop() {
  M5.update();

  delay(1000);
}
```

以下の部分を環境に合わせて書き換えてください。

・LINE BEACON HWID：LINEビーコンのハードウェアID(5バイト)
・LINE_BEACON_DEVICE_MESSAGE：LINEビーコンのDevice Message(1～13バイト)

LINE BEACON HWIDは、先ほど払い出したハードウェアIDです。
LINE_BEACON_DEVICE_MESSAGEは、自由に定義でき、指定したバイト列が、LINEボットに通知されます。指定しない場合は、1バイトの0x00を指定します。

これで、LINEアプリがインストールされたスマホが、LINEビーコンを自動的に検知し、LINEボットに知らされます。受け取れるように、以下を追記しておきます。

```js:api/controllers/linebot/index.js
app.beacon( async (event, client) =>{
	console.log(event);

	var message = app.createSimpleResponse("ビーコン(" + event.beacon.dm + ")の受信圏内に入りました");
	return client.replyMessage(event.replyToken, message);
});
```

```app.beacon()``` の部分が、LINEビーコンを検出したことを受け取る部分です。

LINEアプリ側では、以下となっている前提です。
・LINEボットがお友達として登録されている
・LINEアプリの 設定 → プライバシー管理 → 情報の提供 → LINE Beacon のチェックボックスがOnになっている

# 最後に

ほかにも、LINEボットに関する投稿をしていますので、参考にしてください。

　[Dialogflowと連携してLINE Botを作る](https://qiita.com/poruruba/items/09563e251adef1e1e8af)
　[LINE Beaconを自宅に住まわせる](https://qiita.com/poruruba/items/a28476ad53b2232c789d)
　[TI SensorTagをブラウザから使う(2)：with LINE Things](https://qiita.com/poruruba/items/e95d9dd2b93ff875170f)
　[Actions on Google向けに作ったDialogflowボットをLINEボットにする](https://qiita.com/poruruba/items/4f20e8f5cf2b0a4e9ee6)
　[LINEボットでゲームブックを作ってみた](https://qiita.com/poruruba/items/28c1c4ba95c4eaf2b739)
　[LINEボットでtodoist連携](https://qiita.com/poruruba/items/7ea8cc5bcd025d9069d8)
　[LINEボットでGoogle Alertを通知](https://qiita.com/poruruba/items/ad5bb6fb0372771e9045)
　[LINEBotのリッチメニューエディタがないので自作した](https://qiita.com/poruruba/items/adee5be5eefa83a35962)
　[LINEボットとATOM Echoでボイスメッセージを作る](https://qiita.com/poruruba/items/b1d61e56da879c4f3482)

以上