api gateway + lambdaでline通知サービス構築
TRANSCRIPT
API Gateway + LambdaでLINE通知サービス構築
2016年 10月 6日木村健一郎
名前:木村健一郎所属:株式会社コム・アンド・コム JAWS-UG福岡 さくらクラブ福岡 IoTお仕事:技術に関することはなんでも好きな言語: perl好きな DB: PostgreSQL
今日のお題: Lambdaで LINEに通知するサービスを作る
LINE Messaging API/LINE Notify
• 2016/4/7に BOT APIは公開されてた• 利用者からの入力を受けて、それに返す
•Messaging APIが先日 (2016/9/29)公開された• 外部から API叩いて LINEに送信できる• かなり色々出来そう (まだ試してない )
• LINE Notifyの公開 (2016/9/29)• 外部から API叩いて LINEに通知を送れる• Mackerelや IFTTTが公開済み
•さっそく Notifyで遊んでみた• せっかくなんで Serverlessフレームワーク使ってみた
準備するもの
• LINE自体のアカウントPCからログインできるようにしておく必要があります• LINE Developerへの登録この辺から登録しましょうhttps://business.line.me/ja/services/bot• Nodeと pythonが動くマシン私は Node6.6.0/Serverless 0.5.6/Python2.7.12で試しました
処理の流れAPI Gateway + Lambda
①アクセス
LINEのサーバ
②リダイレクト
③ログインページ
④認証
⑤認証情報 (code)返信
⑥oauth APIコール
⑦token返信
⑧token使ってメッセージ送信
⑨メッセージプッシュ
今回の構成
インターネットhttpアクセス
CSRF対策 /oauthのトークンと利用者を紐付けるキーの保存
準備IMAロールや DynamoDBの設定を先にやっておきましょう (詳細略 )
• LINE notifyのサイトでアプリケーションを登録しておきます• https://notify-bot.line.me/ja/• アプリケーション名はメッセージが「 [アプリ名 ]ほげほげ」という感じになります• クライアント IDと秘密鍵をメモっておきます• Callbackはアプリ作成時に入力求められますが、適当でいいです
• DynamoDBのテーブル作成• テーブル名「 line-message」、スキーマは「 state」というテキストだけ
• IAMユーザならびにロールの作成• serverless用に AdministratorAccessのユーザ• Dynamodbの読み書きが出来るロール
serverlessをインストールしてプロジェクト作成%sudo npm install serverless –g%sls project create _______ __| _ .-----.----.--.--.-----.----| .-----.-----.-----.| |___| -__| _| | | -__| _| | -__|__ --|__ --||____ |_____|__| \___/|_____|__| |__|_____|_____|_____|| | | The Serverless Application Framework| | serverless.com, v0.5.6`-------'
Serverless: Initializing Serverless Project... Serverless: Enter a name for this project: (serverless-ryqpyr)line-messageServerless: Enter a new stage name for this project: (dev) devServerless: For the "dev" stage, do you want to use an existing Amazon Web Services profile or create a new one? Existing Profile > Create A New ProfileServerless: Please enter the ACCESS KEY ID for your Admin AWS IAM User: *****Serverless: Enter the SECRET ACCESS KEY for your Admin AWS IAM User: *****Serverless: Enter the name of your new profile: (hoge_dev) Serverless: Creating stage "dev"... Serverless: Select a new region for your stage: us-east-1 us-west-2 eu-west-1 eu-central-1 > ap-northeast-1Serverless: Creating region "ap-northeast-1" in stage "dev"... Serverless: Deploying resources to stage "dev" in region "ap-northeast-1" via Cloudformation (~3 minutes)...
最初にリダイレクトする /authと、認証情報のコールバックを受ける /callbackの 2つのエンドポイントを作成
エンドポイントの作成
%sls function create auth%sls function create callback
CSRF対策の文字列を作って DynamoDBに保存して、リダイレクトするコードを auth/handler.pyに書きます
authの作成 (1)
from __future__ import print_function
import jsonimport loggingimport uuidimport hashlibimport boto3
log = logging.getLogger()log.setLevel(logging.DEBUG)dynamodb = boto3.resource('dynamodb')table = dynamodb.Table('line-message')
def handler(event, context): log.debug("Received event {}".format(json.dumps(event))) token = hashlib.md5(str(uuid.uuid4())).hexdigest() table.put_item( Item={ "state" : token } ) return {"location" : "https://notify-bot.line.me/oauth/authorize?response_type=code&client_id=***&redirect_uri=https://******/dev/callback&scope=notify&resonse_mode=form_post&state="+token}
302でリダイレクトするように api gatewayの設定を auth/s-function.jsonに書きます。 DynamoDBにアクセスするためのロールもここに記載します。
authの作成 (2)
・・・・ "customRole": "arn:aws:iam::***************",・・・・・ "responses": { "400": { "statusCode": "400" }, "default": { "statusCode": "302", "responseParameters": { "method.response.header.Location" : "integration.response.body.location" },
ハンドラーの戻り値を Locationヘッダに出す
LINEサーバから渡された情報を元に Oauthを行って、その結果を HTMLで表示します。 callback/handler.pyに書きます。前半の、受け取った CSRFトークンが正しいかの検証部分です。
callbackの作成 (1)
from __future__ import print_function
import jsonimport loggingimport boto3import urllibimport urllib2import jsonlog = logging.getLogger()log.setLevel(logging.DEBUG)dynamodb = boto3.resource('dynamodb')table = dynamodb.Table('line-message')
def handler(event, context): res = table.get_item( Key={ 'state':event['state'] } )
if res['Item']: table.delete_item( Key={ 'state':event['state'] } )
後半の oauthを呼ぶ部分です
callbackの作成 (2)
url = 'https://notify-bot.line.me/oauth/token' postvalue = { 'grant_type' : 'authorization_code', 'code' : event['code'], 'redirect_uri' : 'https://********/dev/callback', 'client_id' : ‘**********', 'client_secret' : ‘*********', } postdata = urllib.urlencode(postvalue); req = urllib2.Request(url,postdata) response = urllib2.urlopen(req) page = response.read() return page else: return "not found"
ハンドラーへのパラメータ受け渡しと、結果を text/htmlで返す api gatewayの設定を callbac/s-function.jsonに書きます。DynamoDBにアクセスするためのロールもここに記載します (text/htmlで返すのは不要かも・・ )。
callbackの作成 (2)
・・・・ "customRole": "arn:aws:iam::***************",・・・・・ "requestTemplates": { "application/json": { "state" : "$input.params('state')", "code" : "$input.params('code')" } }, "responses": { "400": { "statusCode": "400" }, "default": { "statusCode": "200", "responseParameters": {}, "responseModels": { "text/html;charset=UTF-8": "Empty" }, "responseTemplates": { "text/html;charset=UTF-8": "$input.path('$')" } } }
ここで入力のテンプレートを定義するとハンドラーの第 1引数 (event)にハッシュで渡る
出力を text/htmlとして、 Velocityの出力を出すようにする
デプロイします。カーソルキーとリターンで全ての functionと endpointを選んで「 Deploy」を選びます
デプロイ
%sls dash deploy| _ .-----.----.--.--.-----.----| .-----.-----.-----.| |___| -__| _| | | -__| _| | -__|__ --|__ --||____ |_____|__| \___/|_____|__| |__|_____|_____|_____|| | | The Serverless Application Framework| | serverless.com, v0.5.6`-------'
Use the <up>, <down>, <pageup>, <pagedown>, <home>, and <end> keys to navigate.Press <enter> to select/deselect, or <space> to select/deselect and move down.Press <ctrl> + a to select all, and <ctrl> + d to deselect all.Press <ctrl> + f to select all functions, and <ctrl> + e to select all endpoints.Press <ctrl> + <enter> to immediately deploy selected.Press <escape> to cancel.
Serverless: Select the assets you wish to deploy: auth > function - auth endpoint - auth - GET callback function - callback endpoint - callback - GET - - - - - Deploy Cancel
動かしてみましょう
• authのエンドポイントにアクセス• LINEの認証後、メッセージをどのグループで受け取るかを選ぶ• 1対 1またはグループが選べるようです• グループの場合は LINE Notifyをグループに招待します
• うまくいったらトークンが表示されるのでコピペしてメッセージ送ってみましょう
%curl -X POST -H "Authorization: Bearer ******" -F 'message=テストです ' https://notify-api.line.me/api/notify
注意
• 2016年 10月 4日時点での情報です• LINE Messaging APIも serverlessの使い方も変わっている可能性はあります。必ず最新の公式ドキュメントを参照してください
• Serverless 、もうすぐ 1.0が出てずいぶん変わってるとか・・
• リダイレクトだけ (auth)を Lambdaでやる意味があるかというと微妙?
• トークンの管理は手を抜いています• エラーハンドリングもしてません• DynamoDBの not foundは try-catchでチェック?
「とりあえず試してみた」のレベルなので以下の点はご注意を
感想
• 受信メッセージが「 LINE Notify」っていうグループにまとめられるのがちょっと残念
• 直接おしゃべりみたいなのは BOT APIやMessaging APIでやりましょうってことかと
• LINE APIの Rate limitは 1000アクセス /時・トークン。実用上まず問題ないレベル。
• Serverlessフレームワーク便利だけど、どんどん変わっていってて情報が錯綜気味?・外部ライブラリをどこに置いといたらいいのか分からなかった
• 「 serverless」ってググりにくい・・ (´ ・ ω ・ `)
• テキスト送る程度の個人用途なら、アプリ登録までしないでも tokenだけ取得も可能なのでここまで作らなくても OK
• もっと言うなら IFTTT + Makerで十分かも (元も子もない )