AWS LambdaとLINEでオウム返しbotを作ってみる

# LINEでオウム返しbotを作る
* 入力した文字がそのまま帰ってくる、オウム返しLINE Botを作ってみました🦜
　画像はLINEからメッセージを送信してオウム返しを確認した様子です。
 <img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483299/942cff84-32c8-4c76-04de-48ace5023021.jpeg width=320>

# 主な手順
* 1.LINE Developersアカウントへのログイン
* 2.lambda_functionの作成
* 3.AWS Lambdaの設定
* 4.API Gatewayの設定
* 5.LINE DeveloppersでMessaging APIの設定
* 6.LINEからメッセージを送信してオウム返しを確認する


# 1.LINE Developersの作成とログイン
LINE Developersアカウントへのログイン[URL](https://developers.line.biz/ja/)

# 2.lambda_functionの作成
おうむ返しを実現されるfunctionを作成します。
　※”▶︎”を押して開くと表示される。
<details><summary>具体的なソースコード　</summary>
<div>

```py:lambda_function.py
import os
import sys
import logging

from linebot import (LineBotApi, WebhookHandler)
from linebot.models import (MessageEvent, TextMessage, TextSendMessage)
from linebot.exceptions import (LineBotApiError, InvalidSignatureError)

logger = logging.getLogger()
logger.setLevel(logging.ERROR)

#LINEBOTと接続するための記述
#環境変数からLINEBotのチャンネルアクセストークンとシークレットを読み込む
channel_secret = os.getenv('LINE_CHANNEL_SECRET', None)
channel_access_token = os.getenv('LINE_CHANNEL_ACCESS_TOKEN', None)

#無いならエラー
if channel_secret is None:
    logger.error('Specify LINE_CHANNEL_SECRET as environment variable.')
    sys.exit(1)
if channel_access_token is None:
    logger.error('Specify LINE_CHANNEL_ACCESS_TOKEN as environment variable.')
    sys.exit(1)

#apiとhandlerの生成（チャンネルアクセストークンとシークレットを渡す）
line_bot_api = LineBotApi(channel_access_token)
handler = WebhookHandler(channel_secret)

#Lambdaのメインの動作
def lambda_handler(event, context):
    print("Event:", event)
    # 認証用のx-line-signatureヘッダー
    if "headers" in event and "x-line-signature" in event["headers"]:
        signature = event["headers"]["x-line-signature"]
    else:
        # エラーレスポンスを返す
        return {
            "isBase64Encoded": False,
            "statusCode": 400,
            "headers": {},
            "body": "Missing x-line-signature header"
        }
    body = event["body"]
    # リターン値の設定
    ok_json = {
        "isBase64Encoded": False,
        "statusCode": 200,
        "headers": {},
        "body": ""
    }
    error_json = {
        "isBase64Encoded": False,
        "statusCode": 500,
        "headers": {},
        "body": "Error"
    }

    #メッセージを受け取る・受け取ったら受け取ったテキストを返信する
    @handler.add(MessageEvent, message=TextMessage)
    def message(line_event):
        text = line_event.message.text
        line_bot_api.reply_message(line_event.reply_token, TextSendMessage(text=text))

    #例外処理としての動作
    try:
        handler.handle(body, signature)
    except LineBotApiError as e:
        logger.error("Got exception from LINE Messaging API: %s\n" % e.message)
        for m in e.error.details:
            logger.error("  %s: %s" % (m.property, m.message))
        return error_json
    except InvalidSignatureError:
        return error_json

    return ok_json
```

</div>
</details>

# 3.AWS Lambdaの設定
　Lambdaでライブラリーを使うには下記の複数の方法があります。
    【3-A】EC2を使い、.zipファイルにしてアップロードする方法
    【3-B】ローカルPCを使い、.zipファイルにしてアップロードする方法
    【3-C】Lambda Layerを使う(今回は省略)
　　　　Lambda Layerはre:Invent 2018で発表された機能で、ライブラリをレイヤーに設定することにより、複数のLambdaで共有して利用できる仕組み。

## 【3-A】EC2を使ってアップロードする方法

<details><summary>EC2を使ってアップロードする手順</summary>
<div>
 AmazonLinux2で構築する場合
[AmazonLinux2でPython3環境構築](https://qiita.com/hiren/items/17984191da2ab8955174) 
[参考サイト](https://www.ryucom.co.jp/blog/aws/2776)

* line-bot-sdkのインストール
```
pip install line-bot-sdk -t python
pip install urllib3==1.26.7 -t python
```
※実行する際にsudoをつける等、実行権限に注意
 -tは指定したディレクトリにインストールする引数

* 「lambda_function.py」ファイルを.zip化する
【例】 `zip -r （作成するZipファイル名）（圧縮対象のディレクトリ名）`
```
zip -r line-bot-sdk.zip python
```
* S3へファイル転送
【例】 `aws s3 cp （転送ファイル名） s3://（転送先のS3バケット名）`
```
sudo aws s3 cp ./line-bot-sdk.zip s3://lambda-dev-zip
```
</div>
</details>

## 【3-B】ローカルPCで準備する
* 作業ディレクトリ作成とSDKインストール
line-botフォルダーにインストールする場合、下記をコマンドプロンプトにて実行
```bash:bash
mkdir line-bot
cd line-bot 
python -m pip install line-bot-sdk -t line-bot
```

line-botフォルダーのなかに「lambda_function.py」を作成する
* LINE botのPythonコードをLambdaへアップロードする
    * 先ほど作成したLambdaの関数のページへ行く
    * 右側のアップロード元をクリックする
    * .zipファイルを選択する
    * 先ほど作成したPythonコードを選択する
    * 保存を押す

 ※参考サイト　[AWS Lambdaを利用したLINEbotハンズオン](https://cloud5.jp/aws-lambda_line-api/)


# 4.API Gatewayの設定
AWSコンソールにサインインして
①　API Gatewayのコンソールから「APIを作成」を選択する
②　「REST API」を選択し、「構築」を選択する
<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483299/93e48944-a2a4-ea68-29bb-d095cd31059c.png width=320>

③　「プロトコルを選択する」
「REST」・「新しいAPI」を選択し、「API名」に名前を入力して、「APIの作成」を押します。
④　「アクション」から「リソースの作成」を選択する
⑤　「新しい子リソース」
「リソース名」に名前を入力し、「API Gateway CORSを有効にする」をチェックして、「リソースの作成」を押す
⑥　「アクション」から「メソッドの作成」を選択する
⑦　メソッドのセットアップ
　メソッドは「POST」を選択し、「統合タイプ」は「Lambda関数」を選び、「Lambdaプロキシ統合の使用」にチェックをして、「Lambda関数」にリプライ用の関数を入力し、「デフォルトタイムアウトの使用」にチェックを入れ、「保存」を押す。
⑧　「API Gatewayに、Lambda関数を呼び出す権限を与えようとしています」でOKを押す
⑨　「アクション」から「APIのデプロイ」を選択する
⑩　「APIのデプロイ」
「デプロイされるステージ」は「[新しいステージ]」を選び、「ステージ名」に名前を入れ、「デプロイ」を押す。
⑪　「ステージ」
「demo」＞「/」＞「/items」＞「POST」を選択し、「URLの呼び出し」に表示されたURLをコピーする。
　※参考サイト [AWS + Python + LINEbotでゴミ出しリマインドをする【後編】](
https://note.alhinc.jp/n/n2b0ad926834c)

# 5.LINE DeveloppersでMessaging APIの設定
先ほどAPI GatewayでコピーしたURLを、LINE DevelopersのWebhook設定の「Webhook URL」に入力し、「Webhookの利用」をチェックする。
<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483299/6f11a593-90ef-36ee-8c9c-4eaa6f96d317.png width=320>



# 6.LINEからメッセージを送信してオウム返しを確認する。
　Messaging API設定 のタブにはLINEの友達追加用のQRコードが表示されています。
QRコードを読み取り、友達追加しましょう！
友達追加をすると、あいさつメッセージが送られてきます。
<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483299/d56153fd-fb23-a01b-0265-26c9619eeb2e.png width=320>

メッセージを入力して、同じメッセージが返信されたら完成！
<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483299/942cff84-32c8-4c76-04de-48ace5023021.jpeg width=320>

Lambda関数を変えれば、様々な応用ができそう。

以上

# 参考
* [AWSを活用してLINEチャットボットを作ってみた！①](https://www.ryucom.co.jp/blog/aws/4713)
* [AWS + Python + LINEbotでゴミ出しリマインドをする【後編】](
https://note.alhinc.jp/n/n2b0ad926834c)
* [体重管理アプリを作りながらLine botとAWSについて学ぶ -前編-](https://dev.classmethod.jp/articles/learning-line-bot-and-aws-while-making-a-weight-management-app-first-half/)