メタラーまとんがハイソにやらかすようです

東大理系修士卒JTBCエンジニアのハイソサイエティ(上流階級)な日常

LINE Botに生命保険の解約返戻金を教えてもらえるようにした!Messaging API, Amazon API Gateway, Lambda

ども!AWS認定SAアソシエイトのまとんです。

LINE botに話しかけると、生命保険の金額を教えてくれる仕組みをで作ったので紹介します!

使ったもの:AWSを使ってサーバーレス実装しました。

経緯

先日、ドル建て生命保険の解約返戻金をLINEに通知してくれる仕組みを作りました。

highso.hatenablog.com

SORACOMのボタンを押すと、LINEのトークルームに金額を通知してくれるという仕組みです。

SORACOM LTE-M Button powered by AWS

SORACOM LTE-M Button powered by AWS

 

f:id:highso:20190916232950j:plain

このボタンをダイニングテーブルの上に置いておきました。

嫁には「好きな時にボタンを押していいよ!」と言っておいたのですが・・・

f:id:highso:20190922154821p:plain

嫁いわく、「ボタンを押すのがめんどくさい。LINEで反応させてくれ」とのこと。

ボタンを押すことさえ、手間。LINE上で完結させないとダメのようです。

となると、実装がガラリと変わるので、大変なのですが。。。

 

しかし、顧客(嫁)が本当に求めているものを、何としてでも作って提供するのがエンジニア冥利というものです。

というわけで、LINE Botで返事をする仕組みを作りました。

作りたいもの

今回の要求仕様は以下の通りです。

完成形のイメージ:

f:id:highso:20190922161659j:plain

実装

アーキテクチャ

f:id:highso:20190922154840p:plain

LINEはMessaging APIなるサービスで、独自のbotを作る仕組みを提供しています。

Messaging APIで作った自分専用のbotを、僕と嫁のトークルームに参加させます。

トークルームで話しかけると、botのMessaging APIが反応し、webhookで別システムのAPIを叩くことができます。

別システムとして、AWSを使用します。

AWSではAPI Gatewayを使って、自分のAPI(アドレス)を簡単にインターネット公開することができます。

Messaging APIのwebhookでAPI Gatewayのアドレスを叩くように設定することで、LINEとAWSを連結させることができます。

API Gatewayは、後段のLambdaを起動して、POSTメッセージを捌く処理を実装します。

POSTの応答で、botに生命保険金額を発言するように返せばOKです。

生命保険金額の複利計算と、ドル円為替レートの取得については、前回の記事で詳細に説明したので、この記事では省略します。

Messaging API

LINE botを作っていきます。以下の記事を参考にさせていただきました。

LambdaではじめてのLINE Botを作る | DevelopersIO

LINE Developersに登録して、bot用チャネルを作成

まず、LINE Developersコンソールに、自分のLINEアカウントでログインします。

新規プロバイダーを作成します。プロバイダーとは、開発者(企業・個人)の名前です。

プロバイダー名「メタラーまとん」で作成しました。

次に、Messaging APIのチャネルを作成します。チャネルは、botことだと理解しています。

チャネル名「メタラーまとん」で作成しました。

チャネルの設定

メタラーまとんチャネルの「チャネル基本設定」カラムで、色々と設定します。

・Channel Secretの文字列をコピー

・アクセストークン(ロングターム)を発行→文字列をコピー

Botグループトーク参加→設定はこちら

 「グループ・複数人チャットへの参加を許可する」にチェック。自分と嫁のグループチャットを使うため。

f:id:highso:20190922154846p:plain

・LINE@機能の利用→自動応答メッセージ→設定はこちら

 「応答メッセージ」をオフ。デフォルト(オン)では、話しかけたときに毎回返信をするようになっている。今回は不要なのでオフにする。

AWS

必要なライブラリをzipで固めてLambdaにアップロードする

今回の実装では、LambdaのPython3.7ランタイムを使います。

Lambdaでサードパーティのライブラリを使いたいときは、直接pip installすることができず、一旦ローカルでpipをして、zipで固めてアップロードする必要があります。

今回必要なライブラリは、line-bot-sdk、oandapyV20、requestsです。これらをzipで固めていきます。

 

僕は開発環境でWindows 10を使っていて、pipするのが難しいです。

そこで、Windows Subsystem for Linux (WSL)でUbuntuを走らせて、この上でpipしていきます。WSLへのPythonインストール手順は割愛します。

作業用ディレクトリ作成、移動

$ mkdir HighsoButton

$ cd HighsoButton

ライブラリを作業用ディレクトリにインストール

$ sudo pip3 install oandapyV20 -t ./

$ sudo pip3 install requests -t ./

$ sudo pip3 install line-bot-sdk -t ./

不要ファイルを削除

$ rm *.dist-info -r

フォルダごとzipに固める

$ sudo zip -r HighsoLineBot.zip ./*

f:id:highso:20190922154836p:plain

「HighsoLineBot.zip」ができればOKです。

WSLを使っている人は、下記コマンドでzipをWindows環境にコピーしましょう。

$ cp HighsoLineBot.zip /mnt/c/Users/<ユーザー名>/Desktop/

 

次に、AWS Lambdaで、新しいLambda関数を作成します。

関数名:HighsoLineBot

リージョン:オレゴン

ランタイム:Python 3.7

コードエントリタイプ→「.zipファイルをアップロード」で、先ほど作成したzipをアップロードし、画面右上の「保存」ボタンを押します。

f:id:highso:20190922154809p:plain

./HighsoLineBot/ 以下に、各種ライブラリのフォルダが出現すればOKです。

./HighsoLineBot/に、新しいPython「lambda_function.py」を作成して、保存します。

AWS LambdaでPOST処理を書く

lambda_function.pyに、POSTを捌く処理を書いていきます。

line-sdk-botの使い方について、以下の記事を参考にさせていただきました。

Lambdaでline-bot-sdk-pythonを使用してオウム返しBOTを作成する - Qiita

Lambdaでやることは以下の通りです。太字以外の部分は、前回の記事実装済みです。

  1. OANDA APIドル円為替レートを取得
  2. 生命保険金の複利運用額を計算し、ドル円換金額を計算
  3. LINE-botハンドラーを作成。
  4. botからイベントを受け取り、X-Line-Signatureで認証
  5. メッセージ内に「円」「いくら」等特定の文字列があったら、2で計算したドル円換金額を答えるよう、botに指示
  6. POSTのステータスコードjsonで返す

具体的には、以下のコードをlambda_function.pyに書きました。(太字が今回実装した部分)

import json
import sys
import urllib.parse
import urllib.request
import datetime
import os
from oandapyV20 import API
import oandapyV20.endpoints.instruments as instruments
from datetime import datetime as dt
from datetime import timedelta
from linebot import (
LineBotApi, WebhookHandler
)
from linebot.models import (
MessageEvent, TextMessage, TextSendMessage,
)
from linebot.exceptions import (
LineBotApiError, InvalidSignatureError
)

# LINE Messaging API
LINE_CHANNEL_SECRET = os.getenv('LINE_CHANNEL_SECRET', None)
LINE_CHANNEL_ACCESS_TOKEN = os.getenv('LINE_CHANNEL_ACCESS_TOKEN', None)
if LINE_CHANNEL_SECRET is None:
    print('Specify LINE_CHANNEL_SECRET as environment variable.')
    sys.exit(1)
if LINE_CHANNEL_ACCESS_TOKEN is None:
    print('Specify LINE_CHANNEL_ACCESS_TOKEN as environment variable.')
    sys.exit(1)
line_bot_api = LineBotApi(LINE_CHANNEL_ACCESS_TOKEN)
handler = WebhookHandler(LINE_CHANNEL_SECRET)

#OANDA API
OANDA_ACCESS_TOKEN = os.environ.get('OANDA_ACCESS_TOKEN')
api = API(access_token=OANDA_ACCESS_TOKEN)


def get_USDJPY():
    params = {
        'count': 1,
        'granularity': 'D'
    }
    r = instruments.InstrumentsCandles(instrument="USD_JPY", params=params)
    api.request(r)

    data = []
    for raw in r.response['candles']:
        data.append({
        'time': raw['time'],
        'volume': raw['volume'],
        'o': raw['mid']['o'], #始値
        'h': raw['mid']['h'], #高値
        'l': raw['mid']['l'], #安値
        'c': raw['mid']['c'], #終値
    })
    print(data)

    #前日の終値
    date = dt.strptime(data[0]['time'][:19], '%Y-%m-%dT%H:%M:%S')
    date = date + timedelta(hours = 9)
    #print(date)
    exchange_rate = float(data[0]['c']) #USD-JPYレート
    msg1 = str(date.year) + '年' + str(date.month) + '月' + str(date.day) + '日'
    msg2 = 'USD-JPY終値: ' + str(exchange_rate) + '円'
    print(msg1)
    print(msg2)

    #日ごとの複利計算
    start_date = dt(year=2019, month=8, day=20) #保険開始日
    elapsed_time = date - start_date
    elapsed_day = elapsed_time.days
    msg3 = '経過日数: ' + str(elapsed_day) + '日'
    print(msg3)

    ## Y = a*b^X
    ## 解約返戻金(USD) = a*b^(経過日数)
    a = 8457.28972
    b = 1.00007929
    refund_USD = a * b ** elapsed_day
    #refund_USD = refund/5
    exchange_fee = 0.5 #USDからJPYへの為替手数料(円)

    refund_JPY = refund_USD * (exchange_rate - exchange_fee)
    msg4 = '複利運用額: ' + str(round(refund_USD, 1)) +'ドル'
    msg5 = '解約返戻金: ' + str(round(refund_JPY, 1)) + '円'

    msgs = '\n'.join([msg1, msg2, msg3, msg4, msg5])
    print(msgs)
    return msgs


def send_line_bot(signature, body, msg_refund):
    ok_json = {"isBase64Encoded": False,
        "statusCode": 200,
        "headers": {},
        "body": ""}
    error_json = {"isBase64Encoded": False,
        "statusCode": 403,
        "headers": {},
        "body": "Error"}

    @handler.add(MessageEvent, message=TextMessage)
    def message(line_event):
        text = line_event.message.text
        if '円' in text or 'いくら' in text:
            rep = msg_refund
            line_bot_api.reply_message(line_event.reply_token, TextSendMessage(text=rep))
        elif 'まとん' in text:
            rep = 'いじめないで・・・'
            line_bot_api.reply_message(line_event.reply_token, TextSendMessage(text=rep))

    try:
        handler.handle(body, signature)
        except LineBotApiError as e:
        print("Got exception from LINE Messaging API: %s\n" % e.message)
        for m in e.error.details:
            print(" %s: %s" % (m.property, m.message))
            return error_json
    except InvalidSignatureError:
        return error_json

    return ok_json

def lambda_handler(event, context):
    msg_refund = get_USDJPY()

    signature = event["headers"]["X-Line-Signature"]
    body = event["body"]
    res = send_line_bot(signature, body, msg_refund)
    print(res)

    return res

アクセストークンなどのクレデンシャルをコードにハード埋め込みしないよう、Lambdaの環境変数に、以下を登録します。

・Messaging APIのChannel Secret 

・Messaging APIのアクセストークン(ロングターム)

・OANDA APIのアクセストーク

f:id:highso:20190922154815p:plain

以上の設定が終わったら、Lambdaを保存します。

Amazon API GatewayでMessaging APIのwebhookをLambdaに投げる

API Gatewayを開き、新規作成をします。

プロトコル:REST

新しいAPI

API名:HighsoInvestment

エンドポイントタイプ:リージョン

f:id:highso:20190922154750p:plain

リソース→アクション→メソッドの作成→POSTを選択

統合タイプ:Lambda関数

Lambdaプロキシ統合の使用:チェック

Lambdaリージョン:us-west-2 (オレゴン)

Lambda関数:HighsoLineBot

f:id:highso:20190922154754p:plain

メソッドリクエストを設定

・HTTPリクエストヘッダーに名前「X-Line-Signature」を追加

POSTが以下のようになっていればOK

f:id:highso:20190922154826p:plain

アクション→APIのデプロイ

新しいステージを選択。

ステージ名「beta」でデプロイ

デプロイが完了すると、URLが表示されるのでコピーする。

API GatewayとLambdaの連携に成功すると、Lambdaの画面で以下のようにAPI Gatewayトリガーが追加されたことが分かる。

f:id:highso:20190922154801p:plain

最後に、Messaging APIの設定画面に戻り、以下の設定をすれば完了。

・Webhook送信:利用する

・Webhook URL:先ほどデプロイしたAPI Gatewayのアドレス

f:id:highso:20190922154831p:plain

botを友達登録して、トークルームにbotを招待

Messaging APIの「LINEアプリへのQRコード」を、LINEの友達追加で読み取り、botメタラーまとん」を友達登録。

僕と嫁が参加しているトークルームに、メタラーまとんを招待。botなので一瞬で承認して入ってきます。

トークルームで何か発言して、botが反応すればOK。

上のLambda関数だと、「円」「いくら」を含む発言をすると、解約返戻金を教えてくれて、「まとん」を含む発言をすると「いじめないで・・・」と返事をします!

デモ動画

感想

LINE Botを作るのはめちゃくちゃ簡単でした!

親切な解説記事があったおかげとはいえ、わずか2.5時間でLINE botを作れました。

botを作ると、さっそく嫁が色々と遊んでくれたので、満足です。

みなさんもLINE Botを作って遊んでみてください。

 

以上、メタラーまとんでした。

ではでは。

ファイナンシャル × IoT記事まとめ

1. ドル円為替レートをLINEに通知するハイソ投資ボタンを作った! Lambda, LINE Notify, OANDA, SORACOM

2. ドル建て生命保険の解約返戻金を計算してLINEに通知する仕組みを作った!

3. LINE Botに生命保険の解約返戻金を教えてもらえるようにした!Messaging API, Amazon API Gateway, Lambda

4. 投資の近況報告!!積み立てNISA、ドル建て生命保険、国内株式