Serverless FrameworkでLINEbot作成

#概要
Serverless FrameworkでLINEbotを作成するための記事です。

#アジェンダ
1. Serverless Frameworkの設定
2. LINEbotの設定
3. パラメータの変更
4. デプロイ・実行

#1 Serverless Frameworkの設定

```javascript:aws-nodejsテンプレートのインストール
~/develop/study/linebot $ serverless create --template aws-nodejs --path linebot
Serverless: Generating boilerplate...
Serverless: Generating boilerplate in "/Users/kawamurakouji/develop/study/linebot/linebot"
 _______                             __
|   _   .-----.----.--.--.-----.----|  .-----.-----.-----.
|   |___|  -__|   _|  |  |  -__|   _|  |  -__|__ --|__ --|
|____   |_____|__|  \___/|_____|__| |__|_____|_____|_____|
|   |   |             The Serverless Application Framework
|       |                           serverless.com, v2.52.0
 -------'

Serverless: Successfully generated boilerplate for template: "aws-nodejs"
~/develop/study $ cd linebot 
~/develop/study/linebot $ yarn init
~/develop/study/linebot/linebot $ yarn add serverless-offline
```
※Serverless Frameworkを触ったことのない人は[Serverless FrameworkでHello World作成(入門編)](https://qiita.com/kouji0705/items/6a31774b2f19e9b5e144)を参考にしてね！！！

#2.LINEbotの設定

Messaging APIのチャンネルを作成する。わからない人は[公式ドキュメント](https://developers.line.biz/ja/services/messaging-api/)を見ながらやってね！！

#3 実装
```shell:ディレクトリ構成
~/develop/study/linebot/linebot $ tree -I node_modules
.
├── handler.js
├── line.js          ←新規作成
├── package.json
├── serverless.yml   ←修正
└── yarn.lock
0 directories, 5 files
```
line.jsのソースコード

```javascript:line.js
const request = require('superagent');
const endpoint = 'https://api.line.me/v2/bot/message/reply';
const accessToken = 'Linebotのアクセストークン';

module.exports.webhook = (event, context, callback) => {
  var body = JSON.parse(event.body);
  body.events.forEach(function(data) {
    var replyToken = data.replyToken;
    var message = data.message.text

    request.post(endpoint)
            .set('Content-type', 'application/json; charset=UTF-8')
            .set('Authorization',  'Bearer ' + accessToken)
            .send({
              replyToken: replyToken,
              messages: [
                {
                  type: 'text',
                  text: message,
                },
              ],
            })
            .end(function(error){
              if (error) {
                console.log(error);
              }
            });
  });

  callback(null, {statusCode: 200, body: JSON.stringify({})});
};
```

```yml:serverless.yml
service: linebot

frameworkVersion: '2'

provider:
  name: aws
  runtime: nodejs12.x
  lambdaHashingVersion: 20201221
  stage: dev
  region: ap-northeast-1

plugins:
 - serverless-offline

functions:
  hello:
    handler: handler.hello
    events:
      - http:
          path: hello
          method: get
  lineWebhook:
    handler: line.webhook
    events:
      - http:
          path: line/webhook
          method: post

```

#4 デプロイ・実行
```shell:デプロイコマンド
~/develop/study/linebot/linebot $ serverless deploy  
Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Creating Stack...
Serverless: Checking Stack create progress...
........
Serverless: Stack create finished...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service linebot.zip file to S3 (30.47 MB)...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
...................................................
Serverless: Stack update finished...
Service Information
service: linebot
stage: dev
region: ap-northeast-1
stack: linebot-dev
resources: 18
api keys:
  None
endpoints:
  GET - https://URL/dev/hello
  POST - https://URL/dev/line/webhook ←このURLに注目
functions:
  hello: linebot-dev-hello
  lineWebhook: linebot-dev-lineWebhook
layers:
  None
```
POSTのURLをLineBotのWebhookに登録。これで、オウム返しbotの完成。

##追記
追記した実装の方がスマートですね。

```shell:ディレクトリ構造
~/develop/study/linebot/linebot $ tree -I node_modules 
.
├── handler.js
├── index.js
├── line.js
├── package.json
├── serverless.yml
└── yarn.lock
```

```javascript:index.js(linebotのsdkでの実装)
'use strict';

// パッケージのインストール
const line = require('@line/bot-sdk');

// LINEアクセストークンの設定
const config = {
  channelAccessToken: 'アクセストークン',
  channelSecret: 'チャンネルシークレット',
};

// インスタンス化
const client = new line.Client(config);

exports.webhook = async (event, context) => {
  // eventを出力
  console.log(event);

  // JSONとして解析して値やオブジェクトを構築する
  const body = JSON.parse(event.body);
  console.log(`【body】: ${JSON.stringify(body)}`);

  // LINE Eventを取得
  const response = body.events[0];
  console.log(`【response】: ${JSON.stringify(response)}`);

  // メッセージ送信のために必要な情報
  const replyToken = response.replyToken;
  const post = {
    type: 'text',
    text: response.message.text,
  };

  try {
    await client.replyMessage(replyToken, post);
  } catch (err) {
    console.log(err);
  }
};
```

```yml:serverless.yml
service: linebot

frameworkVersion: '2'

provider:
  name: aws
  runtime: nodejs12.x
  lambdaHashingVersion: 20201221
  stage: dev
  region: ap-northeast-1

plugins:
 - serverless-offline

functions:
  hello:
    handler: handler.hello
    events:
      - http:
          path: hello
          method: get
  lineWebhook:
    handler: line.webhook
    events:
      - http:
          path: line/webhook
          method: post
  index:
    handler: index.webhook
    events:
      - http:
          path: webhook
          method: post
```