LINE Messaging API + Serverless Frameworkで作る、初老向け意志決定支援システム

本記事は [初老丸Advent Calendar](http://qiita.com/advent-calendar/2016/syoroumaru)の8日目の記事です。

皆さんこんにちは。気がつけば初老になって、本厄ももうすぐ終わろうかという今日この頃いかがお過ごしでしょうか。
さて、初老と言えば40代。40と言えばかの孔子様が論語で「四十而不惑」と言われ、「不惑」とも呼ばれる年頃です。
しかし時代は変わりました(多分)。40代と言ってもまだまだ迷ってばかりです。その割には判断速度が鈍ってきていて困っていたりします。

ここでは、そんなあなたに素敵な意志決定支援システムをお届けします！(前提が長い)

# 1. 構成
全体構成を考えます。
やはりここは流行に乗って2016年10月に公開されたばかりの[LINE Messaging API](https://business.line.me/ja/services/bot)を使って、LINE Botで作りましょう。初老と言ってもスマホにLINE位は入ってますよね？入ってない人は今すぐダウンロードしましょう。
バックエンドは運用にお金もかけたくないですし、ここも流行に乗ってAWS Lambda(python)を使いましょう。そして、せっかくなので[Severless Framework](https://github.com/serverless/serverless)で作りましょう。

# 2. LINEのDeveloper Trialに登録してボットを作る

[LINE BUSINESS CENTER](https://business.line.me/ja/services/bot)の「Developer Trialを始める」から、アカウントを登録します。
登録したらログインした画面で「アカウントリスト」に進み、「ビジネスアカウントを作成する」に進みます。
サービス選択画面になるので「Messaging API」を選びます。ここではアカウント名を「初老丸」で作成しました。業種は適当でもいいんですが、ここは「個人(架空の人物)」を選んでみました。
![syorou-1.jpg](https://qiita-image-store.s3.amazonaws.com/0/59415/70a70546-88f5-1504-b10a-a9a7ccb62576.jpeg)

登録が完了したら「LINE@MANAGER」に進みます。ここでボットアカウントの設定をしますが、まずはAPIを有効にしましょう。
![syorou-2.jpg](https://qiita-image-store.s3.amazonaws.com/0/59415/5666fe12-2ecd-76f8-44c6-9c92602bf5a1.jpeg)

そして「Webhook送信」を有効にします。また、「自動応答メッセージ」は「利用しない」にしておきましょう。
![syorou-5.jpg](https://qiita-image-store.s3.amazonaws.com/0/59415/299da152-2fa8-f1b3-933b-fb96c766f7d0.jpeg)

終わったら、「Bot設定」の画面から「LINE Developersで設定する」を押して「LINE Developers」のページに移動します。

Channnel Access Tokenの横の「ISSUE」ボタンを押してトークンを払い出し、そのトークンとChannel Secretをメモしておきます。

とりあえず事前準備はこれで完了です。

# 3. Serverless Frameworkの準備

npmコマンドでインストールします。npmはyumなりなんなりで適当にインストールしておきます。

```shell-session
#sudo npm install serverless -g
```

正常にインストールされているか確認しましょう。

```shell-session
#sls --help

Commands
* Serverless documentation: http://docs.serverless.com
* You can run commands with "serverless" or the shortcut "sls"
* Pass "--help" after any <command> for contextual help

config credentials ............ Configures a new provider profile for the Serverless Framework
create ........................ Create new Serverless service
install ....................... Install a Serverless service from GitHub
deploy ........................ Deploy a Serverless service
deploy function ............... Deploy a single function from the service
deploy list ................... List deployed version of your Serverless Service
invoke ........................ Invoke a deployed function
invoke local .................. Invoke function locally
info .......................... Display information about the service
logs .......................... Output the logs of a deployed function
metrics ....................... Show metrics for a specific function
remove ........................ Remove Serverless service and all resources
rollback ...................... Rollback the Serverless service to a specific deployment
slstats ....................... Enable or disable stats

Plugins
AwsConfigCredentials, Config, Create, Deploy, Info, Install, Invoke, Logs, Metrics, Package, Remove, Rollback, SlStats
```
上記のようにヘルプが表示されれば成功です。「sls」はserverlessコマンドの短縮エイリアスです。
なお、本稿はFreeBSD 11.0上のnpm 3.9.2とserverless 1.3.0で動作確認をしています。

# 4. コーディング
## 4.1 プロジェクトの作成
まずはserverlessのプロジェクトを作成します。

```shell-session
#mkdir line-syorou
#cd line-syorou
#sls create -t aws-python
```

-t でテンプレートを指定します。ここではAWS Lambda(python)を使うので「aws-python」を指定しています。

これでカレントディレクトリに「handler.py」と「serverless.yml」の2つのファイルが作成されます。
hapdler.pyがプログラム本体、serverless.ymlが設定ファイルです。とってもシンプルですね！

## 4.2 設定
serverless.ymlを設定します。今回はほとんど書き直すところはありません。
https://XXXXXXXX/syorou にHTTP GETとHTTP POSTが来たらhandler.pyの中のsyorouという関数が呼ばれるようにしましょう。以下の所だけ書き換えてください。

```yaml
functions:
  syorou:
    handler: handler.hello
    events:
      - http:
           path: syorou
           method: get
      - http:
           path: syorou
           method: post
```

## 4.3 公式SKDのインストール

LINE公式のpython SDKがあるのでインストールします。

[line-bot-sdk-python](https://github.com/line/line-bot-sdk-python)

```shell-session
#pip install line-bot-sdk -t ./
```

インストールしたSDKはLambdaにデプロイするときにまとめてZIPに固める必要があるので、「-t」オプションを使ってカレントディレクトリにインストールしましょう。

## 4.4 コードを書く
handler.pyにコードを書きます。ファイルはUTF-8で保存してください。

[公式のドキュメント](https://devdocs.line.me/ja/?shell#messaging-api)に沿って書けば特に難しい部分はないのですが、流れとしては

- X-Line-Signatureヘッダの情報ならびにチャンネルシークレットを使ってリクエストを検証し、パースする
- リクエストヘッダ中に、「Events」という配列に発生したイベント(Message/Joinなど)が含まれる。今回はユーザからの書き込みに返事をしたいので、Message Eventを対象とする。
- reply_tokenを取り出す。
- 内容に従って返信する。TextMessageオブジェクトを作り、送信先を先ほど取得したreply_tokenで指定する

となります。

```python
# coding: utf-8
import json
import random
from linebot import (
    LineBotApi, WebhookHandler, WebhookParser
)
from linebot.exceptions import (
    InvalidSignatureError,LineBotApiError
)
from linebot.models import (
    MessageEvent, TextMessage, TextSendMessage,TemplateSendMessage,ButtonsTemplate,URITemplateAction,LocationSendMessage
)

def syorou(oevent, context):

    #リクエスト本体と、X-LINE-Signatureヘッダを取出す
    body = oevent['body']
    signature = oevent['headers']['X-Line-Signature']

    #Channel Secretを使って入力が正しいかを確認する
    secret = '先ほどメモしたチャンネルシークレット'
    parser = WebhookParser(secret)

    try:
        events = parser.parse(body,signature)
    except InvalidSignatureError:
        return {"stautsCode" : 400,"body" : ""};

    #LineBotAPIオブジェクトを作成する
    token = '先ほどメモしたトークン'
    line_bot_api = LineBotApi(token)
    
    try:
        events = parser.parse(body,signature)
    except InvalidSignatureError:
        return {"stautsCode" : 400,"body" : ""};

    for event in events:
        if event.type == 'message':
            reply_token = event.reply_token
            if random.randint(0,1) == 0:
                message = TextSendMessage(
                    text=u"いけいけGOGO！"
                )
            else:
                message = TextSendMessage(
                    text=u"うーん、やめときましょう..."
                )
                
            try:
                line_bot_api.reply_message(reply_token,message)
            except LineBotApiError as e:
                print(e.status_code)
                print(e.error.message)
                print(e.error.details)

    return {"stautsCode" : 200,"body" : "OK"};
            

```
# 5. 動かしてみましょう

## 5.1 デプロイします
Lambdaにデプロイします。デプロイするにはawsのcredentialが設定されてる必要があるので適宜設定しておきましょう。
~/.aws/credentialsに書いておくか環境変数を設定してください。

```shell-session
#sls deploy
Serverless: Creating Stack...
Serverless: Checking Stack create progress...
.....
Serverless: Stack create finished...
Serverless: Packaging service...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading service .zip file to S3 (2.26 MB)...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
.................................
Serverless: Stack update finished...

Service Information
service: aws-python
stage: dev
region: us-east-1
api keys:
  None
endpoints:
  GET - https://*****us-east-1.amazonaws.com/dev/syorou
  POST - https://******us-east-1.amazonaws.com/dev/syorou
functions:
  aws-python-dev-syorou: arn:aws:lambda:us-east-1:********:function:aws-python-dev-syorou
```

成功したらこんな感じでendpointが表示されますので、これを先ほどのLINE DevelopersのページからWebhook URLに設定します。

## 5.2 相談してみましょう
先ほどのLINE@MANAGERのページの「アカウント設定」に、今回作ったボットの登録用QRコードがあるのでこれをLINEアプリから読み込んで友達になりましょう。

そして、今悩んでることを相談してみましょう。

![syorou-4.png](https://qiita-image-store.s3.amazonaws.com/0/59415/52cd16d8-45b8-cc8e-2bde-aa19616076c3.png)

無事に悩みが解決しましたね！:v:

# まとめ
というわけで、Serverless Frameworkを使ってAWS Lambda上に、LINE Messaging APIを用いたチャットボットを構築してみました。どちらも使ってみるととても簡単ですので、皆さん是非お試しください。
なお、本チャットボットの指示に従って何らかの重大な事故等が発生したとしても一切の責任は負いかねますので、その当たりは自己責任でお願いいたします:stuck_out_tongue:

以下のQRコードから本ボットに友達申請できますが、最大64人までしか登録できないのといつまで動かしているかは不明ですので、繋がらなかったらごめんなさい。一通り試された後は次の方のために友達解除していただけますと幸いです。

![QRコード](http://qr-official.line.me/L/7PZLREtEiM.png)